polyxml 0.15.0__tar.gz → 0.17.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. {polyxml-0.15.0 → polyxml-0.17.0}/Cargo.lock +5 -5
  2. {polyxml-0.15.0 → polyxml-0.17.0}/Cargo.toml +1 -1
  3. {polyxml-0.15.0 → polyxml-0.17.0}/PKG-INFO +1 -1
  4. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/cpp/mod.rs +156 -4
  5. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/csharp/mod.rs +80 -1
  6. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/go/mod.rs +67 -8
  7. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/mod.rs +3 -3
  8. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/rust/mod.rs +24 -0
  9. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/typescript/mod.rs +536 -36
  10. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_cpp_codegen.rs +140 -2
  11. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_csharp_codegen.rs +107 -0
  12. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_go_codegen.rs +130 -2
  13. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_rust_codegen.rs +77 -0
  14. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_ts_codegen.rs +243 -2
  15. {polyxml-0.15.0 → polyxml-0.17.0}/pyproject.toml +1 -1
  16. {polyxml-0.15.0 → polyxml-0.17.0}/README.md +0 -0
  17. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/Cargo.toml +0 -0
  18. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/README.md +0 -0
  19. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  20. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/java/mod.rs +0 -0
  21. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  22. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/converters.rs +0 -0
  23. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/error.rs +0 -0
  24. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/ir/mod.rs +0 -0
  25. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  26. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/json.rs +0 -0
  27. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/lib.rs +0 -0
  28. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/parser.rs +0 -0
  29. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/schema.rs +0 -0
  30. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
  31. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/serializer.rs +0 -0
  32. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/transcoder.rs +0 -0
  33. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/src/value.rs +0 -0
  34. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  35. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_core.rs +0 -0
  36. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_java_codegen.rs +0 -0
  37. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_json.rs +0 -0
  38. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  39. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  40. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
  41. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_security.rs +0 -0
  42. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  43. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/Cargo.toml +0 -0
  44. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/README.md +0 -0
  45. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/src/lib.rs +0 -0
  46. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/tests/test_conformance.py +0 -0
  47. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  48. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/tests/test_json.py +0 -0
  49. {polyxml-0.15.0 → polyxml-0.17.0}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  50. {polyxml-0.15.0 → polyxml-0.17.0}/python/polyxml/AGENT_GUIDE.md +0 -0
  51. {polyxml-0.15.0 → polyxml-0.17.0}/python/polyxml/__init__.py +0 -0
  52. {polyxml-0.15.0 → polyxml-0.17.0}/python/polyxml/compat/__init__.py +0 -0
  53. {polyxml-0.15.0 → polyxml-0.17.0}/python/polyxml/compat/xsdata.py +0 -0
  54. {polyxml-0.15.0 → polyxml-0.17.0}/python/polyxml/py.typed +0 -0
