trans_schema/
lib.rs

1use heck::*;
2use once_cell::sync::Lazy;
3use std::collections::{HashMap, HashSet};
4use std::sync::{Arc, Mutex};
5
6pub use trans_schema_derive::*;
7
8#[derive(Debug, PartialEq, Eq, Hash, Clone)]
9pub struct Name(String);
10
11impl Name {
12    pub fn new(name: String) -> Self {
13        Self(name.to_camel_case())
14    }
15    pub fn raw(&self) -> String {
16        self.0.clone()
17    }
18    pub fn snake_case(&self, conv: impl FnOnce(&str) -> String) -> String {
19        conv(&self.0).to_snake_case()
20    }
21    pub fn camel_case(&self, conv: impl FnOnce(&str) -> String) -> String {
22        conv(&self.0).to_camel_case()
23    }
24    pub fn shouty_snake_case(&self, conv: impl FnOnce(&str) -> String) -> String {
25        conv(&self.0).to_shouty_snake_case()
26    }
27    pub fn mixed_case(&self, conv: impl FnOnce(&str) -> String) -> String {
28        conv(&self.0).to_mixed_case()
29    }
30}
31
32#[derive(Debug, PartialEq, Eq, Hash, Clone)]
33pub struct Field {
34    pub name: Name,
35    pub schema: Arc<Schema>,
36}
37
38#[derive(Debug, PartialEq, Eq, Hash, Clone)]
39pub struct Struct {
40    pub magic: Option<i32>,
41    pub name: Name,
42    pub fields: Vec<Field>,
43}
44
45#[derive(Debug, PartialEq, Eq, Hash, Clone)]
46pub enum Schema {
47    Bool,
48    Int32,
49    Int64,
50    Float32,
51    Float64,
52    String,
53    Struct(Struct),
54    OneOf {
55        base_name: Name,
56        variants: Vec<Struct>,
57    },
58    Option(Arc<Schema>),
59    Vec(Arc<Schema>),
60    Map(Arc<Schema>, Arc<Schema>),
61    Enum {
62        base_name: Name,
63        variants: Vec<Name>,
64    },
65}
66
67impl Schema {
68    pub fn full_name(&self) -> Name {
69        match self {
70            Schema::Bool => Name("Bool".to_owned()),
71            Schema::Int32 => Name("Int32".to_owned()),
72            Schema::Int64 => Name("Int64".to_owned()),
73            Schema::Float32 => Name("Float32".to_owned()),
74            Schema::Float64 => Name("Float64".to_owned()),
75            Schema::String => Name("String".to_owned()),
76            Schema::Struct(Struct { name, .. }) => name.clone(),
77            Schema::OneOf { base_name, .. } => base_name.to_owned(),
78            Schema::Option(inner) => Name(format!("Opt{}", inner.full_name().0)),
79            Schema::Vec(inner) => Name(format!("Vec{}", inner.full_name().0)),
80            Schema::Map(key, value) => {
81                Name(format!("Map{}{}", key.full_name().0, value.full_name().0))
82            }
83            Schema::Enum { base_name, .. } => base_name.clone(),
84        }
85    }
86    pub fn hashable(&self) -> bool {
87        match self {
88            Self::Bool | Self::Int32 | Self::Int64 | Self::String => true,
89            Self::Float32 | Self::Float64 => false,
90            Self::Option(_) => false,
91            Self::Struct(Struct { fields, .. }) => {
92                fields.iter().all(|field| field.schema.hashable())
93            }
94            Self::OneOf { .. } => false,
95            Self::Vec(_) => false,
96            Self::Map(_, _) => false,
97            Self::Enum { .. } => true,
98        }
99    }
100}
101
102pub trait Schematic {
103    fn create_schema() -> Schema;
104}
105
106pub fn schema<T: Schematic + 'static>() -> Arc<Schema> {
107    static MAP: Lazy<Mutex<HashSet<Arc<Schema>>>> = Lazy::new(|| Mutex::new(HashSet::new()));
108    let schema = T::create_schema();
109    if !MAP.lock().unwrap().contains(&schema) {
110        let schema = Arc::new(T::create_schema());
111        MAP.lock().unwrap().insert(schema);
112    }
113    MAP.lock().unwrap().get(&schema).unwrap().clone()
114}
115
116impl Schematic for bool {
117    fn create_schema() -> Schema {
118        Schema::Bool
119    }
120}
121
122impl Schematic for usize {
123    fn create_schema() -> Schema {
124        Schema::Int32
125    }
126}
127
128impl Schematic for i32 {
129    fn create_schema() -> Schema {
130        Schema::Int32
131    }
132}
133
134impl Schematic for i64 {
135    fn create_schema() -> Schema {
136        Schema::Int64
137    }
138}
139
140impl Schematic for f32 {
141    fn create_schema() -> Schema {
142        Schema::Float32
143    }
144}
145
146impl Schematic for f64 {
147    fn create_schema() -> Schema {
148        Schema::Float64
149    }
150}
151
152impl Schematic for String {
153    fn create_schema() -> Schema {
154        Schema::String
155    }
156}
157
158impl<T: Schematic + 'static> Schematic for Option<T> {
159    fn create_schema() -> Schema {
160        Schema::Option(schema::<T>())
161    }
162}
163
164impl<T: Schematic + 'static> Schematic for Vec<T> {
165    fn create_schema() -> Schema {
166        Schema::Vec(schema::<T>())
167    }
168}
169
170impl<K: Schematic + 'static, V: Schematic + 'static> Schematic for HashMap<K, V> {
171    fn create_schema() -> Schema {
172        Schema::Map(schema::<K>(), schema::<V>())
173    }
174}
175
176#[test]
177fn test() {
178    assert_eq!(*schema::<i32>(), Schema::Int32);
179    assert_eq!(*schema::<i64>(), Schema::Int64);
180    assert_eq!(*schema::<f32>(), Schema::Float32);
181    assert_eq!(*schema::<f64>(), Schema::Float64);
182    assert_eq!(*schema::<String>(), Schema::String);
183}