1use std::{borrow::Cow, cmp::Ordering, fmt::Display, ops::Deref};
2
3use heck::{AsKebabCase, AsPascalCase, AsSnekCase};
4use itertools::Itertools;
5use ploidy_core::{
6 codegen::{
7 UniqueNames,
8 unique::{UniqueNamesScope, WordSegments},
9 },
10 ir::{
11 ExtendableView, InlineIrTypePathSegment, InlineIrTypeView, IrStructFieldName,
12 IrStructFieldNameHint, IrUntaggedVariantNameHint, PrimitiveIrType, SchemaIrTypeView,
13 },
14};
15use proc_macro2::{Ident, Span, TokenStream};
16use quote::{ToTokens, TokenStreamExt};
17use ref_cast::{RefCastCustom, ref_cast_custom};
18
19const KEYWORDS: &[&str] = &["crate", "self", "super", "Self"];
21
22#[derive(Clone, Copy, Debug)]
33pub enum CodegenTypeName<'a> {
34 Schema(&'a SchemaIrTypeView<'a>),
35 Inline(&'a InlineIrTypeView<'a>),
36}
37
38impl<'a> CodegenTypeName<'a> {
39 #[inline]
40 pub fn into_module_name(self) -> CodegenModuleName<'a> {
41 CodegenModuleName(self)
42 }
43
44 #[inline]
45 pub fn into_sort_key(self) -> CodegenTypeNameSortKey<'a> {
46 CodegenTypeNameSortKey(self)
47 }
48}
49
50impl ToTokens for CodegenTypeName<'_> {
51 fn to_tokens(&self, tokens: &mut TokenStream) {
52 match self {
53 Self::Schema(view) => {
54 let ident = view.extensions().get::<CodegenIdent>().unwrap();
55 CodegenIdentUsage::Type(&ident).to_tokens(tokens);
56 }
57 Self::Inline(view) => {
58 let ident = CodegenIdent::from_segments(&view.path().segments);
59 CodegenIdentUsage::Type(&ident).to_tokens(tokens);
60 }
61 }
62 }
63}
64
65#[derive(Clone, Copy, Debug)]
71pub struct CodegenModuleName<'a>(CodegenTypeName<'a>);
72
73impl<'a> CodegenModuleName<'a> {
74 #[inline]
75 pub fn into_type_name(self) -> CodegenTypeName<'a> {
76 self.0
77 }
78
79 pub fn display(&self) -> impl Display {
85 struct DisplayModuleName<'a>(CodegenTypeName<'a>);
86 impl Display for DisplayModuleName<'_> {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 match self.0 {
89 CodegenTypeName::Schema(view) => {
90 let ident = view.extensions().get::<CodegenIdent>().unwrap();
91 write!(f, "{}", CodegenIdentUsage::Module(&ident).display())
92 }
93 CodegenTypeName::Inline(view) => {
94 let ident = CodegenIdent::from_segments(&view.path().segments);
95 write!(f, "{}", CodegenIdentUsage::Module(&ident).display())
96 }
97 }
98 }
99 }
100 DisplayModuleName(self.0)
101 }
102}
103
104impl ToTokens for CodegenModuleName<'_> {
105 fn to_tokens(&self, tokens: &mut TokenStream) {
106 match self.0 {
107 CodegenTypeName::Schema(view) => {
108 let ident = view.extensions().get::<CodegenIdent>().unwrap();
109 CodegenIdentUsage::Module(&ident).to_tokens(tokens);
110 }
111 CodegenTypeName::Inline(view) => {
112 let ident = CodegenIdent::from_segments(&view.path().segments);
113 CodegenIdentUsage::Module(&ident).to_tokens(tokens);
114 }
115 }
116 }
117}
118
119#[derive(Clone, Copy, Debug)]
125pub struct CodegenTypeNameSortKey<'a>(CodegenTypeName<'a>);
126
127impl<'a> CodegenTypeNameSortKey<'a> {
128 #[inline]
129 pub fn for_schema(view: &'a SchemaIrTypeView<'a>) -> Self {
130 Self(CodegenTypeName::Schema(view))
131 }
132
133 #[inline]
134 pub fn for_inline(view: &'a InlineIrTypeView<'a>) -> Self {
135 Self(CodegenTypeName::Inline(view))
136 }
137
138 #[inline]
139 pub fn into_name(self) -> CodegenTypeName<'a> {
140 self.0
141 }
142}
143
144impl Eq for CodegenTypeNameSortKey<'_> {}
145
146impl Ord for CodegenTypeNameSortKey<'_> {
147 fn cmp(&self, other: &Self) -> Ordering {
148 match (&self.0, &other.0) {
149 (CodegenTypeName::Schema(a), CodegenTypeName::Schema(b)) => a.name().cmp(b.name()),
150 (CodegenTypeName::Inline(a), CodegenTypeName::Inline(b)) => a.path().cmp(b.path()),
151 (CodegenTypeName::Schema(_), CodegenTypeName::Inline(_)) => Ordering::Less,
152 (CodegenTypeName::Inline(_), CodegenTypeName::Schema(_)) => Ordering::Greater,
153 }
154 }
155}
156
157impl PartialEq for CodegenTypeNameSortKey<'_> {
158 fn eq(&self, other: &Self) -> bool {
159 self.cmp(other).is_eq()
160 }
161}
162
163impl PartialOrd for CodegenTypeNameSortKey<'_> {
164 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
165 Some(self.cmp(other))
166 }
167}
168
169#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
172pub struct CodegenIdent(String);
173
174impl CodegenIdent {
175 pub fn new(s: &str) -> Self {
177 let s = clean(s);
178 if KEYWORDS.contains(&s.as_str()) {
179 Self(format!("_{s}"))
180 } else {
181 Self(s)
182 }
183 }
184
185 pub fn from_segments(segments: &[InlineIrTypePathSegment<'_>]) -> Self {
187 Self(format!(
188 "{}",
189 segments
190 .iter()
191 .map(CodegenTypePathSegment)
192 .format_with("", |segment, f| f(&segment.display()))
193 ))
194 }
195}
196
197impl AsRef<CodegenIdentRef> for CodegenIdent {
198 fn as_ref(&self) -> &CodegenIdentRef {
199 self
200 }
201}
202
203impl Deref for CodegenIdent {
204 type Target = CodegenIdentRef;
205
206 fn deref(&self) -> &Self::Target {
207 CodegenIdentRef::new(&self.0)
208 }
209}
210
211#[derive(Debug, Eq, Ord, PartialEq, PartialOrd, RefCastCustom)]
213#[repr(transparent)]
214pub struct CodegenIdentRef(str);
215
216impl CodegenIdentRef {
217 #[ref_cast_custom]
218 fn new(s: &str) -> &Self;
219}
220
221#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
227pub enum CargoFeature {
228 #[default]
229 Default,
230 Named(CodegenIdent),
231}
232
233impl CargoFeature {
234 #[inline]
235 pub fn from_name(name: &str) -> Self {
236 match name {
237 "default" => Self::Default,
239
240 name => Self::Named(CodegenIdent::new(name)),
244 }
245 }
246
247 #[inline]
248 pub fn as_ident(&self) -> &CodegenIdentRef {
249 match self {
250 Self::Named(name) => name,
251 Self::Default => CodegenIdentRef::new("default"),
252 }
253 }
254
255 #[inline]
256 pub fn display(&self) -> impl Display {
257 match self {
258 Self::Named(name) => AsKebabCase(name.0.as_str()),
259 Self::Default => AsKebabCase("default"),
260 }
261 }
262}
263
264#[derive(Clone, Copy, Debug)]
276pub enum CodegenIdentUsage<'a> {
277 Module(&'a CodegenIdentRef),
278 Type(&'a CodegenIdentRef),
279 Field(&'a CodegenIdentRef),
280 Variant(&'a CodegenIdentRef),
281 Param(&'a CodegenIdentRef),
282 Method(&'a CodegenIdentRef),
283}
284
285impl CodegenIdentUsage<'_> {
286 pub fn display(self) -> impl Display {
292 struct DisplayUsage<'a>(CodegenIdentUsage<'a>);
293 impl Display for DisplayUsage<'_> {
294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
295 use CodegenIdentUsage::*;
296 match self.0 {
297 Module(name) | Field(name) | Param(name) | Method(name) => {
298 if name.0.starts_with(unicode_ident::is_xid_start) {
299 write!(f, "{}", AsSnekCase(&name.0))
300 } else {
301 write!(f, "_{}", AsSnekCase(&name.0))
305 }
306 }
307 Type(name) | Variant(name) => {
308 if name.0.starts_with(unicode_ident::is_xid_start) {
309 write!(f, "{}", AsPascalCase(&name.0))
310 } else {
311 write!(f, "_{}", AsPascalCase(&name.0))
312 }
313 }
314 }
315 }
316 }
317 DisplayUsage(self)
318 }
319}
320
321impl ToTokens for CodegenIdentUsage<'_> {
322 fn to_tokens(&self, tokens: &mut TokenStream) {
323 let s = self.display().to_string();
324 let ident = syn::parse_str(&s).unwrap_or_else(|_| Ident::new_raw(&s, Span::call_site()));
325 tokens.append(ident);
326 }
327}
328
329#[derive(Debug)]
331pub struct CodegenIdentScope<'a>(UniqueNamesScope<'a>);
332
333impl<'a> CodegenIdentScope<'a> {
334 pub fn new(arena: &'a UniqueNames) -> Self {
336 Self::with_reserved(arena, &[])
337 }
338
339 pub fn with_reserved(arena: &'a UniqueNames, reserved: &[&str]) -> Self {
342 Self(arena.scope_with_reserved(itertools::chain!(
343 reserved.iter().copied(),
344 KEYWORDS.iter().copied(),
345 std::iter::once("")
346 )))
347 }
348
349 pub fn uniquify(&mut self, name: &str) -> CodegenIdent {
352 CodegenIdent(self.0.uniquify(&clean(name)).into_owned())
353 }
354}
355
356#[derive(Clone, Copy, Debug)]
357pub struct CodegenUntaggedVariantName(pub IrUntaggedVariantNameHint);
358
359impl ToTokens for CodegenUntaggedVariantName {
360 fn to_tokens(&self, tokens: &mut TokenStream) {
361 use IrUntaggedVariantNameHint::*;
362 let s = match self.0 {
363 Primitive(PrimitiveIrType::String) => "String".into(),
364 Primitive(PrimitiveIrType::I8) => "I8".into(),
365 Primitive(PrimitiveIrType::U8) => "U8".into(),
366 Primitive(PrimitiveIrType::I16) => "I16".into(),
367 Primitive(PrimitiveIrType::U16) => "U16".into(),
368 Primitive(PrimitiveIrType::I32) => "I32".into(),
369 Primitive(PrimitiveIrType::U32) => "U32".into(),
370 Primitive(PrimitiveIrType::I64) => "I64".into(),
371 Primitive(PrimitiveIrType::U64) => "U64".into(),
372 Primitive(PrimitiveIrType::F32) => "F32".into(),
373 Primitive(PrimitiveIrType::F64) => "F64".into(),
374 Primitive(PrimitiveIrType::Bool) => "Bool".into(),
375 Primitive(PrimitiveIrType::DateTime) => "DateTime".into(),
376 Primitive(PrimitiveIrType::UnixTime) => "UnixTime".into(),
377 Primitive(PrimitiveIrType::Date) => "Date".into(),
378 Primitive(PrimitiveIrType::Url) => "Url".into(),
379 Primitive(PrimitiveIrType::Uuid) => "Uuid".into(),
380 Primitive(PrimitiveIrType::Bytes) => "Bytes".into(),
381 Primitive(PrimitiveIrType::Binary) => "Binary".into(),
382 Array => "Array".into(),
383 Map => "Map".into(),
384 Index(index) => Cow::Owned(format!("V{index}")),
385 };
386 tokens.append(Ident::new(&s, Span::call_site()));
387 }
388}
389
390#[derive(Clone, Copy, Debug)]
391pub struct CodegenStructFieldName(pub IrStructFieldNameHint);
392
393impl ToTokens for CodegenStructFieldName {
394 fn to_tokens(&self, tokens: &mut TokenStream) {
395 match self.0 {
396 IrStructFieldNameHint::Index(index) => {
397 CodegenIdentUsage::Field(&CodegenIdent(format!("variant_{index}")))
398 .to_tokens(tokens)
399 }
400 }
401 }
402}
403
404#[derive(Clone, Copy, Debug)]
405pub struct CodegenTypePathSegment<'a>(&'a InlineIrTypePathSegment<'a>);
406
407impl<'a> CodegenTypePathSegment<'a> {
408 pub fn display(&self) -> impl Display {
409 struct DisplaySegment<'a>(&'a InlineIrTypePathSegment<'a>);
410 impl Display for DisplaySegment<'_> {
411 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412 use InlineIrTypePathSegment::*;
413 match self.0 {
414 Operation(name) => write!(f, "{}", AsPascalCase(clean(name))),
417 Parameter(name) => write!(f, "{}", AsPascalCase(clean(name))),
418 Request => f.write_str("Request"),
419 Response => f.write_str("Response"),
420 Field(IrStructFieldName::Name(name)) => {
421 write!(f, "{}", AsPascalCase(clean(name)))
422 }
423 Field(IrStructFieldName::Hint(IrStructFieldNameHint::Index(index))) => {
424 write!(f, "Variant{index}")
425 }
426 MapValue => f.write_str("Value"),
427 ArrayItem => f.write_str("Item"),
428 Variant(index) => write!(f, "V{index}"),
429 }
430 }
431 }
432 DisplaySegment(self.0)
433 }
434}
435
436fn clean(s: &str) -> String {
448 WordSegments::new(s)
449 .flat_map(|s| s.split(|c| !unicode_ident::is_xid_continue(c)))
450 .join("_")
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456
457 use pretty_assertions::assert_eq;
458 use syn::parse_quote;
459
460 #[test]
463 fn test_feature_from_name() {
464 let feature = CargoFeature::from_name("customers");
465 assert_eq!(feature.display().to_string(), "customers");
466 }
467
468 #[test]
469 fn test_feature_default() {
470 let feature = CargoFeature::Default;
471 assert_eq!(feature.display().to_string(), "default");
472
473 let feature = CargoFeature::from_name("default");
474 assert_eq!(feature, CargoFeature::Default);
475 }
476
477 #[test]
478 fn test_features_from_multiple_words() {
479 let feature = CargoFeature::from_name("foo_bar");
480 assert_eq!(feature.display().to_string(), "foo-bar");
481
482 let feature = CargoFeature::from_name("foo.bar");
483 assert_eq!(feature.display().to_string(), "foo-bar");
484
485 let feature = CargoFeature::from_name("fooBar");
486 assert_eq!(feature.display().to_string(), "foo-bar");
487
488 let feature = CargoFeature::from_name("FooBar");
489 assert_eq!(feature.display().to_string(), "foo-bar");
490 }
491
492 #[test]
495 fn test_codegen_ident_type() {
496 let ident = CodegenIdent::new("pet_store");
497 let usage = CodegenIdentUsage::Type(&ident);
498 let actual: syn::Ident = parse_quote!(#usage);
499 let expected: syn::Ident = parse_quote!(PetStore);
500 assert_eq!(actual, expected);
501 }
502
503 #[test]
504 fn test_codegen_ident_field() {
505 let ident = CodegenIdent::new("petStore");
506 let usage = CodegenIdentUsage::Field(&ident);
507 let actual: syn::Ident = parse_quote!(#usage);
508 let expected: syn::Ident = parse_quote!(pet_store);
509 assert_eq!(actual, expected);
510 }
511
512 #[test]
513 fn test_codegen_ident_module() {
514 let ident = CodegenIdent::new("MyModule");
515 let usage = CodegenIdentUsage::Module(&ident);
516 let actual: syn::Ident = parse_quote!(#usage);
517 let expected: syn::Ident = parse_quote!(my_module);
518 assert_eq!(actual, expected);
519 }
520
521 #[test]
522 fn test_codegen_ident_variant() {
523 let ident = CodegenIdent::new("http_error");
524 let usage = CodegenIdentUsage::Variant(&ident);
525 let actual: syn::Ident = parse_quote!(#usage);
526 let expected: syn::Ident = parse_quote!(HttpError);
527 assert_eq!(actual, expected);
528 }
529
530 #[test]
531 fn test_codegen_ident_param() {
532 let ident = CodegenIdent::new("userId");
533 let usage = CodegenIdentUsage::Param(&ident);
534 let actual: syn::Ident = parse_quote!(#usage);
535 let expected: syn::Ident = parse_quote!(user_id);
536 assert_eq!(actual, expected);
537 }
538
539 #[test]
540 fn test_codegen_ident_method() {
541 let ident = CodegenIdent::new("getUserById");
542 let usage = CodegenIdentUsage::Method(&ident);
543 let actual: syn::Ident = parse_quote!(#usage);
544 let expected: syn::Ident = parse_quote!(get_user_by_id);
545 assert_eq!(actual, expected);
546 }
547
548 #[test]
551 fn test_codegen_ident_handles_rust_keywords() {
552 let ident = CodegenIdent::new("type");
553 let usage = CodegenIdentUsage::Field(&ident);
554 let actual: syn::Ident = parse_quote!(#usage);
555 let expected: syn::Ident = parse_quote!(r#type);
556 assert_eq!(actual, expected);
557 }
558
559 #[test]
560 fn test_codegen_ident_handles_invalid_start_chars() {
561 let ident = CodegenIdent::new("123foo");
562 let usage = CodegenIdentUsage::Field(&ident);
563 let actual: syn::Ident = parse_quote!(#usage);
564 let expected: syn::Ident = parse_quote!(_123_foo);
565 assert_eq!(actual, expected);
566 }
567
568 #[test]
569 fn test_codegen_ident_handles_special_chars() {
570 let ident = CodegenIdent::new("foo-bar-baz");
571 let usage = CodegenIdentUsage::Field(&ident);
572 let actual: syn::Ident = parse_quote!(#usage);
573 let expected: syn::Ident = parse_quote!(foo_bar_baz);
574 assert_eq!(actual, expected);
575 }
576
577 #[test]
578 fn test_codegen_ident_handles_number_prefix() {
579 let ident = CodegenIdent::new("1099KStatus");
580
581 let usage = CodegenIdentUsage::Field(&ident);
582 let actual: syn::Ident = parse_quote!(#usage);
583 let expected: syn::Ident = parse_quote!(_1099_k_status);
584 assert_eq!(actual, expected);
585
586 let usage = CodegenIdentUsage::Type(&ident);
587 let actual: syn::Ident = parse_quote!(#usage);
588 let expected: syn::Ident = parse_quote!(_1099KStatus);
589 assert_eq!(actual, expected);
590 }
591
592 #[test]
595 fn test_untagged_variant_name_string() {
596 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(
597 PrimitiveIrType::String,
598 ));
599 let actual: syn::Ident = parse_quote!(#variant_name);
600 let expected: syn::Ident = parse_quote!(String);
601 assert_eq!(actual, expected);
602 }
603
604 #[test]
605 fn test_untagged_variant_name_i32() {
606 let variant_name =
607 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::I32));
608 let actual: syn::Ident = parse_quote!(#variant_name);
609 let expected: syn::Ident = parse_quote!(I32);
610 assert_eq!(actual, expected);
611 }
612
613 #[test]
614 fn test_untagged_variant_name_i64() {
615 let variant_name =
616 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::I64));
617 let actual: syn::Ident = parse_quote!(#variant_name);
618 let expected: syn::Ident = parse_quote!(I64);
619 assert_eq!(actual, expected);
620 }
621
622 #[test]
623 fn test_untagged_variant_name_f32() {
624 let variant_name =
625 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::F32));
626 let actual: syn::Ident = parse_quote!(#variant_name);
627 let expected: syn::Ident = parse_quote!(F32);
628 assert_eq!(actual, expected);
629 }
630
631 #[test]
632 fn test_untagged_variant_name_f64() {
633 let variant_name =
634 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::F64));
635 let actual: syn::Ident = parse_quote!(#variant_name);
636 let expected: syn::Ident = parse_quote!(F64);
637 assert_eq!(actual, expected);
638 }
639
640 #[test]
641 fn test_untagged_variant_name_bool() {
642 let variant_name =
643 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::Bool));
644 let actual: syn::Ident = parse_quote!(#variant_name);
645 let expected: syn::Ident = parse_quote!(Bool);
646 assert_eq!(actual, expected);
647 }
648
649 #[test]
650 fn test_untagged_variant_name_datetime() {
651 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(
652 PrimitiveIrType::DateTime,
653 ));
654 let actual: syn::Ident = parse_quote!(#variant_name);
655 let expected: syn::Ident = parse_quote!(DateTime);
656 assert_eq!(actual, expected);
657 }
658
659 #[test]
660 fn test_untagged_variant_name_date() {
661 let variant_name =
662 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::Date));
663 let actual: syn::Ident = parse_quote!(#variant_name);
664 let expected: syn::Ident = parse_quote!(Date);
665 assert_eq!(actual, expected);
666 }
667
668 #[test]
669 fn test_untagged_variant_name_url() {
670 let variant_name =
671 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::Url));
672 let actual: syn::Ident = parse_quote!(#variant_name);
673 let expected: syn::Ident = parse_quote!(Url);
674 assert_eq!(actual, expected);
675 }
676
677 #[test]
678 fn test_untagged_variant_name_uuid() {
679 let variant_name =
680 CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(PrimitiveIrType::Uuid));
681 let actual: syn::Ident = parse_quote!(#variant_name);
682 let expected: syn::Ident = parse_quote!(Uuid);
683 assert_eq!(actual, expected);
684 }
685
686 #[test]
687 fn test_untagged_variant_name_bytes() {
688 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Primitive(
689 PrimitiveIrType::Bytes,
690 ));
691 let actual: syn::Ident = parse_quote!(#variant_name);
692 let expected: syn::Ident = parse_quote!(Bytes);
693 assert_eq!(actual, expected);
694 }
695
696 #[test]
697 fn test_untagged_variant_name_index() {
698 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Index(0));
699 let actual: syn::Ident = parse_quote!(#variant_name);
700 let expected: syn::Ident = parse_quote!(V0);
701 assert_eq!(actual, expected);
702
703 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Index(42));
704 let actual: syn::Ident = parse_quote!(#variant_name);
705 let expected: syn::Ident = parse_quote!(V42);
706 assert_eq!(actual, expected);
707 }
708
709 #[test]
710 fn test_untagged_variant_name_array() {
711 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Array);
712 let actual: syn::Ident = parse_quote!(#variant_name);
713 let expected: syn::Ident = parse_quote!(Array);
714 assert_eq!(actual, expected);
715 }
716
717 #[test]
718 fn test_untagged_variant_name_map() {
719 let variant_name = CodegenUntaggedVariantName(IrUntaggedVariantNameHint::Map);
720 let actual: syn::Ident = parse_quote!(#variant_name);
721 let expected: syn::Ident = parse_quote!(Map);
722 assert_eq!(actual, expected);
723 }
724
725 #[test]
728 fn test_struct_field_name_index() {
729 let field_name = CodegenStructFieldName(IrStructFieldNameHint::Index(0));
730 let actual: syn::Ident = parse_quote!(#field_name);
731 let expected: syn::Ident = parse_quote!(variant_0);
732 assert_eq!(actual, expected);
733
734 let field_name = CodegenStructFieldName(IrStructFieldNameHint::Index(5));
735 let actual: syn::Ident = parse_quote!(#field_name);
736 let expected: syn::Ident = parse_quote!(variant_5);
737 assert_eq!(actual, expected);
738 }
739
740 #[test]
743 fn test_clean() {
744 assert_eq!(clean("foo-bar"), "foo_bar");
745 assert_eq!(clean("foo.bar"), "foo_bar");
746 assert_eq!(clean("foo bar"), "foo_bar");
747 assert_eq!(clean("foo@bar"), "foo_bar");
748 assert_eq!(clean("foo#bar"), "foo_bar");
749 assert_eq!(clean("foo!bar"), "foo_bar");
750
751 assert_eq!(clean("foo_bar"), "foo_bar");
752 assert_eq!(clean("FooBar"), "Foo_Bar");
753 assert_eq!(clean("foo123"), "foo123");
754 assert_eq!(clean("_foo"), "foo");
755
756 assert_eq!(clean("_foo"), "foo");
757 assert_eq!(clean("__foo"), "foo");
758
759 assert_eq!(clean("123foo"), "123_foo");
761 assert_eq!(clean("9bar"), "9_bar");
762
763 assert_eq!(clean("café"), "café");
766 assert_eq!(clean("foo™bar"), "foo_bar");
767
768 assert_eq!(clean("foo---bar"), "foo_bar");
770 assert_eq!(clean("foo...bar"), "foo_bar");
771 }
772
773 #[test]
776 fn test_codegen_ident_scope_handles_empty() {
777 let unique = UniqueNames::new();
778 let mut scope = CodegenIdentScope::new(&unique);
779 let ident = scope.uniquify("");
780
781 let usage = CodegenIdentUsage::Field(&ident);
782 let actual: syn::Ident = parse_quote!(#usage);
783 let expected: syn::Ident = parse_quote!(_2);
784 assert_eq!(actual, expected);
785
786 let usage = CodegenIdentUsage::Type(&ident);
787 let actual: syn::Ident = parse_quote!(#usage);
788 let expected: syn::Ident = parse_quote!(_2);
789 assert_eq!(actual, expected);
790 }
791}