polyxml 0.14.3__tar.gz → 0.16.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.14.3 → polyxml-0.16.0}/Cargo.lock +5 -5
  2. {polyxml-0.14.3 → polyxml-0.16.0}/Cargo.toml +1 -1
  3. {polyxml-0.14.3 → polyxml-0.16.0}/PKG-INFO +1 -1
  4. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/cpp/mod.rs +156 -4
  5. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/java/mod.rs +164 -12
  6. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/mod.rs +2 -2
  7. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_cpp_codegen.rs +140 -2
  8. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_java_codegen.rs +280 -1
  9. {polyxml-0.14.3 → polyxml-0.16.0}/pyproject.toml +1 -1
  10. {polyxml-0.14.3 → polyxml-0.16.0}/README.md +0 -0
  11. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/Cargo.toml +0 -0
  12. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/README.md +0 -0
  13. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/benches/core_benchmarks.rs +0 -0
  14. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/csharp/mod.rs +0 -0
  15. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/go/mod.rs +0 -0
  16. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/python/mod.rs +0 -0
  17. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/rust/mod.rs +0 -0
  18. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/codegen/typescript/mod.rs +0 -0
  19. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/converters.rs +0 -0
  20. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/error.rs +0 -0
  21. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/ir/mod.rs +0 -0
  22. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/ir/tarjan.rs +0 -0
  23. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/json.rs +0 -0
  24. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/lib.rs +0 -0
  25. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/parser.rs +0 -0
  26. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/schema.rs +0 -0
  27. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/schema_parser/mod.rs +0 -0
  28. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/serializer.rs +0 -0
  29. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/transcoder.rs +0 -0
  30. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/src/value.rs +0 -0
  31. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_conformance.rs +0 -0
  32. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_core.rs +0 -0
  33. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_csharp_codegen.rs +0 -0
  34. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_go_codegen.rs +0 -0
  35. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_json.rs +0 -0
  36. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_python_codegen.rs +0 -0
  37. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_rust_codecs.rs +0 -0
  38. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_rust_codegen.rs +0 -0
  39. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_schema_ir.rs +0 -0
  40. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_security.rs +0 -0
  41. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_transcoder.rs +0 -0
  42. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-core/tests/test_ts_codegen.rs +0 -0
  43. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/Cargo.toml +0 -0
  44. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/README.md +0 -0
  45. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/src/lib.rs +0 -0
  46. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/tests/test_conformance.py +0 -0
  47. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/tests/test_generated_models.py +0 -0
  48. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/tests/test_json.py +0 -0
  49. {polyxml-0.14.3 → polyxml-0.16.0}/crates/polyxml-python/tests/test_polyxml.py +0 -0
  50. {polyxml-0.14.3 → polyxml-0.16.0}/python/polyxml/AGENT_GUIDE.md +0 -0
  51. {polyxml-0.14.3 → polyxml-0.16.0}/python/polyxml/__init__.py +0 -0
  52. {polyxml-0.14.3 → polyxml-0.16.0}/python/polyxml/compat/__init__.py +0 -0
  53. {polyxml-0.14.3 → polyxml-0.16.0}/python/polyxml/compat/xsdata.py +0 -0
  54. {polyxml-0.14.3 → polyxml-0.16.0}/python/polyxml/py.typed +0 -0
