1use std::{borrow::Cow, fmt, sync::Arc};
42
43use specta::{
44 Type, Types,
45 datatype::{DataType, Fields, NamedReferenceType, Primitive, Reference},
46};
47
48use crate::{
49 define,
50 primitives::{escape_typescript_string_literal, is_identifier},
51};
52
53#[derive(Clone)]
83#[non_exhaustive]
84pub struct Transform(
85 Option<Arc<dyn Fn(&str) -> String + Send + Sync>>,
91);
92
93impl fmt::Debug for Transform {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match &self.0 {
96 Some(r) => write!(f, "{r:p}"),
97 None => write!(f, "<none>"),
98 }
99 }
100}
101
102impl Transform {
103 pub fn new(runtime: impl Fn(&str) -> String + Send + Sync + 'static) -> Self {
114 Self(Some(Arc::new(runtime)))
115 }
116
117 pub fn identity() -> Self {
129 Self(None)
130 }
131
132 fn apply(&self, ident: &str) -> String {
133 match &self.0 {
134 Some(runtime) => runtime(ident),
135 None => ident.to_owned(),
136 }
137 }
138}
139
140#[derive(Clone)]
141pub(crate) struct DataTypeFn(Arc<dyn Fn(DataType) -> DataType + Send + Sync>);
142
143impl DataTypeFn {
144 pub(crate) fn new(f: impl Fn(DataType) -> DataType + Send + Sync + 'static) -> Self {
145 Self(Arc::new(f))
146 }
147}
148
149impl fmt::Debug for DataTypeFn {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 f.debug_tuple("DataTypeFn")
152 .field(&format!("{:p}", self.0))
153 .finish()
154 }
155}
156
157#[derive(Debug, Clone)]
159#[non_exhaustive]
160pub struct Rule {
161 pub name: Cow<'static, str>,
163 pub module_path: Cow<'static, str>,
165 pub(crate) data_type: DataTypeFn,
169 pub(crate) serialize: Option<Transform>,
172 pub(crate) deserialize: Option<Transform>,
175}
176
177#[derive(Debug, Clone)]
184pub struct Configuration {
185 rules: Vec<Rule>,
186 lossless_bigint: bool,
187 lossless_floats: bool,
188}
189
190impl Default for Configuration {
191 fn default() -> Self {
192 Self {
193 rules: vec![
194 Rule {
196 name: "Bytes".into(),
197 module_path: "bytes".into(),
198 data_type: DataTypeFn::new(|_| define("Uint8Array").into()),
199 serialize: Some(Transform::new(|i| format!("[...{i}]"))),
200 deserialize: Some(Transform::new(|i| format!("new Uint8Array({i})"))),
201 },
202 Rule {
203 name: "BytesMut".into(),
204 module_path: "bytes".into(),
205 data_type: DataTypeFn::new(|_| define("Uint8Array").into()),
206 serialize: Some(Transform::new(|i| format!("[...{i}]"))),
207 deserialize: Some(Transform::new(|i| format!("new Uint8Array({i})"))),
208 },
209 Rule {
211 name: "Url".into(),
212 module_path: "url".into(),
213 data_type: DataTypeFn::new(|_| define("URL").into()),
214 serialize: None,
215 deserialize: Some(Transform::new(|i| format!("new URL({i})"))),
216 },
217 Rule {
219 name: "DateTime".into(),
220 module_path: "chrono".into(),
221 data_type: DataTypeFn::new(|_| define("Date").into()),
222 serialize: None,
223 deserialize: Some(Transform::new(|i| format!("new Date({i})"))),
224 },
225 Rule {
226 name: "NaiveDate".into(),
227 module_path: "chrono".into(),
228 data_type: DataTypeFn::new(|_| define("Date").into()),
229 serialize: Some(Transform::new(|i| {
230 format!("{i}.toISOString().slice(0, 10)")
231 })),
232 deserialize: Some(Transform::new(|i| format!("new Date({i})"))),
233 },
234 Rule {
235 name: "Timestamp".into(),
236 module_path: "jiff".into(),
237 data_type: DataTypeFn::new(|_| define("Date").into()),
238 serialize: Some(Transform::new(|i| format!("{i}.toISOString()"))),
239 deserialize: Some(Transform::new(|i| format!("new Date({i})"))),
240 },
241 Rule {
242 name: "Date".into(),
243 module_path: "jiff::civil".into(),
244 data_type: DataTypeFn::new(|_| define("Date").into()),
245 serialize: Some(Transform::new(|i| {
246 format!("{i}.toISOString().slice(0, 10)")
247 })),
248 deserialize: Some(Transform::new(|i| format!("new Date({i})"))),
249 },
250 ],
252 lossless_bigint: false,
253 lossless_floats: false,
254 }
255 }
256}
257
258impl Configuration {
259 pub fn empty() -> Self {
264 Self {
265 rules: Default::default(),
266 lossless_bigint: false,
267 lossless_floats: false,
268 }
269 }
270
271 pub fn rules_mut(&mut self) -> &mut Vec<Rule> {
275 &mut self.rules
276 }
277
278 pub fn define<T: Type>(
304 mut self,
305 dt: impl Fn(DataType) -> DataType + Send + Sync + 'static,
306 serialize: Option<Transform>,
307 deserialize: Option<Transform>,
308 ) -> Self {
309 let mut types = Types::default();
310 let ndt = match T::definition(&mut types) {
311 DataType::Reference(Reference::Named(r)) => types.get(&r),
312 _ => None,
313 };
314 if let Some(ndt) = ndt {
315 self.rules.push(Rule {
316 name: ndt.name.clone(),
317 module_path: ndt.module_path.clone(),
318 data_type: DataTypeFn(Arc::new(dt)),
319 serialize,
320 deserialize,
321 });
322 }
323
324 self
325 }
326
327 pub fn enable_lossless_bigints(mut self) -> Self {
336 if !self.lossless_bigint {
337 self.lossless_bigint = true;
338 }
339
340 self
341 }
342
343 pub fn enable_lossless_floats(mut self) -> Self {
350 if !self.lossless_floats {
351 self.lossless_floats = true;
352 }
353
354 self
355 }
356
357 pub fn apply_types<'a>(&self, types: &'a Types) -> Cow<'a, Types> {
365 let mut types = Cow::Borrowed(types);
366
367 if self.has_builtin_remaps() {
368 types = Cow::Owned(types.into_owned().map(|mut ndt| {
369 let remap_bigint = if ndt.name.ends_with("_Serialize") {
370 serialize_bigint
371 } else {
372 deserialize_bigint
373 };
374
375 ndt.generics.to_mut().iter_mut().for_each(|generic| {
376 if let Some(dt) = &mut generic.default {
377 apply_builtin_remaps(
378 dt,
379 remap_bigint,
380 self.lossless_bigint,
381 self.lossless_floats,
382 );
383 }
384 });
385 if let Some(dt) = &mut ndt.ty {
386 apply_builtin_remaps(
387 dt,
388 remap_bigint,
389 self.lossless_bigint,
390 self.lossless_floats,
391 );
392 }
393
394 ndt
395 }));
396 }
397
398 if !self.rules.is_empty() {
399 let source = types.into_owned();
400 let lookup = source.clone();
401 types = Cow::Owned(source.map(|mut ndt| {
402 if let Some(dt) = &mut ndt.ty {
403 self.apply_rules_to_dt(&lookup, dt);
404 }
405
406 if let Some(rule) = self
407 .rules
408 .iter()
409 .find(|r| r.name == ndt.name && r.module_path == ndt.module_path)
410 && let Some(dt) = ndt.ty.take()
411 {
412 ndt.ty = Some((rule.data_type.0)(dt));
413 }
414
415 ndt
416 }));
417 }
418
419 types
420 }
421
422 fn apply_rules_to_dt(&self, types: &Types, dt: &mut DataType) {
423 if let DataType::Reference(Reference::Named(reference)) = dt
424 && let Some(rule) = self.rule_for_reference(types, reference)
425 {
426 *dt = (rule.data_type.0)(Self::reference_source_dt(types, reference));
427 return;
428 }
429
430 match dt {
431 DataType::Primitive(_) | DataType::Generic(_) => {}
432 DataType::List(list) => self.apply_rules_to_dt(types, &mut list.ty),
433 DataType::Map(map) => {
434 self.apply_rules_to_dt(types, map.key_ty_mut());
435 self.apply_rules_to_dt(types, map.value_ty_mut());
436 }
437 DataType::Struct(s) => self.apply_rules_to_fields(types, &mut s.fields),
438 DataType::Enum(e) => {
439 for (_, variant) in &mut e.variants {
440 self.apply_rules_to_fields(types, &mut variant.fields);
441 }
442 }
443 DataType::Tuple(tuple) => {
444 for dt in &mut tuple.elements {
445 self.apply_rules_to_dt(types, dt);
446 }
447 }
448 DataType::Nullable(dt) => self.apply_rules_to_dt(types, dt),
449 DataType::Intersection(dts) => {
450 for dt in dts {
451 self.apply_rules_to_dt(types, dt);
452 }
453 }
454 DataType::Reference(Reference::Named(reference)) => match &mut reference.inner {
455 NamedReferenceType::Recursive(_) | NamedReferenceType::Reference { .. } => {}
456 NamedReferenceType::Inline { dt, .. } => self.apply_rules_to_dt(types, dt),
457 },
458 DataType::Reference(Reference::Opaque(_)) => {}
459 }
460 }
461
462 fn apply_rules_to_fields(&self, types: &Types, fields: &mut Fields) {
463 match fields {
464 Fields::Unit => {}
465 Fields::Unnamed(fields) => {
466 for field in &mut fields.fields {
467 if let Some(dt) = &mut field.ty {
468 self.apply_rules_to_dt(types, dt);
469 }
470 }
471 }
472 Fields::Named(fields) => {
473 for (_, field) in &mut fields.fields {
474 if let Some(dt) = &mut field.ty {
475 self.apply_rules_to_dt(types, dt);
476 }
477 }
478 }
479 }
480 }
481
482 pub fn apply_serialize(
496 &self,
497 types: &Types,
498 dt: &DataType,
499 js_ident: &str,
500 ) -> Option<(Option<DataType>, String)> {
501 self.apply_inner(
502 |rule| &rule.serialize,
503 serialize_bigint,
504 types,
505 dt,
506 js_ident,
507 &mut Vec::new(),
508 )
509 }
510
511 pub fn apply_deserialize(
516 &self,
517 types: &Types,
518 dt: &DataType,
519 js_ident: &str,
520 ) -> Option<(Option<DataType>, String)> {
521 self.apply_inner(
522 |rule| &rule.deserialize,
523 deserialize_bigint,
524 types,
525 dt,
526 js_ident,
527 &mut Vec::new(),
528 )
529 }
530
531 fn apply_inner(
532 &self,
533 transform_for_rule: fn(&Rule) -> &Option<Transform>,
534 remap_bigint: fn() -> DataType,
535 types: &Types,
536 dt: &DataType,
537 js_ident: &str,
538 stack: &mut Vec<(Cow<'static, str>, Cow<'static, str>)>,
539 ) -> Option<(Option<DataType>, String)> {
540 let result = match dt {
541 DataType::Reference(Reference::Named(r)) => {
542 if let Some(rule) = self.rule_for_reference(types, r) {
543 return Some((
544 Some((rule.data_type.0)(Self::reference_source_dt(types, r))),
545 transform_for_rule(rule).as_ref().map_or_else(
546 || js_ident.to_owned(),
547 |transform| transform.apply(js_ident),
548 ),
549 ));
550 }
551
552 match &r.inner {
553 NamedReferenceType::Inline { dt, .. } => self.apply_inner(
554 transform_for_rule,
555 remap_bigint,
556 types,
557 dt,
558 js_ident,
559 stack,
560 ),
561 NamedReferenceType::Recursive(_) => None,
562 NamedReferenceType::Reference { .. } => {
563 let ndt = types.get(r)?;
564
565 let ty = ndt.ty.as_ref()?;
566 let key = (ndt.name.clone(), ndt.module_path.clone());
567 if stack.contains(&key) {
568 return None;
569 }
570 stack.push(key);
571 let result = self
572 .apply_inner(
573 transform_for_rule,
574 remap_bigint,
575 types,
576 ty,
577 js_ident,
578 stack,
579 )
580 .map(|(_, runtime)| (None, runtime));
581 stack.pop();
582 result
583 }
584 }
585 }
586 DataType::Struct(s) => match &s.fields {
587 Fields::Named(fields) => {
588 let mut ty = s.clone();
589 let mut changed = false;
590 let mut parts = Vec::new();
591
592 for (name, field) in &fields.fields {
593 let Some(field_ty) = &field.ty else { continue };
594 let field_ident = js_property_access(js_ident, name);
595 let Some((next_ty, runtime)) = self.apply_inner(
596 transform_for_rule,
597 remap_bigint,
598 types,
599 field_ty,
600 &field_ident,
601 stack,
602 ) else {
603 continue;
604 };
605
606 if let Some(next_ty) = next_ty
607 && let Fields::Named(fields) = &mut ty.fields
608 && let Some((_, field)) =
609 fields.fields.iter_mut().find(|(n, _)| n == name)
610 {
611 field.ty = Some(next_ty);
612 changed = true;
613 }
614 if runtime != field_ident {
615 parts.push(format!("{}:{runtime}", js_object_key(name)));
616 }
617 }
618
619 if parts.is_empty() {
620 changed.then_some((Some(DataType::Struct(ty)), js_ident.to_owned()))
621 } else {
622 Some((
623 changed.then_some(DataType::Struct(ty)),
624 spread_transform(js_ident, parts),
625 ))
626 }
627 }
628 Fields::Unnamed(fields) => {
629 let mut ty = s.clone();
630 let mut changed = false;
631 let parts = fields
632 .fields
633 .iter()
634 .enumerate()
635 .filter_map(|(idx, field)| {
636 let field_ty = field.ty.as_ref()?;
637 let field_ident = format!("{js_ident}[{idx}]");
638 let (next_ty, runtime) = self.apply_inner(
639 transform_for_rule,
640 remap_bigint,
641 types,
642 field_ty,
643 &field_ident,
644 stack,
645 )?;
646
647 if let Some(next_ty) = next_ty
648 && let Fields::Unnamed(fields) = &mut ty.fields
649 {
650 fields.fields[idx].ty = Some(next_ty);
651 changed = true;
652 }
653
654 (runtime != field_ident).then_some((idx, runtime))
655 })
656 .collect::<Vec<_>>();
657
658 if parts.is_empty() {
659 changed.then_some((Some(DataType::Struct(ty)), js_ident.to_owned()))
660 } else {
661 Some((
662 changed.then_some(DataType::Struct(ty)),
663 array_transform(js_ident, fields.fields.len(), parts),
664 ))
665 }
666 }
667 Fields::Unit => None,
668 },
669 DataType::Tuple(tuple) => {
670 let mut ty = tuple.clone();
671 let mut changed = false;
672 let parts = tuple
673 .elements
674 .iter()
675 .enumerate()
676 .filter_map(|(idx, element)| {
677 let ident = format!("{js_ident}[{idx}]");
678 let (next_ty, runtime) = self.apply_inner(
679 transform_for_rule,
680 remap_bigint,
681 types,
682 element,
683 &ident,
684 stack,
685 )?;
686 if let Some(next_ty) = next_ty {
687 ty.elements[idx] = next_ty;
688 changed = true;
689 }
690 (runtime != ident).then_some((idx, runtime))
691 })
692 .collect::<Vec<_>>();
693
694 if parts.is_empty() {
695 changed.then_some((Some(DataType::Tuple(ty)), js_ident.to_owned()))
696 } else {
697 Some((
698 changed.then_some(DataType::Tuple(ty)),
699 array_transform(js_ident, tuple.elements.len(), parts),
700 ))
701 }
702 }
703 DataType::Map(map) => {
704 let item = "v";
705 let (next_ty, runtime) = self.apply_inner(
706 transform_for_rule,
707 remap_bigint,
708 types,
709 map.value_ty(),
710 item,
711 stack,
712 )?;
713
714 let mut ty = map.clone();
715 let mut changed = false;
716 if let Some(next_ty) = next_ty {
717 ty.set_value_ty(next_ty);
718 changed = true;
719 }
720
721 Some((
722 changed.then_some(DataType::Map(ty)),
723 format!(
724 "Object.fromEntries(Object.entries({js_ident}).map(([k,{item}])=>[k,{runtime}]))"
725 ),
726 ))
727 }
728 DataType::List(list) => {
729 let item = "i";
730 let (next_ty, runtime) = self.apply_inner(
731 transform_for_rule,
732 remap_bigint,
733 types,
734 &list.ty,
735 item,
736 stack,
737 )?;
738 let mut ty = list.clone();
739 let mut changed = false;
740 if let Some(next_ty) = next_ty {
741 ty.ty = Box::new(next_ty);
742 changed = true;
743 }
744 Some((
745 changed.then_some(DataType::List(ty)),
746 format!("{js_ident}.map({item}=>{runtime})"),
747 ))
748 }
749 DataType::Nullable(inner) => {
750 let (next_ty, runtime) = self.apply_inner(
751 transform_for_rule,
752 remap_bigint,
753 types,
754 inner,
755 js_ident,
756 stack,
757 )?;
758 Some((
759 next_ty.map(|dt| DataType::Nullable(Box::new(dt))),
760 format!("{js_ident}==null?{js_ident}:{runtime}"),
761 ))
762 }
763 DataType::Intersection(items) => {
764 let mut ty = items.clone();
765 let mut changed = false;
766 let parts = items
767 .iter()
768 .enumerate()
769 .filter_map(|(idx, item)| {
770 let (next_ty, runtime) = self.apply_inner(
771 transform_for_rule,
772 remap_bigint,
773 types,
774 item,
775 js_ident,
776 stack,
777 )?;
778 if let Some(next_ty) = next_ty {
779 ty[idx] = next_ty;
780 changed = true;
781 }
782 Some(runtime)
783 })
784 .collect::<Vec<_>>();
785
786 match parts.as_slice() {
787 [] => None,
788 [runtime] => Some((
789 changed.then_some(DataType::Intersection(ty)),
790 runtime.clone(),
791 )),
792 _ => Some((
793 changed.then_some(DataType::Intersection(ty)),
794 spread_transform(
795 "",
796 parts.into_iter().map(|p| format!("...{p}")).collect(),
797 ),
798 )),
799 }
800 }
801 DataType::Enum(_)
802 | DataType::Primitive(_)
803 | DataType::Generic(_)
804 | DataType::Reference(Reference::Opaque(_)) => None,
805 };
806
807 self.apply_builtin_remaps(remap_bigint, dt, js_ident, result)
808 }
809
810 fn rule_for_reference<'a>(
811 &'a self,
812 types: &'a Types,
813 reference: &specta::datatype::NamedReference,
814 ) -> Option<&'a Rule> {
815 let ndt = types.get(reference)?;
816 self.rules
817 .iter()
818 .find(|rule| rule.name == ndt.name && rule.module_path == ndt.module_path)
819 }
820
821 fn reference_source_dt(
822 types: &Types,
823 reference: &specta::datatype::NamedReference,
824 ) -> DataType {
825 match &reference.inner {
826 NamedReferenceType::Inline { dt, .. } => (**dt).clone(),
827 NamedReferenceType::Reference { .. } | NamedReferenceType::Recursive(_) => types
828 .get(reference)
829 .and_then(|ndt| ndt.ty.clone())
830 .unwrap_or_else(|| DataType::Reference(Reference::Named(reference.clone()))),
831 }
832 }
833
834 fn has_builtin_remaps(&self) -> bool {
835 self.lossless_bigint || self.lossless_floats
836 }
837
838 fn apply_builtin_remaps(
839 &self,
840 remap_bigint: fn() -> DataType,
841 dt: &DataType,
842 js_ident: &str,
843 result: Option<(Option<DataType>, String)>,
844 ) -> Option<(Option<DataType>, String)> {
845 if !self.has_builtin_remaps() {
846 return result;
847 }
848
849 let source = result
850 .as_ref()
851 .and_then(|(dt, _)| dt.clone())
852 .unwrap_or_else(|| dt.clone());
853 let mut remapped = source.clone();
854 apply_builtin_remaps(
855 &mut remapped,
856 remap_bigint,
857 self.lossless_bigint,
858 self.lossless_floats,
859 );
860
861 let runtime = result
862 .as_ref()
863 .map(|(_, runtime)| runtime.as_str())
864 .unwrap_or(js_ident);
865 let runtime = if is_lossless_bigint_primitive(&source)
866 && self.lossless_bigint
867 && remap_bigint() == deserialize_bigint()
868 {
869 format!("BigInt({runtime})")
870 } else {
871 runtime.to_owned()
872 };
873
874 if remapped == source {
875 result
876 } else {
877 Some((Some(remapped), runtime))
878 }
879 }
880}
881
882fn is_lossless_bigint_primitive(dt: &DataType) -> bool {
883 matches!(
884 dt,
885 DataType::Primitive(
886 Primitive::usize
887 | Primitive::isize
888 | Primitive::u64
889 | Primitive::i64
890 | Primitive::u128
891 | Primitive::i128
892 )
893 )
894}
895
896fn apply_builtin_remaps(
897 dt: &mut DataType,
898 remap_bigint: fn() -> DataType,
899 lossless_bigint: bool,
900 lossless_floats: bool,
901) {
902 if let DataType::Primitive(primitive) = dt
903 && let Some(remapped) = remap_primitive(
904 primitive.clone(),
905 remap_bigint,
906 lossless_bigint,
907 lossless_floats,
908 )
909 {
910 *dt = remapped;
911 return;
912 }
913
914 match dt {
915 DataType::Primitive(_) | DataType::Generic(_) => {}
916 DataType::List(list) => {
917 apply_builtin_remaps(&mut list.ty, remap_bigint, lossless_bigint, lossless_floats)
918 }
919 DataType::Map(map) => {
920 apply_builtin_remaps(
921 map.key_ty_mut(),
922 remap_bigint,
923 lossless_bigint,
924 lossless_floats,
925 );
926 apply_builtin_remaps(
927 map.value_ty_mut(),
928 remap_bigint,
929 lossless_bigint,
930 lossless_floats,
931 );
932 }
933 DataType::Struct(s) => apply_builtin_remaps_to_fields(
934 &mut s.fields,
935 remap_bigint,
936 lossless_bigint,
937 lossless_floats,
938 ),
939 DataType::Enum(e) => {
940 for (_, variant) in &mut e.variants {
941 apply_builtin_remaps_to_fields(
942 &mut variant.fields,
943 remap_bigint,
944 lossless_bigint,
945 lossless_floats,
946 );
947 }
948 }
949 DataType::Tuple(tuple) => {
950 for dt in &mut tuple.elements {
951 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
952 }
953 }
954 DataType::Nullable(dt) => {
955 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
956 }
957 DataType::Intersection(dts) => {
958 for dt in dts {
959 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
960 }
961 }
962 DataType::Reference(reference) => {
963 let Reference::Named(reference) = reference else {
964 return;
965 };
966
967 match &mut reference.inner {
968 NamedReferenceType::Recursive(_) => {}
969 NamedReferenceType::Inline { dt, .. } => {
970 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
971 }
972 NamedReferenceType::Reference { generics, .. } => {
973 for (_, dt) in generics {
974 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
975 }
976 }
977 }
978 }
979 }
980}
981
982fn apply_builtin_remaps_to_fields(
983 fields: &mut Fields,
984 remap_bigint: fn() -> DataType,
985 lossless_bigint: bool,
986 lossless_floats: bool,
987) {
988 match fields {
989 Fields::Unit => {}
990 Fields::Unnamed(fields) => {
991 for field in &mut fields.fields {
992 if let Some(dt) = &mut field.ty {
993 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
994 }
995 }
996 }
997 Fields::Named(fields) => {
998 for (_, field) in &mut fields.fields {
999 if let Some(dt) = &mut field.ty {
1000 apply_builtin_remaps(dt, remap_bigint, lossless_bigint, lossless_floats);
1001 }
1002 }
1003 }
1004 }
1005}
1006
1007fn remap_primitive(
1008 primitive: Primitive,
1009 remap_bigint: fn() -> DataType,
1010 lossless_bigint: bool,
1011 lossless_floats: bool,
1012) -> Option<DataType> {
1013 if lossless_bigint
1014 && matches!(
1015 primitive,
1016 Primitive::usize
1017 | Primitive::isize
1018 | Primitive::u64
1019 | Primitive::i64
1020 | Primitive::u128
1021 | Primitive::i128
1022 )
1023 {
1024 return Some(remap_bigint());
1025 }
1026
1027 if lossless_floats && matches!(primitive, Primitive::f16 | Primitive::f32 | Primitive::f64) {
1028 return Some(Reference::opaque(crate::opaque::Number).into());
1029 }
1030
1031 None
1032}
1033
1034fn serialize_bigint() -> DataType {
1035 crate::define("bigint | number").into()
1036}
1037
1038fn deserialize_bigint() -> DataType {
1039 Reference::opaque(crate::opaque::BigInt).into()
1040}
1041
1042fn spread_transform(js_ident: &str, mut parts: Vec<String>) -> String {
1043 if !js_ident.is_empty() {
1044 parts.insert(0, format!("...{js_ident}"));
1045 }
1046 format!("({{{}}})", parts.join(","))
1047}
1048
1049fn array_transform(js_ident: &str, len: usize, parts: Vec<(usize, String)>) -> String {
1050 let mut items = (0..len)
1051 .map(|idx| format!("{js_ident}[{idx}]"))
1052 .collect::<Vec<_>>();
1053
1054 for (idx, runtime) in parts {
1055 items[idx] = runtime;
1056 }
1057
1058 format!("([{}])", items.join(","))
1059}
1060
1061fn js_property_access(base: &str, name: &str) -> String {
1062 if is_identifier(name) {
1063 format!("{base}.{name}")
1064 } else {
1065 format!("{base}[\"{}\"]", escape_typescript_string_literal(name))
1066 }
1067}
1068
1069fn js_object_key(name: &str) -> Cow<'_, str> {
1070 if is_identifier(name) {
1071 Cow::Borrowed(name)
1072 } else {
1073 Cow::Owned(format!("\"{}\"", escape_typescript_string_literal(name)))
1074 }
1075}