1pub mod types;
2pub mod utils;
3
4#[cfg(feature = "generate")]
5pub use linkme;
6
7#[cfg(feature = "experimental")]
8use serde_reflection;
9
10#[cfg(feature = "generate")]
11pub struct Context {
12 tags: std::collections::BTreeSet<String>,
13 #[cfg(feature = "experimental")]
14 tracer: Option<(Vec<(String, types::TypeRoot)>, serde_reflection::Tracer)>,
15 untraced: Vec<types::TypeRoot>,
16 errors: Vec<String>,
17}
18
19#[cfg(feature = "generate")]
20impl Context {
21 pub fn add_type_root(
23 &mut self,
24 names_json: &str,
25 file_name: &str,
26 line: u32,
27 tags: &[&str],
28 ) -> () {
29 if !self.should_include(tags) {
30 return;
31 }
32 let type_root = self.create_type_root(names_json, file_name, line);
33 self.untraced.push(type_root);
34 }
35
36 fn should_include(&self, tags: &[&str]) -> bool {
37 if tags.is_empty() {
38 if !self.tags.is_empty() {
39 return false;
40 }
41 } else {
42 let mut found = false;
43 for tag in tags {
44 if self.tags.contains(*tag) {
45 found = true;
46 continue;
47 }
48 }
49 if !found {
50 return false;
51 }
52 }
53 return true;
54 }
55
56 fn create_type_root(
57 &mut self,
58 names_json: &str,
59 file_name: &str,
60 line: u32,
61 ) -> types::TypeRoot {
62 let mut type_root = serde_json::from_str::<types::TypeRoot>(names_json)
63 .expect("Incompatible versions of generate & code");
64
65 type_root.file = file_name.to_string();
66 type_root.line = line;
67 type_root
68 }
69
70 #[cfg(features = "experimental")]
71 pub fn trace_type_root<T>(
72 &mut self,
73 names_json: &str,
74 file_name: &str,
75 line: u32,
76 tags: &[&str],
77 ) -> () {
78 if !self.should_include(tags) {
79 return;
80 }
81 let type_root = self.create_type_root(names_json, file_name, line);
82
83 match &type_root.inner.value {
84 types::ContainerFormat::Enum(_) => {
85 for types::Spanned {
86 value: (key, _value),
87 ..
88 } in type_root.inner.serde_attrs.iter()
89 {
90 if &key.value == "tag" || &key.value == "content" {
91 eprintln!("We can't trace enums with tag and content!");
93 return;
94 }
95 }
96 }
97 _ => {}
98 }
99
100 if let Some((ref mut tracer, ref mut merge)) = self.tracer {
101 type TODO = ();
102 todo!("Trace simple enabled for serialize only?");
103 match tracer.trace_simple_type::<TODO>() {
104 Ok((serde_reflection::Format::TypeName(name), _samples)) => {
105 merge.push((name, type_root));
106 }
107 Ok((other_format, _samples)) => {
108 todo!("handle other format {other_format:?}\nFor type root:{type_root:#?}")
109 }
110 Err(err) => {
111 self.errors.push(format!("{err:#?}"));
112 }
113 }
114 } else {
115 self.untraced.push(type_root);
116 }
117 }
118}
119
120#[cfg(feature = "generate")]
121#[linkme::distributed_slice]
122pub static CODEGEN_ITEMS: [fn(&mut Context)] = [..];
123
124#[cfg(feature = "generate")]
125#[track_caller]
126pub fn get_types_by_tags(tags: &[String]) -> Vec<types::TypeRoot> {
127 let mut context = Context {
128 tags: tags.into_iter().cloned().map(String::from).collect(),
129 errors: Vec::new(),
130 #[cfg(feature = "experimental")]
131 tracer: None,
132 untraced: Vec::new(),
134 };
135 {
136 let mut context = &mut context;
137 for gen in CODEGEN_ITEMS {
138 gen(&mut context);
139 if !context.errors.is_empty() {
140 for err in &context.errors {
141 eprintln!("{err}");
142 }
143 }
144 context.errors.clear();
145 }
146 }
147
148 #[allow(unused_mut)]
149 let Context {
150 errors,
151 untraced: mut type_roots,
152 #[cfg(feature = "experimental")]
153 tracer,
154 tags,
155 } = context;
156
157 if !errors.is_empty() {
158 eprintln!("Context trace errors for tags {tags:?}:");
159 for err in errors {
160 eprintln!(" * {err:?}");
161 }
162 }
163
164 #[cfg(feature = "experimental")]
165 if let Some((tracer, merge)) = tracer {
166 let registry = tracer.registry().expect("constructing registry");
167 eprintln!("{registry:#?}");
168 type_roots.extend(types.into_iter().map(|(name, mut type_root)| {
169 use types::{ContainerFormat, VariantFormat};
170 use serde_reflection as sr;
171 let format = registry.get(&name).expect("type exists in registry (if not, maybe alias unsupported)");
172 match (&mut type_root.inner.value, format) {
173 (ContainerFormat::Struct(ref mut named_formats), sr::ContainerFormat::Struct(ref reflected_named_formats)) => {
174 for named_format in named_formats.iter_mut() {
175 let serialize_name = named_format.serialize_name().to_string();
176 named_format.value.replace_incomplete(
177 reflected_named_formats.iter().find_map(|i| {
178 if &i.name == &serialize_name {
179 Some(format_to_format(&i.value))
180 } else {
181 None
182 }
183 }).expect("found matching struct item")
184 );
185 }
186 },
187 (ContainerFormat::Enum(ref mut enu), sr::ContainerFormat::Enum(ref reflected_enu)) => {
188 for (idx, ref mut enu_variant) in enu.iter_mut() {
189 let reflected_variant = reflected_enu.get(idx).expect("found matching enum new type variant");
190 match (&mut enu_variant.value, &reflected_variant.value) {
191 (VariantFormat::Unit, sr::VariantFormat::Unit) => {
192 },
194 (VariantFormat::NewType(ref mut format), sr::VariantFormat::NewType(reflected)) => {
195 format.replace_incomplete(format_to_format(&reflected));
196 },
197 (VariantFormat::Tuple(ref mut formats), sr::VariantFormat::Tuple(reflected_formats)) => {
198 for (format, reflected) in formats.iter_mut().zip(reflected_formats.iter()) {
199 format.replace_incomplete(format_to_format(&reflected));
200 }
201 },
202 (VariantFormat::Struct(ref mut named_formats), sr::VariantFormat::Struct(reflected_named_formats)) => {
203 for named_format in named_formats.iter_mut() {
204 let serialize_name = named_format.serialize_name().to_string();
205 named_format.value.replace_incomplete({
206 reflected_named_formats.iter().find_map(|i| {
207 if &i.name == &serialize_name {
208 Some(format_to_format(&i.value))
209 } else {
210 None
211 }
212 }).expect("found matching struct item")
213 });
214 }
215 },
216 (other, reflected_other) => {
217 panic!("Unknown enum combination {other:#?} VERSUS {reflected_other:?}");
218 }
219 };
220 }
221 },
222 (ContainerFormat::UnitStruct, sr::ContainerFormat::UnitStruct) => {
223 },
225 (ContainerFormat::NewTypeStruct(format), sr::ContainerFormat::NewTypeStruct(reflected_format)) => {
226 format.replace_incomplete(format_to_format(&reflected_format));
227 },
228 (ContainerFormat::TupleStruct(formats), sr::ContainerFormat::TupleStruct(reflected_formats)) => {
229 for (ref mut format, reflected_format) in formats.iter_mut().zip(reflected_formats.iter()) {
230 format.replace_incomplete(format_to_format(&reflected_format));
231 }
232 },
233 (named_type, reflected_type) => {
234 panic!("Mismatch between containers (do we need to handle flatten or similar correctly?) {named_type:#?} VERSUS {reflected_type:#?}")
235 }
236 }
237
238 type_root
239 }));
240 }
241
242 type_roots
243}
244
245#[cfg(feature = "experimental")]
246fn format_to_format(input: &serde_reflection::Format) -> types::Format {
247 use serde_reflection::Format as SFormat;
248 use types::Format as IFormat;
249 match input {
250 SFormat::Variable(_) => unreachable!(),
251 SFormat::TypeName(name) => IFormat::TypeName(name.clone()),
252 SFormat::Unit => IFormat::Unit,
253 SFormat::Bool => IFormat::Bool,
254 SFormat::I8 => IFormat::I8,
255 SFormat::I16 => IFormat::I16,
256 SFormat::I32 => IFormat::I32,
257 SFormat::I64 => IFormat::I64,
258 SFormat::I128 => IFormat::I128,
259 SFormat::U8 => IFormat::U8,
260 SFormat::U16 => IFormat::U16,
261 SFormat::U32 => IFormat::U32,
262 SFormat::U64 => IFormat::U64,
263 SFormat::U128 => IFormat::U128,
264 SFormat::F32 => IFormat::F32,
265 SFormat::F64 => IFormat::F64,
266 SFormat::Char => IFormat::Char,
267 SFormat::Str => IFormat::Str,
268 SFormat::Bytes => IFormat::Bytes,
269 SFormat::Option(inner) => IFormat::Option(format_to_format(&inner).into()),
270 SFormat::Seq(inner) => IFormat::Seq(format_to_format(&inner).into()),
271 SFormat::Map { key, value } => IFormat::Map {
272 key: format_to_format(&key).into(),
273 value: format_to_format(&value).into(),
274 },
275 SFormat::Tuple(inner) => IFormat::Tuple(inner.iter().map(format_to_format).collect()),
276 SFormat::TupleArray { content, size } => IFormat::TupleArray {
277 content: format_to_format(&content).into(),
278 size: *size,
279 },
280 }
281}