type_protocol/
lib.rs

1//! A crate provding a simple syntax inspired by the go programming language
2//! for conveying the idea of types.
3//!
4//! # Universal Types
5//!
6//! These are common datatype found in most languages.
7//!
8//! * Boolean Types: `bool`
9//! * Signed Integer Types: `int` or `isize`, `int8`, `int16`, `int32`, `int64`, `int128`
10//! * Unsigned Integer Types: `uint` or `usize`, `uint8`, `uint16`, `uint32`, `uint64`, `uint128`
11//! * Floating Point Types: `float16`, `float32`, `float64`
12//! * Char and String types: `char`, `string`
13//!
14//! # Extension Types
15//!
16//! These are common special datatypes, prefixed with +.
17//!
18//! * `+bytes`
19//! * `+dateTime`, `+date`, `+time`, `+duration`
20//! * `+decimal`
21//! * `+uuid`
22//! * `+rgb`, `+rgba`
23//!
24//! # Types and Paths
25//!
26//! Any string segment not starting with an reserved symbol is a **Type** or a **Path** if
27//! it is not a **Builtin Type**.
28//! Buildin types use **snake_case** so you should use **PascalCase** to avoid collision.
29//!
30//! * Named Types
31//!
32//! `MyType`, `Foöbár`, `Hello World`, `2022`
33//!
34//! A validator can be provided to validate idents based on the user's needs.
35//!
36//! The [`RustIdent`] validator will fail `Hello World` and `2022`,
37//! while the [`AsciiIdent`] validator will fail `Foöbár` additionally.
38//!
39//! Note type-protocol grammer treat whitespaces like normal characters,
40//! the user is responsible for stripping them if needed.
41//!
42//! * Paths
43//!
44//! `path::to::Type`
45//!
46//! * Absolute Paths
47//!
48//! `::path::to::Type`
49//!
50//! # Optional Types
51//!
52//! `?T` represents a optional type like [`Option<T>`] or a nullable pointer.
53//!
54//! e.g. `?string`
55//!
56//! # Array Types
57//!
58//! `[N]T` represents a fix sized array type like [`[T;N]`].
59//! N has to be an integer.
60//!
61//! e.g. `[4]int`
62//!
63//! # Vec Types
64//!
65//! `[]T` represents a dynamic sized array type like [`Vec<T>`].
66//!
67//! e.g. `[]int`
68//!
69//! # Set Types
70//!
71//! `[T]` represents a collection of unique keys like [`HashSet<T>`](std::collections::HashSet).
72//!
73//! e.g. `[string]`
74//!
75//! # Map Types
76//!
77//! `[TKey]TValue` represents a collection of keys and values like [`HashMap<T>`](std::collections::HashMap).
78//!
79//! e.g. `[string]int`
80//!
81//! # Hint Types
82//!
83//! * Foreign Hint
84//!
85//! `@T` hints that T is a foreign type.
86
87
88
89
90
91
92mod derive;
93pub use derive::ToTypeProtocol;
94
95#[cfg(test)]
96mod tests;
97
98use std::{
99    str::FromStr,
100    marker::PhantomData,
101    num::{NonZeroUsize, ParseIntError},
102    fmt::{Display, Pointer}
103};
104
105pub trait IdentValidation {
106    fn validate(s: &str) -> bool;
107}
108
109#[derive(PartialEq, Eq)]
110pub struct AlwaysValid;
111#[derive(PartialEq, Eq)]
112pub struct UnicodeXID;
113#[derive(PartialEq, Eq)]
114pub struct RustIdent;
115#[derive(PartialEq, Eq)]
116pub struct AsciiIdent;
117
118impl IdentValidation for AlwaysValid {
119    fn validate(_: &str) -> bool {
120        true
121    }
122}
123
124impl IdentValidation for UnicodeXID {
125    fn validate(s: &str) -> bool {
126        let mut chars = s.chars();
127        if let Some(c) = chars.next() {
128            if unicode_ident::is_xid_start(c) {
129                chars.all(|c| unicode_ident::is_xid_continue(c))
130            } else {
131                false
132            }
133        } else {
134            false
135        }
136    }
137}
138
139impl IdentValidation for RustIdent {
140    fn validate(s: &str) -> bool {
141        let mut chars = s.chars();
142        if let Some(c) = chars.next() {
143            if unicode_ident::is_xid_start(c) || c == '_' {
144                chars.all(|c| unicode_ident::is_xid_continue(c))
145            } else {
146                false
147            }
148        } else {
149            false
150        }
151    }
152}
153
154impl IdentValidation for AsciiIdent {
155    fn validate(s: &str) -> bool {
156        let mut chars = s.chars();
157        if let Some(c) = chars.next() {
158            if matches!(c, 'a'..='z'|'A'..='Z'|'_') {
159                chars.all(|c| matches!(c, '0'..='9'|'a'..='z'|'A'..='Z'|'_'))
160            } else {
161                false
162            }
163        } else {
164            false
165        }
166    }
167}
168
169#[doc(hidden)]
170#[derive(Debug, Clone, PartialEq, Eq, Hash)]
171pub enum Never {}
172
173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
175#[non_exhaustive]
176pub enum UniversalType {
177    Bool,
178    USize,
179    ISize,
180    Int(NonZeroUsize),
181    UInt(NonZeroUsize),
182    Float(NonZeroUsize),
183    Char,
184    String,
185}
186
187impl FromStr for UniversalType {
188    type Err = ();
189
190    fn from_str(s: &str) -> Result<Self, Self::Err> {
191        Ok(match s {
192            "bool" => Self::Bool,
193            "int"|"isize" => Self::ISize,
194            "uint"|"usize" => Self::USize,
195            "int8" => Self::Int(nz!(1)),
196            "int16" => Self::Int(nz!(2)),
197            "int32" => Self::Int(nz!(4)),
198            "int64" => Self::Int(nz!(8)),
199            "int128" => Self::Int(nz!(16)),
200            "uint8" => Self::UInt(nz!(1)),
201            "uint16" => Self::UInt(nz!(2)),
202            "uint32" => Self::UInt(nz!(4)),
203            "uint64" => Self::UInt(nz!(8)),
204            "uint128" => Self::UInt(nz!(16)),
205            "float16"|"half" => Self::Float(nz!(2)),
206            "float"|"float32"|"single" => Self::Float(nz!(4)),
207            "float64"|"double" => Self::Float(nz!(8)),
208            "char" => Self::Char,
209            "string" => Self::String,
210            _ => return Err(())
211        })
212    }
213}
214
215impl Display for UniversalType {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        match self {
218            UniversalType::Bool => f.write_str("bool"),
219            UniversalType::USize => f.write_str("uint"),
220            UniversalType::ISize => f.write_str("int"),
221            UniversalType::Int(n) => {
222                f.write_str("int")?;
223                n.checked_mul(nz!(8)).unwrap().fmt(f)
224            },
225            UniversalType::UInt(n) => {
226                f.write_str("uint")?;
227                n.checked_mul(nz!(8)).unwrap().fmt(f)
228            },
229            UniversalType::Float(n) => {
230                f.write_str("float")?;
231                n.checked_mul(nz!(8)).unwrap().fmt(f)
232            },
233            UniversalType::Char => f.write_str("char"),
234            UniversalType::String => f.write_str("string"),
235        }
236    }
237}
238
239#[derive(Debug, Clone, PartialEq, Eq, Hash, strum::IntoStaticStr, strum::EnumString)]
240#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
241#[cfg_attr(feature = "rkyv", derive(rkyv::Archive))]
242#[non_exhaustive]
243pub enum ExtensionType {
244    #[strum(serialize="decimal")]
245    Decimal,
246    #[strum(serialize="datetime")]
247    DateTime,
248    #[strum(serialize="date")]
249    Date,
250    #[strum(serialize="time")]
251    Time,
252    #[strum(serialize="duration")]
253    Duration,
254    #[strum(serialize="rgb")]
255    Rgb,
256    #[strum(serialize="rgba")]
257    Rgba,
258    #[strum(serialize="uuid")]
259    Uuid,
260}
261
262
263#[derive(Debug, Clone, PartialEq, Eq, Hash)]
264#[cfg_attr(feature = "rkyv", derive(rkyv::Archive))]
265#[non_exhaustive]
266pub enum Typing<Validation = AlwaysValid> {
267    /// The `none` type.
268    None,
269    /// Universal types in almost every language.
270    ///
271    /// See [`UniversalType`]
272    Common(UniversalType),
273    /// Common types hard to describe with normal data structures.
274    /// Prefixed with `+`.
275    ///
276    /// i.e. `+datetime`, `+rgba`, `+uuid`
277    ///
278    /// See [`ExtensionType`]
279    Extension(ExtensionType),
280    /// A named type, with its contents defined elsewhere.
281    Named(String),
282    /// A type with a path.
283    Path{
284        /// if true, indicate this is an absolute path i.e.`::path`
285        absolute: bool,
286        /// path to name i.e `[path::to]::name`
287        path: Vec<String>,
288        /// last item in the path i.e `path::to::[name]`
289        name: String,
290    },
291    /// An optional or nullable type.
292    Option(Box<Self>),
293    /// A nameless product type deliminated with comma.
294    ///
295    /// i.e. `(int,string,char)`
296    Tuple(Vec<Self>),
297    /// A fix sized array.
298    ///
299    /// i.e. `[10]int`
300    Array(usize, Box<Self>),
301    /// A variable sized array.
302    ///
303    /// i.e. `[]int`
304    Vec(Box<Self>),
305    /// A set of keys.
306    ///
307    /// i.e. `[int]`
308    Set(Box<Self>),
309    /// A set of key value pairs.
310    ///
311    /// i.e. `[string]int`
312    Map(Box<Self>,Box<Self>),
313    /// A hint that the type is foreign.
314    ///
315    /// i.e. `@path::to::struct`
316    Foreign(Box<Self>),
317    #[doc(hidden)]
318    _Invalid(PhantomData<Validation>, Never)
319}
320
321impl<V> Typing<V> {
322    fn boxed(self) -> Box<Self> {
323        Box::new(self)
324    }
325}
326
327impl<V> Display for Typing<V> {
328    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329        match self {
330            Typing::None => f.write_str("none"),
331            Typing::Common(t) => t.fmt(f),
332            Typing::Extension(e) => {
333                f.write_str("+")?;
334                e.fmt(f)
335            },
336            Typing::Named(n) => f.write_str(&n),
337            Typing::Path { absolute, path, name } => {
338                if *absolute { f.write_str("::")?; }
339                for s in path {
340                    f.write_str(&s)?;
341                    f.write_str("::")?;
342                }
343                f.write_str(&name)
344            },
345            Typing::Option(t) => {
346                f.write_str("?")?;
347                t.as_ref().fmt(f)
348            },
349            Typing::Tuple(items) => {
350                f.write_str("(")?;
351                let mut iter = items.iter();
352                if let Some(first) = iter.next() {
353                    first.fmt(f)?;
354                }
355                for item in iter {
356                    f.write_str(",")?;
357                    item.fmt(f)?;
358                }
359                f.write_str(")")
360            },
361            Typing::Array(len, item) => {
362                f.write_str("[")?;
363                len.fmt(f)?;
364                f.write_str("]")?;
365                item.as_ref().fmt(f)
366            },
367            Typing::Vec(item) => {
368                f.write_str("[]")?;
369                item.as_ref().fmt(f)
370            },
371            Typing::Set(item) => {
372                f.write_str("[")?;
373                item.as_ref().fmt(f)?;
374                f.write_str("]")
375            },
376            Typing::Map(key, value) => {
377                f.write_str("[")?;
378                key.as_ref().fmt(f)?;
379                f.write_str("]")?;
380                value.as_ref().fmt(f)
381            },
382            Typing::Foreign(item) => {
383                f.write_str("@")?;
384                item.as_ref().fmt(f)
385            },
386            Typing::_Invalid(..) => unreachable!(),
387        }
388    }
389}
390
391#[derive(Debug, thiserror::Error, PartialEq, Eq)]
392pub enum Error {
393    #[error("Empty string as a type is not allowed, use 'none' instead.")]
394    Empty,
395    #[error("Parenthesis () mismatch.")]
396    ParenthesisMismatch,
397    #[error("Brackets [] mismatch.")]
398    BracketsMismatch,
399    #[error("Unknown extension type.")]
400    UnknownExtensionType,
401    #[error("Invalid ident found.")]
402    ValidationFailed,
403    #[error("{}", 0)]
404    ParseIntError(#[from]ParseIntError)
405}
406
407impl<V: IdentValidation> FromStr for Typing<V> {
408    type Err = Error;
409
410    /// type-protocol assumes compact whitespace-less strings.
411    /// This function does not identify whitespaces in any way
412    /// and will keep them in the resulting ident.
413    /// Remove them before calling this function if you need to.
414    fn from_str(s: &str) -> Result<Self, Self::Err> {
415        if s == "none" {
416            return Ok(Self::None);
417        }
418        let mut chars = s.chars();
419        Ok(match chars.next() {
420            None => return Err(Error::Empty),
421            Some(')'|']'|'}') => return Err(Error::BracketsMismatch),
422            Some('(') => {
423                if chars.next_back() != Some(')') {
424                    return Err(Error::BracketsMismatch);
425                }
426                let mut paren = 0;
427                let mut bracket = 0;
428                let result = Self::Tuple(chars.as_str()
429                    .split(|c| {
430                        match c {
431                            '[' => if bracket >= 0 { bracket += 1 },
432                            ']' => bracket -= 1,
433                            '(' => if paren >= 0 { paren += 1 },
434                            ')' => paren -= 1,
435                            ',' if paren == 0 && bracket == 0 => return true,
436                            _ => (),
437                        }
438                        return false;
439                    })
440                    .map(|x| Self::from_str(x))
441                    .collect::<Result<Vec<_>, _>>()?);
442                if paren != 0 {
443                    return Err(Error::ParenthesisMismatch)
444                } else if bracket != 0 {
445                    return Err(Error::BracketsMismatch)
446                } else {
447                    result
448                }
449            },
450            Some('[') => {
451                let mut paren = 0;
452                let mut bracket = 1;
453                let (key, value) = match chars.as_str().split_once(|c| {
454                    match c {
455                        '[' => if bracket >= 0 { bracket += 1 },
456                        ']' => {
457                            bracket -= 1;
458                            if bracket == 0 {
459                                return true;
460                            }
461                        },
462                        '(' => if paren >= 0 { paren += 1 },
463                        ')' => paren -= 1,
464                        _ => (),
465                    }
466                    return false;
467                }) {
468                    Some((k, v)) => (k, v),
469                    None => return Err(Error::BracketsMismatch),
470                };
471                if key.len() == 0 {
472                    Self::Vec(Self::from_str(value)?.boxed())
473                } else if value.len() == 0 {
474                    Self::Set(Self::from_str(key)?.boxed())
475                } else if key.chars().all(|x| matches!(x, '0'..='9')) {
476                    Self::Array(key.parse()?, Self::from_str(value)?.boxed())
477                } else {
478                    Self::Map(Self::from_str(key)?.boxed(), Self::from_str(value)?.boxed())
479                }
480            },
481            Some('?') => Self::Option(Self::from_str(chars.as_str())?.boxed()),
482            Some('@') => Self::Foreign(Self::from_str(chars.as_str())?.boxed()),
483            Some('+') => {
484                if let Ok(t) = ExtensionType::from_str(chars.as_str()) {
485                    Self::Extension(t)
486                } else {
487                    return Err(Error::UnknownExtensionType);
488                }
489            },
490            _ => {
491                if let Ok(t) = UniversalType::from_str(s) {
492                    Self::Common(t)
493                } else if s.contains("::") {
494                    let absolute = s.starts_with("::");
495                    let s = s.trim_start_matches("::");
496                    let mut path: Vec<String> = s.split("::").map(|x| x.to_owned()).collect();
497                    if path.iter().any(|x| !V::validate(x)) {
498                        return Err(Error::ValidationFailed);
499                    }
500                    let name = path.pop().ok_or(Error::Empty)?;
501                    Self::Path { absolute, path, name }
502                } else if V::validate(s) {
503                    Self::Named(s.to_owned())
504                } else {
505                    return Err(Error::ValidationFailed);
506                }
507            }
508        })
509    }
510}
511
512#[cfg(feature = "serde")]
513const _: () = {
514    use serde::{Serialize, Deserialize};
515
516    impl<V> Serialize for Typing<V> {
517        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
518            self.to_string().serialize(serializer)
519        }
520    }
521
522    impl<'de, V: IdentValidation> Deserialize<'de> for Typing<V> {
523        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
524            Self::from_str(<&str>::deserialize(deserializer)?)
525                .map_err(|e| serde::de::Error::custom(e.to_string()))
526        }
527    }
528};