summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/types.rs235
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(),
+ )
+ }
+}
+//}}}