@@ -549,7 +549,7 @@ dependencies = [
549
549
 
550
550
  [[package]]
551
551
  name = "polyxml"
552
- version = "0.15.0"
552
+ version = "0.17.0"
553
553
  dependencies = [
554
554
  "criterion",
555
555
  "heck",
@@ -567,14 +567,14 @@ dependencies = [
567
567
 
568
568
  [[package]]
569
569
  name = "polyxml-c"
570
- version = "0.15.0"
570
+ version = "0.17.0"
571
571
  dependencies = [
572
572
  "polyxml",
573
573
  ]
574
574
 
575
575
  [[package]]
576
576
  name = "polyxml-cli"
577
- version = "0.15.0"
577
+ version = "0.17.0"
578
578
  dependencies = [
579
579
  "clap",
580
580
  "glob",
@@ -588,7 +588,7 @@ dependencies = [
588
588
 
589
589
  [[package]]
590
590
  name = "polyxml-js"
591
- version = "0.15.0"
591
+ version = "0.17.0"
592
592
  dependencies = [
593
593
  "napi",
594
594
  "napi-build",
@@ -598,7 +598,7 @@ dependencies = [
598
598
 
599
599
  [[package]]
600
600
  name = "polyxml-python"
601
- version = "0.15.0"
601
+ version = "0.17.0"
602
602
  dependencies = [
603
603
  "lexical-core",
604
604
  "polyxml",
@@ -3,7 +3,7 @@ resolver = "2"
3
3
  members = ["crates/polyxml-core", "crates/polyxml-python"]
4
4
 
5
5
  [workspace.package]
6
- version = "0.15.0"
6
+ version = "0.17.0"
7
7
  edition = "2021"
8
8
  rust-version = "1.80"
9
9
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyxml
3
- Version: 0.15.0
3
+ Version: 0.17.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -20,6 +20,40 @@ pub enum CppMode {
20
20
  Module,
21
21
  }
22
22
 
23
+ impl CppMode {
24
+ pub fn from_str_loose(s: &str) -> Option<Self> {
25
+ match s.to_lowercase().trim() {
26
+ "header" | "headeronly" | "header-only" | "headers" | "hpp" => Some(Self::HeaderOnly),
27
+ "module" | "modules" | "cppm" | "c++20-modules" => Some(Self::Module),
28
+ _ => None,
29
+ }
30
+ }
31
+ }
32
+
33
+ /// Target backend for C++ serialization/reflection metadata.
34
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
35
+ pub enum CppBackend {
36
+ /// Zero-dependency standard C++20 (default)
37
+ #[default]
38
+ Standard,
39
+ /// Glaze compile-time reflectionless serde (`glz::meta`)
40
+ Glaze,
41
+ }
42
+
43
+ impl CppBackend {
44
+ pub fn from_str_loose(s: &str) -> Option<Self> {
45
+ match s.to_lowercase().trim() {
46
+ "standard" | "std" | "default" | "none" => Some(Self::Standard),
47
+ "glaze" | "glz" => Some(Self::Glaze),
48
+ _ => None,
49
+ }
50
+ }
51
+
52
+ pub fn is_glaze(self) -> bool {
53
+ matches!(self, Self::Glaze)
54
+ }
55
+ }
56
+
23
57
  /// Options configuring modern C++20/C++23 code generation.
24
58
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25
59
  pub struct CppOptions {
@@ -27,6 +61,8 @@ pub struct CppOptions {
27
61
  pub namespace: String,
28
62
  /// Header-only (.hpp) or C++20 Module (.cppm) mode (default: HeaderOnly)
29
63
  pub mode: CppMode,
64
+ /// Target serialization and metadata backend (default: Standard)
65
+ pub backend: CppBackend,
30
66
  /// Standard language dialect (default: "c++20")
31
67
  pub standard: String,
32
68
  /// Emit C++20 defaulted equality operators `bool operator==(const T&) const = default;` (default: true)
@@ -48,6 +84,7 @@ impl Default for CppOptions {
48
84
  Self {
49
85
  namespace: "polyxml::generated".to_string(),
50
86
  mode: CppMode::HeaderOnly,
87
+ backend: CppBackend::Standard,
51
88
  standard: "c++20".to_string(),
52
89
  emit_equality_operators: true,
53
90
  emit_enum_converters: true,
@@ -229,6 +266,11 @@ impl CppCodegen {
229
266
  self.emit_root_aliases(&mut out, ir);
230
267
 
231
268
  writeln!(out, "\n}} // namespace {}", ns).unwrap();
269
+
270
+ if self.options.backend.is_glaze() {
271
+ self.emit_glaze_meta(&mut out, ir);
272
+ }
273
+
232
274
  out
233
275
  }
234
276
 
@@ -261,6 +303,11 @@ impl CppCodegen {
261
303
  self.emit_root_aliases(&mut out, ir);
262
304
 
263
305
  writeln!(out, "\n}} // namespace {}", ns).unwrap();
306
+
307
+ if self.options.backend.is_glaze() {
308
+ self.emit_glaze_meta(&mut out, ir);
309
+ }
310
+
264
311
  out
265
312
  }
266
313
 
@@ -314,8 +361,26 @@ impl CppCodegen {
314
361
  /// Generate CMakeLists.txt definition for integration via add_subdirectory or FetchContent.
315
362
  pub fn generate_cmake(&self, project_name: &str) -> String {
316
363
  let safe_name = AsSnakeCase(project_name).to_string();
317
- format!(
318
- r#"cmake_minimum_required(VERSION 3.20)
364
+ if self.options.mode == CppMode::Module {
365
+ format!(
366
+ r#"cmake_minimum_required(VERSION 3.28)
367
+ project({safe_name}_models LANGUAGES CXX)
368
+
369
+ set(CMAKE_CXX_STANDARD 20)
370
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
371
+
372
+ add_library({safe_name})
373
+ target_sources({safe_name}
374
+ PUBLIC
375
+ FILE_SET CXX_MODULES FILES
376
+ {safe_name}.cppm
377
+ )
378
+ target_compile_features({safe_name} PUBLIC cxx_std_20)
379
+ "#
380
+ )
381
+ } else {
382
+ format!(
383
+ r#"cmake_minimum_required(VERSION 3.20)
319
384
  project({safe_name}_models LANGUAGES CXX)
320
385
 
321
386
  set(CMAKE_CXX_STANDARD 20)
@@ -328,7 +393,8 @@ target_include_directories({safe_name} INTERFACE
328
393
  )
329
394
  target_compile_features({safe_name} INTERFACE cxx_std_20)
330
395
  "#
331
- )
396
+ )
397
+ }
332
398
  }
333
399
 
334
400
  /// Generate standalone PolyXMLConfig.cmake for `find_package(PolyXML)`.
@@ -372,7 +438,93 @@ endif()
372
438
  writeln!(out, "#include <string>").unwrap();
373
439
  writeln!(out, "#include <string_view>").unwrap();
374
440
  writeln!(out, "#include <variant>").unwrap();
375
- writeln!(out, "#include <vector>\n").unwrap();
441
+ writeln!(out, "#include <vector>").unwrap();
442
+
443
+ if self.options.backend.is_glaze() {
444
+ writeln!(out, "#include <glaze/glaze.hpp>").unwrap();
445
+ }
446
+ writeln!(out).unwrap();
447
+ }
448
+
449
+ fn emit_glaze_meta(&self, out: &mut String, ir: &SchemaIR) {
450
+ let ns = to_cpp_namespace(&self.options.namespace);
451
+ writeln!(out, "\n// Glaze compile-time reflection metadata\n").unwrap();
452
+
453
+ // 1. Enums
454
+ for type_def in ir.types.values() {
455
+ if let TypeDef::Enum(enum_def) = type_def {
456
+ let enum_name = to_cpp_type_name(&enum_def.qname.local);
457
+ let full_type = format!("{}::{}", ns, enum_name);
458
+ writeln!(out, "template <>").unwrap();
459
+ writeln!(out, "struct glz::meta<{}> {{", full_type).unwrap();
460
+ writeln!(out, " using T = {};", full_type).unwrap();
461
+ if enum_def.variants.is_empty() {
462
+ writeln!(out, " static constexpr auto value = enumerate();").unwrap();
463
+ } else {
464
+ writeln!(out, " static constexpr auto value = enumerate(").unwrap();
465
+ for (i, v) in enum_def.variants.iter().enumerate() {
466
+ let var_name = to_cpp_enum_variant(&v.name);
467
+ let comma = if i + 1 < enum_def.variants.len() {
468
+ ","
469
+ } else {
470
+ ""
471
+ };
472
+ writeln!(out, " {:?}, T::{}{}", v.value, var_name, comma).unwrap();
473
+ }
474
+ writeln!(out, " );").unwrap();
475
+ }
476
+ writeln!(out, "}};\n").unwrap();
477
+ }
478
+ }
479
+
480
+ // 2. Structs
481
+ let sorted_qnames = self.topological_sort_types(ir);
482
+ for qname in sorted_qnames {
483
+ if let Some(TypeDef::Struct(s)) = ir.types.get(&qname) {
484
+ let struct_name = to_cpp_type_name(&s.qname.local);
485
+ let full_type = format!("{}::{}", ns, struct_name);
486
+ writeln!(out, "template <>").unwrap();
487
+ writeln!(out, "struct glz::meta<{}> {{", full_type).unwrap();
488
+ writeln!(out, " using T = {};", full_type).unwrap();
489
+
490
+ let mut field_bindings = Vec::new();
491
+
492
+ // Check for simple_content_base "value"
493
+ if let Some(ref base_qname) = s.base_type {
494
+ let has_value_field = s
495
+ .fields
496
+ .iter()
497
+ .any(|f| f.name == "value" || f.kind == crate::ir::FieldKind::Text);
498
+ if !has_value_field
499
+ && (PrimitiveType::from_xsd_name(&base_qname.local).is_some()
500
+ || matches!(ir.types.get(base_qname), Some(TypeDef::Simple(_))))
501
+ {
502
+ field_bindings.push(r#""value", &T::value"#.to_string());
503
+ }
504
+ }
505
+
506
+ for f in &s.fields {
507
+ let field_name = to_cpp_field_name(&f.name);
508
+ field_bindings.push(format!("{:?}, &T::{}", f.xml_name, field_name));
509
+ }
510
+
511
+ if field_bindings.is_empty() {
512
+ writeln!(out, " static constexpr auto value = object();").unwrap();
513
+ } else {
514
+ writeln!(out, " static constexpr auto value = object(").unwrap();
515
+ for (i, fb) in field_bindings.iter().enumerate() {
516
+ let comma = if i + 1 < field_bindings.len() {
517
+ ","
518
+ } else {
519
+ ""
520
+ };
521
+ writeln!(out, " {}{}", fb, comma).unwrap();
522
+ }
523
+ writeln!(out, " );").unwrap();
524
+ }
525
+ writeln!(out, "}};\n").unwrap();
526
+ }
527
+ }
376
528
  }
377
529
 
378
530
  fn emit_utilities(&self, out: &mut String) {
@@ -24,6 +24,16 @@ pub enum CSharpRecordKind {
24
24
  Struct,
25
25
  }
26
26
 
27
+ impl CSharpRecordKind {
28
+ pub fn from_str_loose(s: &str) -> Option<Self> {
29
+ match s.to_lowercase().trim() {
30
+ "class" | "record" | "record-class" | "reference" => Some(Self::Class),
31
+ "struct" | "record-struct" | "value" => Some(Self::Struct),
32
+ _ => None,
33
+ }
34
+ }
35
+ }
36
+
27
37
  /// Options configuring C# 12 / .NET 8+ code generation.
28
38
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29
39
  pub struct CSharpOptions {
@@ -33,6 +43,10 @@ pub struct CSharpOptions {
33
43
  pub emit_xml_attributes: bool,
34
44
  /// Emit System.Text.Json.Serialization attributes ([JsonPropertyName], etc.)
35
45
  pub emit_json_attributes: bool,
46
+ /// Emit System.Text.Json compile-time source generation context (JsonSerializerContext)
47
+ pub emit_source_gen: bool,
48
+ /// Name of the generated JsonSerializerContext partial class (default: "PolyXmlJsonContext")
49
+ pub source_gen_context_name: String,
36
50
  /// Emit IValidatableObject and restriction facet validation logic
37
51
  pub emit_validation: bool,
38
52
  /// Record emission kind (record class vs record struct)
@@ -49,6 +63,8 @@ impl Default for CSharpOptions {
49
63
  namespace: "Generated".to_string(),
50
64
  emit_xml_attributes: true,
51
65
  emit_json_attributes: true,
66
+ emit_source_gen: false,
67
+ source_gen_context_name: "PolyXmlJsonContext".to_string(),
52
68
  emit_validation: true,
53
69
  record_kind: CSharpRecordKind::Class,
54
70
  use_file_scoped_namespaces: true,
@@ -228,7 +244,7 @@ impl CSharpCodegen {
228
244
  if self.options.emit_xml_attributes {
229
245
  writeln!(out, "using System.Xml.Serialization;").unwrap();
230
246
  }
231
- if self.options.emit_json_attributes {
247
+ if self.options.emit_json_attributes || self.options.emit_source_gen {
232
248
  writeln!(out, "using System.Text.Json.Serialization;").unwrap();
233
249
  }
234
250
  writeln!(out).unwrap();
@@ -274,6 +290,11 @@ impl CSharpCodegen {
274
290
  self.emit_root_elements(&mut out, ir, indent);
275
291
  }
276
292
 
293
+ // Emit Source Generator Context if requested
294
+ if self.options.emit_source_gen {
295
+ self.emit_source_gen_context(&mut out, ir, indent);
296
+ }
297
+
277
298
  if !self.options.use_file_scoped_namespaces {
278
299
  writeln!(out, "}}").unwrap();
279
300
  }
@@ -288,6 +309,64 @@ impl CSharpCodegen {
288
309
  vec![(filename, code)]
289
310
  }
290
311
 
312
+ fn emit_source_gen_context(&self, out: &mut String, ir: &SchemaIR, indent: &str) {
313
+ writeln!(out).unwrap();
314
+ writeln!(
315
+ out,
316
+ "{}/// <summary>\n{}/// Source-generated JsonSerializerContext for Native AOT and high-throughput serialization.\n{}/// </summary>",
317
+ indent, indent, indent
318
+ )
319
+ .unwrap();
320
+ writeln!(
321
+ out,
322
+ "{}[JsonSourceGenerationOptions(WriteIndented = true)]",
323
+ indent
324
+ )
325
+ .unwrap();
326
+
327
+ // Enums
328
+ for def in ir.types.values() {
329
+ if let TypeDef::Enum(e) = def {
330
+ let name = to_csharp_type_name(&e.qname.local);
331
+ writeln!(out, "{}[JsonSerializable(typeof({}))]", indent, name).unwrap();
332
+ }
333
+ }
334
+
335
+ // Simple types
336
+ for def in ir.types.values() {
337
+ if let TypeDef::Simple(s) = def {
338
+ let name = to_csharp_type_name(&s.qname.local);
339
+ writeln!(out, "{}[JsonSerializable(typeof({}))]", indent, name).unwrap();
340
+ }
341
+ }
342
+
343
+ // Unions
344
+ for def in ir.types.values() {
345
+ if let TypeDef::Union(u) = def {
346
+ let name = to_csharp_type_name(&u.qname.local);
347
+ writeln!(out, "{}[JsonSerializable(typeof({}))]", indent, name).unwrap();
348
+ }
349
+ }
350
+
351
+ // Structs
352
+ for def in ir.types.values() {
353
+ if let TypeDef::Struct(s) = def {
354
+ let name = to_csharp_type_name(&s.qname.local);
355
+ writeln!(out, "{}[JsonSerializable(typeof({}))]", indent, name).unwrap();
356
+ writeln!(out, "{}[JsonSerializable(typeof(List<{}>))]", indent, name).unwrap();
357
+ }
358
+ }
359
+
360
+ let ctx_name = &self.options.source_gen_context_name;
361
+ writeln!(
362
+ out,
363
+ "{}public partial class {} : JsonSerializerContext\n{}{{",
364
+ indent, ctx_name, indent
365
+ )
366
+ .unwrap();
367
+ writeln!(out, "{}}}", indent).unwrap();
368
+ }
369
+
291
370
  fn emit_enum(&self, out: &mut String, e: &EnumDef, indent: &str) {
292
371
  let enum_name = to_csharp_type_name(&e.qname.local);
293
372
  if let Some(ref doc) = e.documentation {
@@ -9,9 +9,34 @@ use crate::ir::{
9
9
  StructDef, TypeDef, TypeRef, UnionDef,
10
10
  };
11
11
 
12
+ /// Target serialization backend for Go codegen.
13
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
14
+ pub enum GoBackend {
15
+ /// Standard library `encoding/json` and `encoding/xml` (default)
16
+ #[default]
17
+ Standard,
18
+ /// Mailru/EasyJSON static reflectionless serialization generator (`//easyjson:json`)
19
+ EasyJson,
20
+ /// ByteDance Sonic high-performance JIT serialization (`sonic:"..."` struct tags)
21
+ Sonic,
22
+ }
23
+
24
+ impl GoBackend {
25
+ pub fn from_str_loose(s: &str) -> Option<Self> {
26
+ match s.to_lowercase().trim() {
27
+ "standard" | "std" | "default" => Some(Self::Standard),
28
+ "easyjson" | "easy-json" | "easy_json" => Some(Self::EasyJson),
29
+ "sonic" | "bytedance" => Some(Self::Sonic),
30
+ _ => None,
31
+ }
32
+ }
33
+ }
34
+
12
35
  /// Options configuring Go 1.22+ code generation.
13
36
  #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14
37
  pub struct GoOptions {
38
+ /// Target serialization backend (default: GoBackend::Standard)
39
+ pub backend: GoBackend,
15
40
  /// Package name for generated Go code (default: "models")
16
41
  pub package_name: String,
17
42
  /// Emit encoding/xml tags (default: true)
@@ -29,6 +54,7 @@ pub struct GoOptions {
29
54
  impl Default for GoOptions {
30
55
  fn default() -> Self {
31
56
  Self {
57
+ backend: GoBackend::Standard,
32
58
  package_name: "models".to_string(),
33
59
  emit_xml_tags: true,
34
60
  emit_json_tags: true,
@@ -407,6 +433,10 @@ impl GoCodegen {
407
433
  }
408
434
  }
409
435
 
436
+ if self.options.backend == GoBackend::EasyJson {
437
+ writeln!(out, "//easyjson:json").unwrap();
438
+ }
439
+
410
440
  // Choice container struct
411
441
  writeln!(out, "type {} struct {{", choice_name).unwrap();
412
442
  for branch in &u.branches {
@@ -415,12 +445,22 @@ impl GoCodegen {
415
445
  }
416
446
  let field_name = to_go_field_name(&branch.variant_name);
417
447
  let mapped_type = self.context.map_type_ref(&branch.type_ref);
418
- let xml_tag = if self.options.emit_xml_tags {
419
- format!(" `xml:\"{},omitempty\"`", branch.xml_name)
420
- } else {
448
+ let mut tag_parts = Vec::new();
449
+ if self.options.emit_xml_tags {
450
+ tag_parts.push(format!("xml:\"{},omitempty\"", branch.xml_name));
451
+ }
452
+ if self.options.emit_json_tags {
453
+ tag_parts.push(format!("json:\"{},omitempty\"", branch.xml_name));
454
+ }
455
+ if self.options.backend == GoBackend::Sonic {
456
+ tag_parts.push(format!("sonic:\"{},omitempty\"", branch.xml_name));
457
+ }
458
+ let tag = if tag_parts.is_empty() {
421
459
  String::new()
460
+ } else {
461
+ format!(" `{}`", tag_parts.join(" "))
422
462
  };
423
- writeln!(out, " {} *{}{}", field_name, mapped_type, xml_tag).unwrap();
463
+ writeln!(out, " {} *{}{}", field_name, mapped_type, tag).unwrap();
424
464
  }
425
465
  writeln!(out, "}}\n").unwrap();
426
466
 
@@ -539,14 +579,25 @@ impl GoCodegen {
539
579
  }
540
580
  }
541
581
 
582
+ if self.options.backend == GoBackend::EasyJson {
583
+ writeln!(out, "//easyjson:json").unwrap();
584
+ }
585
+
542
586
  writeln!(out, "type {} struct {{", struct_name).unwrap();
543
587
 
544
588
  // Emit XMLName if xml tags enabled
545
589
  if self.options.emit_xml_tags {
590
+ let mut xml_tags = Vec::new();
546
591
  if self.options.emit_json_tags {
547
- writeln!(out, " XMLName xml.Name `json:\"-\"`").unwrap();
548
- } else {
592
+ xml_tags.push("json:\"-\"");
593
+ }
594
+ if self.options.backend == GoBackend::Sonic {
595
+ xml_tags.push("sonic:\"-\"");
596
+ }
597
+ if xml_tags.is_empty() {
549
598
  writeln!(out, " XMLName xml.Name").unwrap();
599
+ } else {
600
+ writeln!(out, " XMLName xml.Name `{}`", xml_tags.join(" ")).unwrap();
550
601
  }
551
602
  }
552
603
 
@@ -576,6 +627,9 @@ impl GoCodegen {
576
627
  if self.options.emit_json_tags {
577
628
  parts.push("json:\"value,omitempty\"".to_string());
578
629
  }
630
+ if self.options.backend == GoBackend::Sonic {
631
+ parts.push("sonic:\"value,omitempty\"".to_string());
632
+ }
579
633
  let tag = if parts.is_empty() {
580
634
  String::new()
581
635
  } else {
@@ -647,7 +701,7 @@ impl GoCodegen {
647
701
  parts.push(format!("xml:\"{}\"", xml_val));
648
702
  }
649
703
 
650
- if self.options.emit_json_tags {
704
+ if self.options.emit_json_tags || self.options.backend == GoBackend::Sonic {
651
705
  let is_opt = f.cardinality.is_optional() || f.nillable;
652
706
  let json_name = if f.kind == FieldKind::Text {
653
707
  "value".to_string()
@@ -659,7 +713,12 @@ impl GoCodegen {
659
713
  } else {
660
714
  json_name
661
715
  };
662
- parts.push(format!("json:\"{}\"", json_val));
716
+ if self.options.emit_json_tags {
717
+ parts.push(format!("json:\"{}\"", json_val));
718
+ }
719
+ if self.options.backend == GoBackend::Sonic {
720
+ parts.push(format!("sonic:\"{}\"", json_val));
721
+ }
663
722
  }
664
723
 
665
724
  if parts.is_empty() {
@@ -13,13 +13,13 @@ pub mod python;
13
13
  pub mod rust;
14
14
  pub mod typescript;
15
15
 
16
- pub use cpp::{CppCodegen, CppMode, CppOptions};
16
+ pub use cpp::{CppBackend, CppCodegen, CppMode, CppOptions};
17
17
  pub use csharp::{CSharpCodegen, CSharpOptions, CSharpRecordKind};
18
- pub use go::{GoCodegen, GoOptions};
18
+ pub use go::{GoBackend, GoCodegen, GoOptions};
19
19
  pub use java::{JavaBackend, JavaCodegen, JavaOptions};
20
20
  pub use python::{PythonBackend, PythonCodegen, PythonOptions};
21
21
  pub use rust::{RustCodegen, RustOptions};
22
- pub use typescript::{TypeScriptCodegen, TypeScriptOptions};
22
+ pub use typescript::{TypeScriptBackend, TypeScriptCodegen, TypeScriptOptions};
23
23
 
24
24
  use crate::ir::{PrimitiveType, TypeRef};
25
25
 
@@ -25,6 +25,8 @@ pub struct RustOptions {
25
25
  pub emit_root_aliases: bool,
26
26
  /// Emit streaming XML codecs (from_xml, to_xml, decode_xml, encode_xml) (default: true).
27
27
  pub emit_codecs: bool,
28
+ /// Derive rkyv::{Archive, Serialize, Deserialize} zero-copy wire format serialization (default: false).
29
+ pub emit_rkyv: bool,
28
30
  }
29
31
 
30
32
  impl Default for RustOptions {
@@ -36,6 +38,7 @@ impl Default for RustOptions {
36
38
  emit_polyxml_attrs: false,
37
39
  emit_root_aliases: true,
38
40
  emit_codecs: true,
41
+ emit_rkyv: false,
39
42
  }
40
43
  }
41
44
  }
@@ -431,6 +434,13 @@ impl RustCodegen {
431
434
  }
432
435
 
433
436
  let _ = writeln!(out, "#[derive({})]", derives.join(", "));
437
+ if self.options.emit_rkyv {
438
+ let _ = writeln!(
439
+ out,
440
+ "#[cfg_attr(feature = \"rkyv\", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]"
441
+ );
442
+ let _ = writeln!(out, "#[cfg_attr(feature = \"rkyv\", rkyv(check_bytes))]");
443
+ }
434
444
  let _ = writeln!(out, "pub enum {} {{", enum_name);
435
445
 
436
446
  let mut seen_variants = HashSet::new();
@@ -520,6 +530,13 @@ impl RustCodegen {
520
530
  }
521
531
 
522
532
  let _ = writeln!(out, "#[derive({})]", derives.join(", "));
533
+ if self.options.emit_rkyv {
534
+ let _ = writeln!(
535
+ out,
536
+ "#[cfg_attr(feature = \"rkyv\", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]"
537
+ );
538
+ let _ = writeln!(out, "#[cfg_attr(feature = \"rkyv\", rkyv(check_bytes))]");
539
+ }
523
540
  let type_signature = if needs_lifetime {
524
541
  format!("{}<'a>", union_name)
525
542
  } else {
@@ -587,6 +604,13 @@ impl RustCodegen {
587
604
  }
588
605
 
589
606
  let _ = writeln!(out, "#[derive({})]", derives.join(", "));
607
+ if self.options.emit_rkyv {
608
+ let _ = writeln!(
609
+ out,
610
+ "#[cfg_attr(feature = \"rkyv\", derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize))]"
611
+ );
612
+ let _ = writeln!(out, "#[cfg_attr(feature = \"rkyv\", rkyv(check_bytes))]");
613
+ }
590
614
 
591
615
  let struct_decl = if needs_lifetime {
592
616
  format!("pub struct {}<'a>", struct_name)