schema_bridge_core/
lib.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4pub enum Schema {
5 String,
6 Number,
7 Boolean,
8 Null,
9 Any,
10 Array(Box<Schema>),
11 Object(Vec<(String, Schema)>),
12 Enum(Vec<String>), Union(Vec<Schema>),
14 Tuple(Vec<Schema>),
15 Ref(String), }
19
20pub trait SchemaBridge {
21 fn to_ts() -> String;
22 fn to_schema() -> Schema;
23}
24
25impl SchemaBridge for String {
27 fn to_ts() -> String {
28 "string".to_string()
29 }
30 fn to_schema() -> Schema {
31 Schema::String
32 }
33}
34
35impl SchemaBridge for i32 {
36 fn to_ts() -> String {
37 "number".to_string()
38 }
39 fn to_schema() -> Schema {
40 Schema::Number
41 }
42}
43
44impl SchemaBridge for f64 {
45 fn to_ts() -> String {
46 "number".to_string()
47 }
48 fn to_schema() -> Schema {
49 Schema::Number
50 }
51}
52
53impl SchemaBridge for bool {
54 fn to_ts() -> String {
55 "boolean".to_string()
56 }
57 fn to_schema() -> Schema {
58 Schema::Boolean
59 }
60}
61
62impl<T: SchemaBridge> SchemaBridge for Option<T> {
63 fn to_ts() -> String {
64 format!("{} | null", T::to_ts())
65 }
66 fn to_schema() -> Schema {
67 Schema::Union(vec![T::to_schema(), Schema::Null])
68 }
69}
70
71impl<T: SchemaBridge> SchemaBridge for Vec<T> {
72 fn to_ts() -> String {
73 format!("{}[]", T::to_ts())
74 }
75 fn to_schema() -> Schema {
76 Schema::Array(Box::new(T::to_schema()))
77 }
78}
79
80pub fn generate_ts_file(types: Vec<(&str, String)>) -> String {
82 let mut content = String::new();
83 content.push_str("// This file is auto-generated by schema-bridge\n\n");
84
85 for (name, ts_def) in types {
86 content.push_str(&format!("export type {} = {};\n\n", name, ts_def));
87 }
88
89 content
90}
91
92pub fn export_to_file(types: Vec<(&str, String)>, path: &str) -> std::io::Result<()> {
94 let content = generate_ts_file(types);
95 std::fs::write(path, content)
96}
97
98#[macro_export]
100macro_rules! export_types {
101 ($path:expr, $($name:ident),+ $(,)?) => {{
102 let types = vec![
103 $((stringify!($name), $name::to_ts()),)+
104 ];
105 $crate::export_to_file(types, $path)
106 }};
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 #[test]
114 fn test_string_to_ts() {
115 assert_eq!(String::to_ts(), "string");
116 }
117
118 #[test]
119 fn test_i32_to_ts() {
120 assert_eq!(i32::to_ts(), "number");
121 }
122
123 #[test]
124 fn test_f64_to_ts() {
125 assert_eq!(f64::to_ts(), "number");
126 }
127
128 #[test]
129 fn test_bool_to_ts() {
130 assert_eq!(bool::to_ts(), "boolean");
131 }
132
133 #[test]
134 fn test_option_to_ts() {
135 assert_eq!(Option::<String>::to_ts(), "string | null");
136 assert_eq!(Option::<i32>::to_ts(), "number | null");
137 }
138
139 #[test]
140 fn test_vec_to_ts() {
141 assert_eq!(Vec::<String>::to_ts(), "string[]");
142 assert_eq!(Vec::<i32>::to_ts(), "number[]");
143 }
144
145 #[test]
146 fn test_nested_vec() {
147 assert_eq!(Vec::<Vec::<String>>::to_ts(), "string[][]");
148 }
149
150 #[test]
151 fn test_optional_vec() {
152 assert_eq!(Option::<Vec::<String>>::to_ts(), "string[] | null");
153 }
154
155 #[test]
156 fn test_generate_ts_file() {
157 let types = vec![
158 ("User", "{ name: string; age: number; }".to_string()),
159 ("Status", "'Active' | 'Inactive'".to_string()),
160 ];
161
162 let result = generate_ts_file(types);
163
164 assert!(result.contains("// This file is auto-generated by schema-bridge"));
165 assert!(result.contains("export type User = { name: string; age: number; };"));
166 assert!(result.contains("export type Status = 'Active' | 'Inactive';"));
167 }
168
169 #[test]
170 fn test_schema_enum() {
171 let schema = Schema::String;
172 assert_eq!(schema, Schema::String);
173
174 let schema = Schema::Array(Box::new(Schema::Number));
175 assert!(matches!(schema, Schema::Array(_)));
176 }
177}