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}