use alloc::collections::BTreeMap;
use miette::Diagnostic;
use rust_decimal::Decimal;
use snafu::Backtrace;
use snafu::ensure;
use snafu::Snafu;
use time::Date;
use core::ops::Deref;
use core::str::FromStr;
use core::fmt::Display;
use core::borrow::Borrow;
pub mod common_keys;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Key {
name: String,
}
impl Key {
#[must_use]
pub const unsafe fn from_unchecked(name: String) -> Self {
Self { name }
}
}
impl Borrow<Ky> for Key {
fn borrow(&self) -> &Ky {
self
}
}
impl Borrow<str> for Key {
fn borrow(&self) -> &str {
&self.name
}
}
impl Deref for Key {
type Target = Ky;
fn deref(&self) -> &Self::Target {
unsafe { Ky::from_unchecked(&self.name) }
}
}
impl Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.name)
}
}
impl From<&Ky> for Key {
fn from(value: &Ky) -> Self {
value.to_owned()
}
}
impl FromStr for Key {
type Err = KeyError;
#[inline]
fn from_str(name: &str) -> Result<Self, Self::Err> {
Self::try_from(name)
}
}
impl TryFrom<&str> for Key {
type Error = KeyError;
#[inline]
fn try_from(name: &str) -> Result<Self, Self::Error> {
ensure!(is_valid_key(name), KeySnafu { name });
let name = name.to_owned();
Ok(Self { name })
}
}
impl TryFrom<String> for Key {
type Error = KeyError;
#[inline]
fn try_from(name: String) -> Result<Self, Self::Error> {
ensure!(is_valid_key(&name), KeySnafu { name });
Ok(Self { name })
}
}
#[derive(Debug, Diagnostic, Snafu)]
pub struct KeyError {
pub(crate) backtrace: Backtrace,
pub(crate) name: String,
}
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Ky {
name: str,
}
impl Ky {
#[must_use]
pub const unsafe fn from_unchecked(name: &str) -> &Self {
unsafe {
let name: *const _ = name;
let name: *const Self = name as _;
&*name
}
}
}
impl Borrow<str> for Ky {
fn borrow(&self) -> &str {
&self.name
}
}
impl Display for Ky {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.name)
}
}
impl ToOwned for Ky {
type Owned = Key;
fn to_owned(&self) -> Self::Owned {
let name = self.name.to_owned();
Key { name }
}
}
impl<'s> TryFrom<&'s str> for &'s Ky {
type Error = KeyError;
#[inline]
fn try_from(name: &'s str) -> Result<Self, Self::Error> {
ensure!(is_valid_key(name), KeySnafu { name });
Ok(unsafe { Ky::from_unchecked(name) })
}
}
pub type Map = BTreeMap<Key, Value>;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Value {
Date(Date),
Number(Decimal),
String(String),
}
impl Value {
#[must_use]
pub const fn as_number(&self) -> Option<Decimal> {
if let Self::Number(v) = self {
Some(*v)
} else {
None
}
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
if let Self::String(inner) = self {
Some(inner)
} else {
None
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Date(inner) => inner.fmt(f),
Self::Number(inner) => inner.fmt(f),
Self::String(inner) => write!(f, "{inner:?}"),
}
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::from(value.to_owned())
}
}
impl From<Date> for Value {
fn from(value: Date) -> Self {
Self::Date(value)
}
}
impl From<Decimal> for Value {
fn from(value: Decimal) -> Self {
Self::Number(value)
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<u8> for Value {
fn from(value: u8) -> Self {
Self::Number(Decimal::from(value))
}
}
impl From<u16> for Value {
fn from(value: u16) -> Self {
Self::Number(Decimal::from(value))
}
}
impl From<u32> for Value {
fn from(value: u32) -> Self {
Self::Number(Decimal::from(value))
}
}
impl From<u64> for Value {
fn from(value: u64) -> Self {
Self::Number(Decimal::from(value))
}
}
impl PartialEq<&str> for Value {
fn eq(&self, other: &&str) -> bool {
self.as_str().map_or(false, |value| value == *other)
}
}
impl PartialEq<Value> for &str {
fn eq(&self, other: &Value) -> bool {
other == self
}
}
fn is_valid_key(name: &str) -> bool {
lazy_regex::regex_is_match!(r"^(:?[a-z][a-zA-Z0-9\-_]+)$", name)
}