@@ -549,7 +549,7 @@ dependencies = [
549
549
 
550
550
  [[package]]
551
551
  name = "polyxml"
552
- version = "0.14.3"
552
+ version = "0.16.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.14.3"
570
+ version = "0.16.0"
571
571
  dependencies = [
572
572
  "polyxml",
573
573
  ]
574
574
 
575
575
  [[package]]
576
576
  name = "polyxml-cli"
577
- version = "0.14.3"
577
+ version = "0.16.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.14.3"
591
+ version = "0.16.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.14.3"
601
+ version = "0.16.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.14.3"
6
+ version = "0.16.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.14.3
3
+ Version: 0.16.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) {
@@ -10,11 +10,37 @@ use crate::ir::{
10
10
  TypeRef, UnionDef,
11
11
  };
12
12
 
13
+ /// Target backend for Java code emission.
14
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
15
+ pub enum JavaBackend {
16
+ /// Zero-dependency Java 21+ records (default).
17
+ #[default]
18
+ Standard,
19
+ /// Jackson-annotated records for Spring Boot / enterprise JSON+XML binding.
20
+ Jackson,
21
+ }
22
+
23
+ impl JavaBackend {
24
+ pub fn from_str_loose(s: &str) -> Option<Self> {
25
+ match s.to_lowercase().trim() {
26
+ "standard" | "std" | "default" => Some(Self::Standard),
27
+ "jackson" | "spring" | "spring-boot" | "enterprise" => Some(Self::Jackson),
28
+ _ => None,
29
+ }
30
+ }
31
+
32
+ pub fn is_jackson(self) -> bool {
33
+ matches!(self, Self::Jackson)
34
+ }
35
+ }
36
+
13
37
  /// Options configuring Java 21+ code generation.
14
38
  #[derive(Debug, Clone, Serialize, Deserialize)]
15
39
  pub struct JavaOptions {
16
40
  /// Java package declaration for generated types (default: "generated.models")
17
41
  pub package_name: String,
42
+ /// Target serialization backend (default: Standard / zero-dependency).
43
+ pub backend: JavaBackend,
18
44
  /// Use Java 21 `record` instead of traditional classes (default: true)
19
45
  pub use_records: bool,
20
46
  /// Generate facet validation in compact constructors (default: true)
@@ -27,6 +53,7 @@ impl Default for JavaOptions {
27
53
  fn default() -> Self {
28
54
  Self {
29
55
  package_name: "generated.models".to_string(),
56
+ backend: JavaBackend::Standard,
30
57
  use_records: true,
31
58
  validate_facets: true,
32
59
  emit_root_aliases: true,
@@ -244,7 +271,11 @@ impl JavaCodegen {
244
271
 
245
272
  fn emit_file_header(&self, out: &mut String) {
246
273
  out.push_str("// Generated by PolyXML Compiler (https://github.com/nth-bailey/PolyXML)\n");
247
- out.push_str("// Target: Java 21+ (Records, Sealed Interfaces, Pattern Matching)\n\n");
274
+ if self.options.backend.is_jackson() {
275
+ out.push_str("// Target: Java 21+ (Records, Jackson XML/JSON Annotations)\n\n");
276
+ } else {
277
+ out.push_str("// Target: Java 21+ (Records, Sealed Interfaces, Pattern Matching)\n\n");
278
+ }
248
279
 
249
280
  if !self.options.package_name.is_empty() {
250
281
  let _ = writeln!(out, "package {};\n", self.options.package_name);
@@ -253,7 +284,15 @@ impl JavaCodegen {
253
284
  out.push_str("import java.util.*;\n");
254
285
  out.push_str("import java.time.*;\n");
255
286
  out.push_str("import java.math.*;\n");
256
- out.push_str("import java.util.regex.Pattern;\n\n");
287
+ out.push_str("import java.util.regex.Pattern;\n");
288
+
289
+ if self.options.backend.is_jackson() {
290
+ out.push('\n');
291
+ out.push_str("import com.fasterxml.jackson.annotation.*;\n");
292
+ out.push_str("import com.fasterxml.jackson.dataformat.xml.annotation.*;\n");
293
+ }
294
+
295
+ out.push('\n');
257
296
  }
258
297
 
259
298
  fn generate_single_type(&self, type_def: &TypeDef, is_nested: bool) -> (String, String) {
@@ -300,6 +339,25 @@ impl JavaCodegen {
300
339
  self.emit_docstring(out, doc, indent);
301
340
  }
302
341
 
342
+ let jackson = self.options.backend.is_jackson();
343
+
344
+ // Jackson class-level annotations
345
+ if jackson {
346
+ let _ = writeln!(out, "{}@JsonIgnoreProperties(ignoreUnknown = true)", indent);
347
+ let _ = writeln!(out, "{}@JsonInclude(JsonInclude.Include.NON_EMPTY)", indent);
348
+ let ns_attr = s
349
+ .qname
350
+ .namespace
351
+ .as_deref()
352
+ .map(|ns| format!(", namespace = {:?}", ns))
353
+ .unwrap_or_default();
354
+ let _ = writeln!(
355
+ out,
356
+ "{}@JacksonXmlRootElement(localName = {:?}{})",
357
+ indent, s.qname.local, ns_attr
358
+ );
359
+ }
360
+
303
361
  let mut seen_fields = HashSet::new();
304
362
  let mut components = Vec::new();
305
363
  let mut validation_checks = Vec::new();
@@ -323,7 +381,38 @@ impl JavaCodegen {
323
381
  self.context.map_type_ref(&field.type_ref)
324
382
  };
325
383
 
326
- components.push(format!("{} {}", field_type, field_id));
384
+ if jackson {
385
+ let mut annotations = Vec::new();
386
+ // @JsonProperty
387
+ annotations.push(format!("@JsonProperty({:?})", field.xml_name));
388
+
389
+ // @JacksonXmlProperty
390
+ let is_attr = field.kind == crate::ir::FieldKind::Attribute;
391
+ let ns_part = field
392
+ .namespace
393
+ .as_deref()
394
+ .map(|ns| format!(", namespace = {:?}", ns))
395
+ .unwrap_or_default();
396
+ annotations.push(format!(
397
+ "@JacksonXmlProperty(localName = {:?}, isAttribute = {}{})",
398
+ field.xml_name, is_attr, ns_part
399
+ ));
400
+
401
+ // List fields: @JacksonXmlElementWrapper(useWrapping = false)
402
+ if is_list {
403
+ annotations.push("@JacksonXmlElementWrapper(useWrapping = false)".to_string());
404
+ }
405
+
406
+ // Optional/list fields: @JsonInclude(NON_EMPTY)
407
+ if is_optional || is_list {
408
+ annotations.push("@JsonInclude(JsonInclude.Include.NON_EMPTY)".to_string());
409
+ }
410
+
411
+ let annotation_str = annotations.join(" ");
412
+ components.push(format!("{} {} {}", annotation_str, field_type, field_id));
413
+ } else {
414
+ components.push(format!("{} {}", field_type, field_id));
415
+ }
327
416
 
328
417
  if self.options.validate_facets {
329
418
  if let Some(ref facets) = field.facets {
@@ -373,6 +462,8 @@ impl JavaCodegen {
373
462
  self.emit_docstring(out, doc, indent);
374
463
  }
375
464
 
465
+ let jackson = self.options.backend.is_jackson();
466
+
376
467
  let modifier = if indent.is_empty() {
377
468
  "public "
378
469
  } else {
@@ -404,10 +495,18 @@ impl JavaCodegen {
404
495
  let _ = writeln!(out, "{} this.value = value;", indent);
405
496
  let _ = writeln!(out, "{} }}\n", indent);
406
497
 
498
+ // @JsonValue on getValue()
499
+ if jackson {
500
+ let _ = writeln!(out, "{} @JsonValue", indent);
501
+ }
407
502
  let _ = writeln!(out, "{} public String getValue() {{", indent);
408
503
  let _ = writeln!(out, "{} return this.value;", indent);
409
504
  let _ = writeln!(out, "{} }}\n", indent);
410
505
 
506
+ // @JsonCreator on fromValue()
507
+ if jackson {
508
+ let _ = writeln!(out, "{} @JsonCreator", indent);
509
+ }
411
510
  let _ = writeln!(
412
511
  out,
413
512
  "{} public static {} fromValue(String value) {{",
@@ -433,6 +532,8 @@ impl JavaCodegen {
433
532
  self.emit_docstring(out, doc, indent);
434
533
  }
435
534
 
535
+ let jackson = self.options.backend.is_jackson();
536
+
436
537
  let modifier = if indent.is_empty() {
437
538
  "public "
438
539
  } else {
@@ -451,6 +552,29 @@ impl JavaCodegen {
451
552
  .collect::<Vec<_>>()
452
553
  .join(", ");
453
554
 
555
+ // Jackson polymorphic type annotations
556
+ if jackson {
557
+ let _ = writeln!(
558
+ out,
559
+ "{}@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = \"@type\")",
560
+ indent
561
+ );
562
+
563
+ let subtypes: Vec<String> = u
564
+ .branches
565
+ .iter()
566
+ .map(|b| {
567
+ let vn = to_java_type_name(&b.variant_name);
568
+ format!(
569
+ "@JsonSubTypes.Type(value = {}.{}.class, name = {:?})",
570
+ java_name, vn, b.xml_name
571
+ )
572
+ })
573
+ .collect();
574
+
575
+ let _ = writeln!(out, "{}@JsonSubTypes({{{}}})", indent, subtypes.join(", "));
576
+ }
577
+
454
578
  let _ = writeln!(
455
579
  out,
456
580
  "{}{}sealed interface {} permits {} {{",
@@ -465,6 +589,10 @@ impl JavaCodegen {
465
589
  self.emit_docstring(out, doc, &format!("{} ", indent));
466
590
  }
467
591
 
592
+ if jackson {
593
+ let _ = writeln!(out, "{} @JsonTypeName({:?})", indent, branch.xml_name);
594
+ }
595
+
468
596
  let _ = writeln!(
469
597
  out,
470
598
  "{} record {}({} value) implements {} {{}}",
@@ -480,6 +608,7 @@ impl JavaCodegen {
480
608
  self.emit_docstring(out, doc, indent);
481
609
  }
482
610
 
611
+ let jackson = self.options.backend.is_jackson();
483
612
  let base_type = self.context.map_type_ref(&s.base_type);
484
613
  let modifier = if indent.is_empty() {
485
614
  "public "
@@ -493,25 +622,48 @@ impl JavaCodegen {
493
622
  Vec::new()
494
623
  };
495
624
 
496
- if checks.is_empty() {
625
+ // Build the value component with optional Jackson annotations
626
+ let value_component = if jackson {
627
+ format!("@JsonValue @JacksonXmlText {} value", base_type)
628
+ } else {
629
+ format!("{} value", base_type)
630
+ };
631
+
632
+ if checks.is_empty() && !jackson {
497
633
  let _ = writeln!(
498
634
  out,
499
- "{}{}record {}({} value) {{}}",
500
- indent, modifier, java_name, base_type
635
+ "{}{}record {}({}) {{}}",
636
+ indent, modifier, java_name, value_component
501
637
  );
502
638
  return;
503
639
  }
504
640
 
505
641
  let _ = writeln!(
506
642
  out,
507
- "{}{}record {}({} value) {{",
508
- indent, modifier, java_name, base_type
643
+ "{}{}record {}({}) {{",
644
+ indent, modifier, java_name, value_component
509
645
  );
510
- let _ = writeln!(out, "{} public {} {{", indent, java_name);
511
- for check in checks {
512
- let _ = writeln!(out, "{} {}", indent, check);
646
+
647
+ // Jackson @JsonCreator factory method
648
+ if jackson {
649
+ let _ = writeln!(out, "{} @JsonCreator", indent);
650
+ let _ = writeln!(
651
+ out,
652
+ "{} public static {} of({} value) {{",
653
+ indent, java_name, base_type
654
+ );
655
+ let _ = writeln!(out, "{} return new {}(value);", indent, java_name);
656
+ let _ = writeln!(out, "{} }}", indent);
513
657
  }
514
- let _ = writeln!(out, "{} }}", indent);
658
+
659
+ if !checks.is_empty() {
660
+ let _ = writeln!(out, "{} public {} {{", indent, java_name);
661
+ for check in checks {
662
+ let _ = writeln!(out, "{} {}", indent, check);
663
+ }
664
+ let _ = writeln!(out, "{} }}", indent);
665
+ }
666
+
515
667
  let _ = writeln!(out, "{}}}", indent);
516
668
  }
517
669
 
@@ -13,10 +13,10 @@ 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
18
  pub use go::{GoCodegen, GoOptions};
19
- pub use java::{JavaCodegen, JavaOptions};
19
+ pub use java::{JavaBackend, JavaCodegen, JavaOptions};
20
20
  pub use python::{PythonBackend, PythonCodegen, PythonOptions};
21
21
  pub use rust::{RustCodegen, RustOptions};
22
22
  pub use typescript::{TypeScriptCodegen, TypeScriptOptions};
@@ -3,8 +3,8 @@ use std::process::Command;
3
3
  use tempfile::tempdir;
4
4
 
5
5
  use polyxml::codegen::cpp::{
6
- to_cpp_enum_variant, to_cpp_field_name, to_cpp_namespace, to_cpp_type_name, CppCodegen,
7
- CppMode, CppOptions,
6
+ to_cpp_enum_variant, to_cpp_field_name, to_cpp_namespace, to_cpp_type_name, CppBackend,
7
+ CppCodegen, CppMode, CppOptions,
8
8
  };
9
9
  use polyxml::ir::{
10
10
  Cardinality, EnumDef, EnumValue, FieldDef, FieldKind, PrimitiveType, QName, RestrictionFacets,
@@ -154,6 +154,7 @@ fn test_cpp_struct_and_enum_codegen_compilation() {
154
154
  let options = CppOptions {
155
155
  namespace: "crm::models".to_string(),
156
156
  mode: CppMode::HeaderOnly,
157
+ backend: CppBackend::Standard,
157
158
  standard: "c++20".to_string(),
158
159
  emit_equality_operators: true,
159
160
  emit_enum_converters: true,
@@ -862,3 +863,140 @@ int main() {
862
863
  "ISO 20022 test binary execution failed"
863
864
  );
864
865
  }
866
+
867
+ #[test]
868
+ fn test_cpp_mode_and_backend_from_str_loose() {
869
+ assert_eq!(CppMode::from_str_loose("header"), Some(CppMode::HeaderOnly));
870
+ assert_eq!(
871
+ CppMode::from_str_loose("header-only"),
872
+ Some(CppMode::HeaderOnly)
873
+ );
874
+ assert_eq!(CppMode::from_str_loose("hpp"), Some(CppMode::HeaderOnly));
875
+ assert_eq!(CppMode::from_str_loose("modules"), Some(CppMode::Module));
876
+ assert_eq!(CppMode::from_str_loose("module"), Some(CppMode::Module));
877
+ assert_eq!(CppMode::from_str_loose("cppm"), Some(CppMode::Module));
878
+ assert_eq!(CppMode::from_str_loose("unknown"), None);
879
+
880
+ assert_eq!(
881
+ CppBackend::from_str_loose("standard"),
882
+ Some(CppBackend::Standard)
883
+ );
884
+ assert_eq!(
885
+ CppBackend::from_str_loose("default"),
886
+ Some(CppBackend::Standard)
887
+ );
888
+ assert_eq!(CppBackend::from_str_loose("glaze"), Some(CppBackend::Glaze));
889
+ assert_eq!(CppBackend::from_str_loose("glz"), Some(CppBackend::Glaze));
890
+ assert_eq!(CppBackend::from_str_loose("unknown"), None);
891
+ }
892
+
893
+ #[test]
894
+ fn test_cpp_glaze_backend_meta_generation() {
895
+ let mut ir = SchemaIR::new().with_target_namespace("https://example.com/shop");
896
+
897
+ // Enum
898
+ ir.add_type(TypeDef::Enum(EnumDef {
899
+ qname: QName::new(Some("https://example.com/shop"), "OrderStatus"),
900
+ base_type: TypeRef::Primitive(PrimitiveType::String),
901
+ variants: vec![
902
+ EnumValue {
903
+ name: "pending".into(),
904
+ value: "pending".into(),
905
+ documentation: None,
906
+ },
907
+ EnumValue {
908
+ name: "shipped".into(),
909
+ value: "shipped".into(),
910
+ documentation: None,
911
+ },
912
+ ],
913
+ documentation: None,
914
+ }));
915
+
916
+ // Struct
917
+ ir.add_type(TypeDef::Struct(StructDef {
918
+ qname: QName::new(Some("https://example.com/shop"), "Order"),
919
+ base_type: None,
920
+ is_abstract: false,
921
+ fields: vec![
922
+ FieldDef {
923
+ name: "order_id".into(),
924
+ xml_name: "orderId".into(),
925
+ namespace: None,
926
+ kind: FieldKind::Element,
927
+ type_ref: TypeRef::Primitive(PrimitiveType::Int),
928
+ cardinality: Cardinality::required_one(),
929
+ nillable: false,
930
+ default_value: None,
931
+ fixed_value: None,
932
+ documentation: None,
933
+ facets: None,
934
+ is_cycle_cut: false,
935
+ },
936
+ FieldDef {
937
+ name: "status".into(),
938
+ xml_name: "status".into(),
939
+ namespace: None,
940
+ kind: FieldKind::Element,
941
+ type_ref: TypeRef::Named(QName::new(
942
+ Some("https://example.com/shop"),
943
+ "OrderStatus",
944
+ )),
945
+ cardinality: Cardinality::required_one(),
946
+ nillable: false,
947
+ default_value: None,
948
+ fixed_value: None,
949
+ documentation: None,
950
+ facets: None,
951
+ is_cycle_cut: false,
952
+ },
953
+ ],
954
+ documentation: None,
955
+ }));
956
+
957
+ let options = CppOptions {
958
+ namespace: "shop".to_string(),
959
+ backend: CppBackend::Glaze,
960
+ ..Default::default()
961
+ };
962
+
963
+ let codegen = CppCodegen::new(options);
964
+ let header_code = codegen.generate_header(&ir);
965
+
966
+ // Includes Glaze header
967
+ assert!(header_code.contains("#include <glaze/glaze.hpp>"));
968
+
969
+ // Glaze enum reflection
970
+ assert!(header_code.contains("template <>"));
971
+ assert!(header_code.contains("struct glz::meta<shop::OrderStatus> {"));
972
+ assert!(header_code.contains("using T = shop::OrderStatus;"));
973
+ assert!(header_code.contains("static constexpr auto value = enumerate("));
974
+ assert!(header_code.contains("\"pending\", T::Pending,"));
975
+ assert!(header_code.contains("\"shipped\", T::Shipped"));
976
+
977
+ // Glaze struct reflection
978
+ assert!(header_code.contains("struct glz::meta<shop::Order> {"));
979
+ assert!(header_code.contains("using T = shop::Order;"));
980
+ assert!(header_code.contains("static constexpr auto value = object("));
981
+ assert!(header_code.contains("\"orderId\", &T::order_id,"));
982
+ assert!(header_code.contains("\"status\", &T::status"));
983
+ }
984
+
985
+ #[test]
986
+ fn test_cpp_cmake_modules_file_set() {
987
+ let _ir = SchemaIR::new();
988
+
989
+ let options = CppOptions {
990
+ namespace: "telemetry".to_string(),
991
+ mode: CppMode::Module,
992
+ emit_cmake: true,
993
+ ..Default::default()
994
+ };
995
+
996
+ let codegen = CppCodegen::new(options);
997
+ let cmake = codegen.generate_cmake("telemetry");
998
+
999
+ assert!(cmake.contains("cmake_minimum_required(VERSION 3.28)"));
1000
+ assert!(cmake.contains("FILE_SET CXX_MODULES FILES"));
1001
+ assert!(cmake.contains("telemetry.cppm"));
1002
+ }
@@ -3,7 +3,8 @@ use std::process::Command;
3
3
  use tempfile::tempdir;
4
4
 
5
5
  use polyxml::codegen::java::{
6
- to_java_enum_constant, to_java_field_identifier, to_java_type_name, JavaCodegen, JavaOptions,
6
+ to_java_enum_constant, to_java_field_identifier, to_java_type_name, JavaBackend, JavaCodegen,
7
+ JavaOptions,
7
8
  };
8
9
  use polyxml::ir::{
9
10
  Cardinality, EnumDef, EnumValue, FieldDef, FieldKind, PrimitiveType, QName, RestrictionFacets,
@@ -153,6 +154,7 @@ fn test_java_records_and_enums_generation() {
153
154
 
154
155
  let options = JavaOptions {
155
156
  package_name: "com.example.shop".to_string(),
157
+ backend: JavaBackend::Standard,
156
158
  use_records: true,
157
159
  validate_facets: true,
158
160
  emit_root_aliases: true,
@@ -239,6 +241,7 @@ fn test_java_sealed_interface_choice() {
239
241
 
240
242
  let options = JavaOptions {
241
243
  package_name: "com.example.payment".to_string(),
244
+ backend: JavaBackend::Standard,
242
245
  use_records: true,
243
246
  validate_facets: true,
244
247
  emit_root_aliases: true,
@@ -291,6 +294,7 @@ fn test_java_module_container_class() {
291
294
 
292
295
  let options = JavaOptions {
293
296
  package_name: "com.example.banking".to_string(),
297
+ backend: JavaBackend::Standard,
294
298
  use_records: true,
295
299
  validate_facets: true,
296
300
  emit_root_aliases: true,
@@ -327,3 +331,278 @@ fn test_java_module_container_class() {
327
331
  );
328
332
  }
329
333
  }
334
+
335
+ #[test]
336
+ fn test_java_jackson_backend_struct_annotations() {
337
+ let mut ir = SchemaIR::new().with_target_namespace("https://example.com/crm");
338
+
339
+ ir.add_type(TypeDef::Struct(StructDef {
340
+ qname: QName::new(Some("https://example.com/crm"), "Contact"),
341
+ base_type: None,
342
+ is_abstract: false,
343
+ fields: vec![
344
+ FieldDef {
345
+ name: "id".into(),
346
+ xml_name: "id".into(),
347
+ namespace: None,
348
+ kind: FieldKind::Attribute,
349
+ type_ref: TypeRef::Primitive(PrimitiveType::Int),
350
+ cardinality: Cardinality::required_one(),
351
+ nillable: false,
352
+ default_value: None,
353
+ fixed_value: None,
354
+ documentation: None,
355
+ facets: None,
356
+ is_cycle_cut: false,
357
+ },
358
+ FieldDef {
359
+ name: "email".into(),
360
+ xml_name: "email".into(),
361
+ namespace: None,
362
+ kind: FieldKind::Element,
363
+ type_ref: TypeRef::Primitive(PrimitiveType::String),
364
+ cardinality: Cardinality::required_one(),
365
+ nillable: false,
366
+ default_value: None,
367
+ fixed_value: None,
368
+ documentation: None,
369
+ facets: None,
370
+ is_cycle_cut: false,
371
+ },
372
+ FieldDef {
373
+ name: "phone".into(),
374
+ xml_name: "phone".into(),
375
+ namespace: None,
376
+ kind: FieldKind::Element,
377
+ type_ref: TypeRef::Primitive(PrimitiveType::String),
378
+ cardinality: Cardinality::optional_one(),
379
+ nillable: true,
380
+ default_value: None,
381
+ fixed_value: None,
382
+ documentation: None,
383
+ facets: None,
384
+ is_cycle_cut: false,
385
+ },
386
+ FieldDef {
387
+ name: "tags".into(),
388
+ xml_name: "tag".into(),
389
+ namespace: None,
390
+ kind: FieldKind::Element,
391
+ type_ref: TypeRef::Primitive(PrimitiveType::String),
392
+ cardinality: Cardinality::unbounded(0),
393
+ nillable: false,
394
+ default_value: None,
395
+ fixed_value: None,
396
+ documentation: None,
397
+ facets: None,
398
+ is_cycle_cut: false,
399
+ },
400
+ ],
401
+ documentation: Some("CRM contact record".into()),
402
+ }));
403
+
404
+ let options = JavaOptions {
405
+ package_name: "com.example.crm".to_string(),
406
+ backend: JavaBackend::Jackson,
407
+ use_records: true,
408
+ validate_facets: true,
409
+ emit_root_aliases: true,
410
+ };
411
+ let codegen = JavaCodegen::new(options);
412
+ let files = codegen.generate_files(&ir);
413
+
414
+ assert_eq!(files.len(), 1);
415
+ let (filename, code) = &files[0];
416
+ assert_eq!(filename, "Contact.java");
417
+
418
+ // Jackson imports
419
+ assert!(code.contains("import com.fasterxml.jackson.annotation.*;"));
420
+ assert!(code.contains("import com.fasterxml.jackson.dataformat.xml.annotation.*;"));
421
+
422
+ // Class-level annotations
423
+ assert!(code.contains("@JsonIgnoreProperties(ignoreUnknown = true)"));
424
+ assert!(code.contains("@JsonInclude(JsonInclude.Include.NON_EMPTY)"));
425
+ assert!(code.contains("@JacksonXmlRootElement(localName = \"Contact\""));
426
+
427
+ // Field annotations — attribute
428
+ assert!(code.contains("@JsonProperty(\"id\")"));
429
+ assert!(code.contains("@JacksonXmlProperty(localName = \"id\", isAttribute = true)"));
430
+
431
+ // Field annotations — element
432
+ assert!(code.contains("@JsonProperty(\"email\")"));
433
+ assert!(code.contains("@JacksonXmlProperty(localName = \"email\", isAttribute = false)"));
434
+
435
+ // Optional field gets NON_EMPTY
436
+ assert!(code.contains("@JsonProperty(\"phone\")"));
437
+
438
+ // List field gets wrapper suppression
439
+ assert!(code.contains("@JacksonXmlElementWrapper(useWrapping = false)"));
440
+ }
441
+
442
+ #[test]
443
+ fn test_java_jackson_backend_enum_annotations() {
444
+ let mut ir = SchemaIR::new().with_target_namespace("https://example.com/orders");
445
+
446
+ ir.add_type(TypeDef::Enum(EnumDef {
447
+ qname: QName::new(Some("https://example.com/orders"), "Priority"),
448
+ base_type: TypeRef::Primitive(PrimitiveType::String),
449
+ variants: vec![
450
+ EnumValue {
451
+ name: "low".into(),
452
+ value: "low".into(),
453
+ documentation: None,
454
+ },
455
+ EnumValue {
456
+ name: "high".into(),
457
+ value: "high".into(),
458
+ documentation: None,
459
+ },
460
+ ],
461
+ documentation: None,
462
+ }));
463
+
464
+ let options = JavaOptions {
465
+ package_name: "com.example.orders".to_string(),
466
+ backend: JavaBackend::Jackson,
467
+ use_records: true,
468
+ validate_facets: true,
469
+ emit_root_aliases: true,
470
+ };
471
+ let codegen = JavaCodegen::new(options);
472
+ let files = codegen.generate_files(&ir);
473
+
474
+ assert_eq!(files.len(), 1);
475
+ let (_, code) = &files[0];
476
+
477
+ // @JsonValue on getValue()
478
+ assert!(code.contains("@JsonValue"));
479
+ assert!(code.contains("public String getValue()"));
480
+
481
+ // @JsonCreator on fromValue()
482
+ assert!(code.contains("@JsonCreator"));
483
+ assert!(code.contains("public static Priority fromValue(String value)"));
484
+ }
485
+
486
+ #[test]
487
+ fn test_java_jackson_backend_union_annotations() {
488
+ let mut ir = SchemaIR::new().with_target_namespace("https://example.com/messaging");
489
+
490
+ ir.add_type(TypeDef::Union(UnionDef {
491
+ qname: QName::new(Some("https://example.com/messaging"), "MessageChannel"),
492
+ branches: vec![
493
+ UnionBranch {
494
+ variant_name: "sms".into(),
495
+ xml_name: "sms".into(),
496
+ namespace: None,
497
+ type_ref: TypeRef::Primitive(PrimitiveType::String),
498
+ documentation: None,
499
+ },
500
+ UnionBranch {
501
+ variant_name: "email".into(),
502
+ xml_name: "email".into(),
503
+ namespace: None,
504
+ type_ref: TypeRef::Primitive(PrimitiveType::String),
505
+ documentation: None,
506
+ },
507
+ ],
508
+ documentation: None,
509
+ }));
510
+
511
+ let options = JavaOptions {
512
+ package_name: "com.example.messaging".to_string(),
513
+ backend: JavaBackend::Jackson,
514
+ use_records: true,
515
+ validate_facets: true,
516
+ emit_root_aliases: true,
517
+ };
518
+ let codegen = JavaCodegen::new(options);
519
+ let files = codegen.generate_files(&ir);
520
+
521
+ assert_eq!(files.len(), 1);
522
+ let (_, code) = &files[0];
523
+
524
+ // Polymorphic type info
525
+ assert!(code.contains("@JsonTypeInfo(use = JsonTypeInfo.Id.NAME"));
526
+ assert!(code.contains("@JsonSubTypes({"));
527
+ assert!(code.contains("@JsonSubTypes.Type(value = MessageChannel.Sms.class, name = \"sms\")"));
528
+ assert!(
529
+ code.contains("@JsonSubTypes.Type(value = MessageChannel.Email.class, name = \"email\")")
530
+ );
531
+
532
+ // Variant type names
533
+ assert!(code.contains("@JsonTypeName(\"sms\")"));
534
+ assert!(code.contains("@JsonTypeName(\"email\")"));
535
+ }
536
+
537
+ #[test]
538
+ fn test_java_jackson_backend_simple_type_annotations() {
539
+ let mut ir = SchemaIR::new().with_target_namespace("https://example.com/types");
540
+
541
+ ir.add_type(TypeDef::Simple(Box::new(SimpleTypeDef {
542
+ qname: QName::new(Some("https://example.com/types"), "CurrencyCode"),
543
+ base_type: TypeRef::Primitive(PrimitiveType::String),
544
+ facets: RestrictionFacets {
545
+ length: Some(3),
546
+ patterns: vec!["^[A-Z]{3}$".into()],
547
+ ..Default::default()
548
+ },
549
+ documentation: Some("ISO 4217 currency code".into()),
550
+ })));
551
+
552
+ let options = JavaOptions {
553
+ package_name: "com.example.types".to_string(),
554
+ backend: JavaBackend::Jackson,
555
+ use_records: true,
556
+ validate_facets: true,
557
+ emit_root_aliases: true,
558
+ };
559
+ let codegen = JavaCodegen::new(options);
560
+ let files = codegen.generate_files(&ir);
561
+
562
+ assert_eq!(files.len(), 1);
563
+ let (_, code) = &files[0];
564
+
565
+ // @JsonValue @JacksonXmlText on value component
566
+ assert!(code.contains("@JsonValue @JacksonXmlText String value"));
567
+
568
+ // @JsonCreator factory method
569
+ assert!(code.contains("@JsonCreator"));
570
+ assert!(code.contains("public static CurrencyCode of(String value)"));
571
+ assert!(code.contains("return new CurrencyCode(value);"));
572
+
573
+ // Validation still present
574
+ assert!(code.contains("if (value.length() != 3)"));
575
+ }
576
+
577
+ #[test]
578
+ fn test_java_jackson_backend_from_str_loose() {
579
+ assert_eq!(
580
+ JavaBackend::from_str_loose("jackson"),
581
+ Some(JavaBackend::Jackson)
582
+ );
583
+ assert_eq!(
584
+ JavaBackend::from_str_loose("Jackson"),
585
+ Some(JavaBackend::Jackson)
586
+ );
587
+ assert_eq!(
588
+ JavaBackend::from_str_loose("spring"),
589
+ Some(JavaBackend::Jackson)
590
+ );
591
+ assert_eq!(
592
+ JavaBackend::from_str_loose("spring-boot"),
593
+ Some(JavaBackend::Jackson)
594
+ );
595
+ assert_eq!(
596
+ JavaBackend::from_str_loose("enterprise"),
597
+ Some(JavaBackend::Jackson)
598
+ );
599
+ assert_eq!(
600
+ JavaBackend::from_str_loose("standard"),
601
+ Some(JavaBackend::Standard)
602
+ );
603
+ assert_eq!(
604
+ JavaBackend::from_str_loose("std"),
605
+ Some(JavaBackend::Standard)
606
+ );
607
+ assert_eq!(JavaBackend::from_str_loose("unknown"), None);
608
+ }
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "polyxml"
7
- version = "0.14.3"
7
+ version = "0.16.0"
8
8
  description = "High-performance, polyglot XML data-binding engine built in Rust"
9
9
  authors = [
10
10
  { name = "Bailey Nguyen", email = "bailey.tan.nguyen@gmail.com" }
File without changes