1use std::any::TypeId;
8
9use crate::codegen_types::RegisteredType;
10
11pub trait TypegenModulePath {
13 const PATH: &'static str;
14}
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct TypegenModule {
19 pub path: String,
22 pub declarations: Vec<Declaration>,
24 pub barrels: bool,
26 pub registered_reexports: Vec<TypeId>,
28}
29
30impl TypegenModule {
31 #[must_use]
33 pub fn new_typed<M: TypegenModulePath>() -> Self {
34 Self::new(M::PATH)
35 }
36
37 pub fn new(path: impl Into<String>) -> Self {
39 Self {
40 path: path.into(),
41 declarations: Vec::new(),
42 barrels: true,
43 registered_reexports: Vec::new(),
44 }
45 }
46
47 #[must_use]
49 pub fn reexport_type<T: RegisteredType>(mut self) -> Self {
50 let type_id = TypeId::of::<T>();
51 if !self.registered_reexports.contains(&type_id) {
52 self.registered_reexports.push(type_id);
53 }
54 self
55 }
56
57 #[must_use]
59 pub fn declare(mut self, declaration: Declaration) -> Self {
60 self.declarations.push(declaration);
61 self
62 }
63
64 #[must_use]
66 pub const fn with_barrels(mut self, barrels: bool) -> Self {
67 self.barrels = barrels;
68 self
69 }
70
71 #[must_use]
73 pub fn declare_registry(mut self, registry: Registry) -> Self {
74 self.declarations.extend(registry.into_declarations());
75 self
76 }
77}
78
79#[derive(Clone, Debug, PartialEq)]
81pub struct RegistryEntry {
82 pub name: String,
83 pub key: Option<Value>,
84 pub value: Value,
85}
86
87impl RegistryEntry {
88 pub fn new(name: impl Into<String>, value: Value) -> Self {
89 Self {
90 name: name.into(),
91 key: None,
92 value,
93 }
94 }
95
96 pub fn keyed(name: impl Into<String>, key: impl Into<Value>, value: Value) -> Self {
97 Self {
98 name: name.into(),
99 key: Some(key.into()),
100 value,
101 }
102 }
103}
104
105#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct RegistryNames {
108 pub all: String,
109 pub index: String,
110 pub find: String,
111 pub parameter: String,
112}
113
114impl RegistryNames {
115 pub fn new(all: impl Into<String>, index: impl Into<String>, find: impl Into<String>) -> Self {
116 Self {
117 all: all.into(),
118 index: index.into(),
119 find: find.into(),
120 parameter: "key".into(),
121 }
122 }
123
124 #[must_use]
125 pub fn with_parameter(mut self, parameter: impl Into<String>) -> Self {
126 self.parameter = parameter.into();
127 self
128 }
129}
130
131#[derive(Clone, Debug, PartialEq)]
134pub struct Registry {
135 pub entries: Vec<RegistryEntry>,
136 pub value_type: Type,
137 pub key_field: String,
138 pub all_name: String,
139 pub index_name: String,
140 pub find_name: String,
141 pub parameter: String,
142 pub key_type: Type,
143}
144
145impl Registry {
146 #[must_use]
147 pub fn for_registered<T: RegisteredType>(names: RegistryNames) -> Self {
148 Self {
149 entries: Vec::new(),
150 value_type: Type::registered::<T>(),
151 key_field: String::new(),
152 all_name: names.all,
153 index_name: names.index,
154 find_name: names.find,
155 parameter: names.parameter,
156 key_type: Type::String,
157 }
158 }
159
160 pub fn new(
161 value_type: Type,
162 key_field: impl Into<String>,
163 all_name: impl Into<String>,
164 index_name: impl Into<String>,
165 find_name: impl Into<String>,
166 ) -> Self {
167 Self {
168 entries: Vec::new(),
169 value_type,
170 key_field: key_field.into(),
171 all_name: all_name.into(),
172 index_name: index_name.into(),
173 find_name: find_name.into(),
174 parameter: "key".into(),
175 key_type: Type::String,
176 }
177 }
178
179 #[must_use]
180 pub fn entry(mut self, entry: RegistryEntry) -> Self {
181 self.entries.push(entry);
182 self
183 }
184
185 #[must_use]
186 pub fn with_parameter(mut self, parameter: impl Into<String>) -> Self {
187 self.parameter = parameter.into();
188 self
189 }
190
191 #[must_use]
192 pub fn with_key_type(mut self, key_type: Type) -> Self {
193 self.key_type = key_type;
194 self
195 }
196
197 fn into_declarations(self) -> Vec<Declaration> {
198 let mut declarations = self
199 .entries
200 .iter()
201 .map(|entry| {
202 Declaration::constant(
203 entry.name.clone(),
204 self.value_type.clone(),
205 entry.value.clone(),
206 )
207 })
208 .collect::<Vec<_>>();
209 declarations.push(Declaration::constant(
210 self.all_name.clone(),
211 Type::array(self.value_type.clone()),
212 Value::Array(
213 self.entries
214 .iter()
215 .map(|entry| Value::reference(entry.name.clone()))
216 .collect(),
217 ),
218 ));
219 let keyed_entries = self
220 .entries
221 .iter()
222 .map(|entry| entry.key.clone().map(|key| (key, entry.name.clone())))
223 .collect::<Option<Vec<_>>>();
224 if let Some(entries) = keyed_entries {
225 declarations.push(Declaration::KeyedIndex {
226 name: self.index_name.clone(),
227 entries,
228 value_type: self.value_type.clone(),
229 });
230 } else {
231 declarations.push(Declaration::Index {
232 name: self.index_name.clone(),
233 source: self.all_name,
234 key_field: self.key_field,
235 value_type: self.value_type.clone(),
236 });
237 }
238 declarations.push(Declaration::Find {
239 name: self.find_name,
240 index: self.index_name,
241 parameter: self.parameter,
242 key_type: self.key_type,
243 value_type: self.value_type,
244 });
245 declarations
246 }
247}
248
249#[derive(Clone, Debug, PartialEq, Eq)]
251pub enum Type {
252 Registered(TypeId),
253 String,
254 Boolean,
255 Number,
256 Named(String),
257 Array(Box<Self>),
258 Optional(Box<Self>),
259 StringUnion(Vec<String>),
260 Object(Vec<Field>),
261 Record(Box<Self>, Box<Self>),
262}
263
264impl Type {
265 #[must_use]
266 pub const fn registered<T: RegisteredType>() -> Self {
267 Self::Registered(TypeId::of::<T>())
268 }
269
270 pub fn named(name: impl Into<String>) -> Self {
271 Self::Named(name.into())
272 }
273
274 #[must_use]
275 pub fn array(item: Self) -> Self {
276 Self::Array(Box::new(item))
277 }
278
279 #[must_use]
280 pub fn optional(inner: Self) -> Self {
281 Self::Optional(Box::new(inner))
282 }
283
284 #[must_use]
285 pub fn record(key: Self, value: Self) -> Self {
286 Self::Record(Box::new(key), Box::new(value))
287 }
288}
289
290#[derive(Clone, Debug, PartialEq, Eq)]
292pub struct Field {
293 pub name: String,
294 pub ty: Type,
295 pub optional: bool,
296}
297
298impl Field {
299 pub fn new(name: impl Into<String>, ty: Type) -> Self {
300 Self {
301 name: name.into(),
302 ty,
303 optional: false,
304 }
305 }
306
307 #[must_use]
308 pub const fn optional(mut self) -> Self {
309 self.optional = true;
310 self
311 }
312}
313
314#[derive(Clone, Debug, PartialEq)]
316pub enum Value {
317 Null,
318 String(String),
319 Bool(bool),
320 Integer(i64),
321 Unsigned(u64),
322 Float(f64),
323 Reference(String),
324 Array(Vec<Self>),
325 Object(Vec<(String, Self)>),
326}
327
328impl Value {
329 pub fn from_serializable(value: &impl serde::Serialize) -> serde_json::Result<Self> {
335 Self::try_from(serde_json::to_value(value)?)
336 }
337
338 pub fn string(value: impl Into<String>) -> Self {
339 Self::String(value.into())
340 }
341
342 pub fn reference(name: impl Into<String>) -> Self {
343 Self::Reference(name.into())
344 }
345
346 pub fn object(entries: impl IntoIterator<Item = (impl Into<String>, Self)>) -> Self {
347 Self::Object(
348 entries
349 .into_iter()
350 .map(|(key, value)| (key.into(), value))
351 .collect(),
352 )
353 }
354}
355
356impl TryFrom<serde_json::Value> for Value {
357 type Error = serde_json::Error;
358
359 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
360 Ok(match value {
361 serde_json::Value::Null => Self::Null,
362 serde_json::Value::Bool(value) => Self::Bool(value),
363 serde_json::Value::String(value) => Self::String(value),
364 serde_json::Value::Number(value) => {
365 if let Some(value) = value.as_i64() {
366 Self::Integer(value)
367 } else if let Some(value) = value.as_u64() {
368 Self::Unsigned(value)
369 } else {
370 Self::Float(value.as_f64().ok_or_else(|| {
371 <serde_json::Error as serde::de::Error>::custom(
372 "JSON number cannot be represented in typegen IR",
373 )
374 })?)
375 }
376 }
377 serde_json::Value::Array(values) => Self::Array(
378 values
379 .into_iter()
380 .map(Self::try_from)
381 .collect::<Result<_, _>>()?,
382 ),
383 serde_json::Value::Object(entries) => {
384 let mut entries = entries
385 .into_iter()
386 .map(|(key, value)| Ok((key, Self::try_from(value)?)))
387 .collect::<Result<Vec<_>, serde_json::Error>>()?;
388 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
389 Self::Object(entries)
390 }
391 })
392 }
393}
394
395impl From<&str> for Value {
396 fn from(value: &str) -> Self {
397 Self::string(value)
398 }
399}
400
401impl From<String> for Value {
402 fn from(value: String) -> Self {
403 Self::String(value)
404 }
405}
406
407impl From<bool> for Value {
408 fn from(value: bool) -> Self {
409 Self::Bool(value)
410 }
411}
412
413#[derive(Clone, Debug, PartialEq, Eq)]
415pub enum Operand {
416 ParameterField(String),
417 String(String),
418 Bool(bool),
419}
420
421#[derive(Clone, Debug, PartialEq, Eq)]
423pub enum Predicate {
424 Equal(Operand, Operand),
425 NotEqual(Operand, Operand),
426 And(Vec<Self>),
427 Or(Vec<Self>),
428}
429
430#[derive(Clone, Debug, PartialEq)]
432pub enum Declaration {
433 Import {
434 names: Vec<String>,
435 from: String,
436 type_only: bool,
437 },
438 TypeAlias {
439 name: String,
440 doc: Option<String>,
441 ty: Type,
442 },
443 Const {
444 name: String,
445 doc: Option<String>,
446 ty: Option<Type>,
447 value: Value,
448 immutable: bool,
449 satisfies: Option<Type>,
450 },
451 FilteredArray {
452 name: String,
453 source: String,
454 parameter: String,
455 predicate: Predicate,
456 },
457 Index {
458 name: String,
459 source: String,
460 key_field: String,
461 value_type: Type,
462 },
463 KeyedIndex {
464 name: String,
465 entries: Vec<(Value, String)>,
466 value_type: Type,
467 },
468 Find {
469 name: String,
470 index: String,
471 parameter: String,
472 key_type: Type,
473 value_type: Type,
474 },
475 LookupOr {
476 name: String,
477 index: String,
478 parameter: String,
479 key_type: Type,
480 value_type: Type,
481 fallback: Value,
482 },
483}
484
485impl Declaration {
486 pub fn import(
488 names: impl IntoIterator<Item = impl Into<String>>,
489 from: impl Into<String>,
490 ) -> Self {
491 Self::Import {
492 names: names.into_iter().map(Into::into).collect(),
493 from: from.into(),
494 type_only: false,
495 }
496 }
497
498 pub fn import_type(
500 names: impl IntoIterator<Item = impl Into<String>>,
501 from: impl Into<String>,
502 ) -> Self {
503 Self::Import {
504 names: names.into_iter().map(Into::into).collect(),
505 from: from.into(),
506 type_only: true,
507 }
508 }
509
510 pub fn type_alias(name: impl Into<String>, ty: Type) -> Self {
511 Self::TypeAlias {
512 name: name.into(),
513 doc: None,
514 ty,
515 }
516 }
517
518 pub fn constant(name: impl Into<String>, ty: Type, value: Value) -> Self {
519 Self::Const {
520 name: name.into(),
521 doc: None,
522 ty: Some(ty),
523 value,
524 immutable: false,
525 satisfies: None,
526 }
527 }
528
529 pub fn inferred_constant(name: impl Into<String>, value: Value) -> Self {
530 Self::Const {
531 name: name.into(),
532 doc: None,
533 ty: None,
534 value,
535 immutable: false,
536 satisfies: None,
537 }
538 }
539
540 #[must_use]
542 pub fn documented(mut self, doc: impl Into<String>) -> Self {
543 match &mut self {
544 Self::TypeAlias { doc: target, .. } | Self::Const { doc: target, .. } => {
545 *target = Some(doc.into());
546 }
547 _ => {}
548 }
549 self
550 }
551
552 #[must_use]
554 pub const fn immutable(mut self) -> Self {
555 if let Self::Const { immutable, .. } = &mut self {
556 *immutable = true;
557 }
558 self
559 }
560
561 #[must_use]
563 pub fn satisfies(mut self, ty: Type) -> Self {
564 if let Self::Const { satisfies, .. } = &mut self {
565 *satisfies = Some(ty);
566 }
567 self
568 }
569}
570
571#[cfg(test)]
572mod tests {
573 use serde::Serialize;
574
575 use super::{Declaration, Registry, RegistryEntry, RegistryNames, TypegenModule, Value};
576
577 #[derive(Serialize)]
578 struct Example<'a> {
579 name: &'a str,
580 optional: Option<bool>,
581 count: u64,
582 }
583
584 #[test]
585 fn converts_serializable_values_without_redeclaring_their_shape() {
586 let value = Value::from_serializable(&Example {
587 name: "example",
588 optional: None,
589 count: u64::MAX,
590 });
591 assert!(value.is_ok(), "example should serialize");
592 let Ok(value) = value else {
593 return;
594 };
595
596 assert_eq!(
597 value,
598 Value::Object(vec![
599 ("count".into(), Value::Unsigned(u64::MAX)),
600 ("name".into(), Value::String("example".into())),
601 ("optional".into(), Value::Null),
602 ])
603 );
604 }
605
606 #[test]
607 fn registry_appends_constants_array_index_and_finder() {
608 let module = TypegenModule::new("registry").declare_registry(
609 Registry::for_registered::<String>(RegistryNames::new(
610 "allEntries",
611 "entriesByKey",
612 "findEntry",
613 ))
614 .entry(RegistryEntry::keyed(
615 "FIRST",
616 "first",
617 Value::object([("value", "example".into())]),
618 )),
619 );
620
621 assert_eq!(module.declarations.len(), 4);
622 assert!(matches!(
623 module.declarations.get(2),
624 Some(Declaration::KeyedIndex { name, entries, .. })
625 if name == "entriesByKey"
626 && entries == &vec![(Value::string("first"), "FIRST".into())]
627 ));
628 assert!(matches!(
629 module.declarations.get(3),
630 Some(Declaration::Find { name, index, .. })
631 if name == "findEntry" && index == "entriesByKey"
632 ));
633 }
634}