diff options
| author | Jakub Stachurski <jakub@wilkuu.nl> | 2026-08-22 01:00:22 +0200 |
|---|---|---|
| committer | Jakub Stachurski <jakub@wilkuu.nl> | 2026-08-22 01:00:22 +0200 |
| commit | d572e439a4be61841776c3bb9517370fbf6ad7e4 (patch) | |
| tree | c1117c830b70ce87c03b5c44e89e397e3d980108 | |
| parent | 030c489d2bc34f267cadacdec8cd13249672ccb5 (diff) | |
Split types into separate file
Diffstat (limited to '')
| -rw-r--r-- | src/types.rs | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..0060e71 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,235 @@ +use jiff::Timestamp; +use jiff::civil::DateTime; +use serde::de::{DeserializeOwned, Error}; +use serde::{Deserialize, Serialize}; +use serde_json::{Number, Value}; +use std::any::Any; +use std::collections::BTreeMap; +use std::collections::btree_map::{Iter, Keys}; +use std::hash::Hash; + +// Types +// *, unknown value +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(untagged)] +pub enum JUnknown { + Object(BTreeMap<String, JUnknown>), + Array(Vec<JUnknown>), + Uint(JUint), + Int(JInt), + Number(JNumber), + JDate(JDate), + JUTCDate(JUTCDate), + String(String), + Id(JMAPId), + Bool(bool), + Null, +} + +/// Implement from for serde_json::Value so that we can use serde to covert unknown to actual types +/// :3 +impl From<JUnknown> for Value { + fn from(value: JUnknown) -> Self { + match value { + JUnknown::Number(n) => { + Value::Number(serde_json::Number::from_f64(n).unwrap_or(Number::from(999))) + } + JUnknown::Int(n) => Value::Number(serde_json::Number::from(n)), + JUnknown::Uint(n) => Value::Number(serde_json::Number::from(n)), + JUnknown::JDate(jdate) => Value::String(jdate.0.to_string()), + JUnknown::JUTCDate(jdate) => Value::String(jdate.0.to_string()), + JUnknown::Object(btree_map) => { + let mut map = serde_json::map::Map::with_capacity(btree_map.len()); + for (k, v) in btree_map.into_iter() { + let val = v.into(); + map.insert(k, val); + } + Value::Object(map) + } + JUnknown::Array(junknowns) => { + Value::Array(junknowns.into_iter().map(|x| x.into()).collect()) + } + JUnknown::String(x) => Value::String(x), + JUnknown::Id(jmapid) => Value::String(jmapid.0), + JUnknown::Bool(bool) => Value::Bool(bool), + JUnknown::Null => Value::Null, + } + } +} + +impl<T: Into<JUnknown>> From<JArr<T>> for JUnknown { + fn from(value: JArr<T>) -> Self { + Self::Array(value.into_iter().map(|x| x.into()).collect()) + } +} + +impl<K: Into<String>, V: Into<JUnknown>> From<JObj<K, V>> for JUnknown { + fn from(value: JObj<K, V>) -> Self { + Self::Object( + value + .into_iter() + .map(|x| (x.0.into(), x.1.into())) + .collect(), + ) + } +} + +impl From<JUint> for JUnknown { + fn from(value: JUint) -> Self { + Self::Uint(value) + } +} +impl From<JInt> for JUnknown { + fn from(value: JInt) -> Self { + Self::Int(value) + } +} + +impl From<JDate> for JUnknown { + fn from(value: JDate) -> Self { + Self::JDate(value) + } +} +impl From<JUTCDate> for JUnknown { + fn from(value: JUTCDate) -> Self { + Self::JUTCDate(value) + } +} + +impl From<String> for JUnknown { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From<JMAPId> for JUnknown { + fn from(value: JMAPId) -> Self { + Self::Id(value) + } +} + +pub fn from_junknown<T: DeserializeOwned, J: Into<JUnknown>>( + ju: J, +) -> Result<T, serde_json::Error> { + let junk: JUnknown = ju.into(); + serde_json::from_value(junk.into()) +} + +pub fn to_junknown<T: Serialize>(value: T) -> Result<JUnknown, serde_json::Error> { + serde_json::to_value(value).and_then(serde_json::from_value) +} + +// JSON String type +// pub type JString = String; +// JSON Number type +pub type JNumber = f64; +// JSON Boolean type +// pub type JBool = bool; +// A[B] is a JSON object where keys are all of type A and values type B; +pub type JObj<A, B> +where + A: Serialize, + B: Serialize, + A: DeserializeOwned, + B: DeserializeOwned, += BTreeMap<A, B>; + +pub type JObject = JObj<String, JUnknown>; +/// A[] is an array of type A +pub type JArr<A> +where + A: Serialize, + A: DeserializeOwned, += Vec<A>; + +//}}} +// 1.2 The ID data type {{{ + +/// The ID data type +/// Where Id is given as a data type, it means a String of at least 1 and a maximum of 255 octets in size, +/// and it MUST only contain characters from the "URL and Filename Safe" base64 alphabet, as defined in Section 5 of [RFC4648], +/// excluding the pad character (=). This means the allowed characters are the ASCII alphanumeric characters (A-Za-z0-9), hyphen (-), and underscore (_). +/// TODO: Add the restrictions above +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)] +pub struct JMAPId(pub String); +//}}} +// 1.3 Int and UnsignedInt {{{ +pub type JInt = i64; +pub type JUint = u64; + +// }}} +// 1.4 Date and UTCDate data Types {{{ + +/// Where date is given a type, it meanse a string in date-time format [RFC3339]. +/// TODO: To ensure a normalized form, the time-setfrac MUST always be ommitted if zero, and any +/// letters in the string (eg. "T" and "Z") must be uppercase. + +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct JDate(DateTime); +impl JDate { + /// Convert from a JUTCDate type to system time + pub fn from_utcdate(date: JDate) -> Self { + // FIXME: unwrap + Self( + date.0 + .to_zoned(jiff::tz::TimeZone::system()) + .unwrap() + .datetime(), + ) + } + /// Parse from a string + pub fn from_string(str: &str) -> Result<Self, jiff::Error> { + Ok(Self(str.parse()?)) + } + /// Get the inner date for reading purposes + pub fn get_date(&self) -> &DateTime { + &self.0 + } + + /// Convert from jiff datetime + pub fn from_datetime(datetime: DateTime) -> Self { + Self(datetime) + } +} + +/// Where UTCDate is given as a type, it means a Date where the time-offset component MUST be Z (i.e., it must be in UTC time). For example, 2014-10-30T06:12:00Z +#[derive(Deserialize, Serialize, Clone, Debug)] +pub struct JUTCDate(Timestamp); +impl JUTCDate { + /// Convert from a JDate type + pub fn from_date(date: JDate) -> Self { + // FIXME: unwrap + Self( + date.0 + .to_zoned(jiff::tz::TimeZone::UTC) + .unwrap() + .timestamp(), + ) + } + + /// Parse from a string + pub fn from_string(str: &str) -> Result<Self, jiff::Error> { + Ok(Self( + str.parse::<jiff::Timestamp>()? + .to_zoned(jiff::tz::TimeZone::UTC) + .timestamp(), + )) + } + + /// Get the date (converted) + pub fn get_date(&self) -> DateTime { + self.0.to_zoned(jiff::tz::TimeZone::UTC).datetime() + } + + /// Convert from jiff datetime + pub fn from_datetime(datetime: DateTime) -> Self { + // FIXME: unwrap + Self( + datetime + .to_zoned(jiff::tz::TimeZone::UTC) + .unwrap() + .timestamp(), + ) + } +} +//}}} |
