serde_saphyr/options.rs
1use crate::budget::Budget;
2
3/// Duplicate key handling policy for mappings.
4#[derive(Clone, Copy, Debug)]
5pub enum DuplicateKeyPolicy {
6 /// Error out on encountering a duplicate key.
7 Error,
8 /// First key wins: later duplicate pairs are skipped (key+value are consumed and ignored).
9 FirstWins,
10 /// Last key wins: later duplicate pairs are passed through (default Serde targets typically overwrite).
11 LastWins,
12}
13
14/// Limits applied to alias replay to harden against alias bombs.
15#[derive(Clone, Copy, Debug)]
16pub struct AliasLimits {
17 /// Maximum total number of **replayed** events injected from aliases across the entire parse.
18 /// When exceeded, deserialization errors (alias replay limit exceeded).
19 pub max_total_replayed_events: usize,
20 /// Maximum depth of the alias replay stack (nested alias → injected buffer → alias, etc.).
21 pub max_replay_stack_depth: usize,
22 /// Maximum number of times a **single anchor id** may be expanded via alias.
23 /// Use `usize::MAX` for "unlimited".
24 pub max_alias_expansions_per_anchor: usize,
25}
26
27impl Default for AliasLimits {
28 fn default() -> Self {
29 Self {
30 max_total_replayed_events: 1_000_000,
31 max_replay_stack_depth: 64,
32 max_alias_expansions_per_anchor: usize::MAX,
33 }
34 }
35}
36
37/// Parser configuration options.
38///
39/// Use this to configure duplicate-key policy, alias-replay limits, and an
40/// optional pre-parse YAML [`Budget`].
41///
42/// Example: parse a small `Config` using custom `Options`.
43///
44/// ```rust
45/// use serde::Deserialize;
46///
47/// use serde_saphyr::options::DuplicateKeyPolicy;
48/// use serde_saphyr::{from_str_with_options, Budget, Options};
49///
50/// #[derive(Deserialize)]
51/// struct Config {
52/// name: String,
53/// enabled: bool,
54/// retries: i32,
55/// }
56///
57/// let yaml = r#"
58/// name: My Application
59/// enabled: true
60/// retries: 5
61/// "#;
62///
63/// let options = Options {
64/// budget: Some(Budget {
65/// max_documents: 2,
66/// ..Budget::default()
67/// }),
68/// duplicate_keys: DuplicateKeyPolicy::LastWins,
69/// ..Options::default()
70/// };
71///
72/// let cfg: Config = from_str_with_options(yaml, options).unwrap();
73/// assert_eq!(cfg.name, "My Application");
74/// ```
75#[derive(Clone, Debug)]
76pub struct Options {
77 /// Optional YAML budget to enforce before parsing (counts raw parser events).
78 pub budget: Option<Budget>,
79 /// Policy for duplicate keys.
80 pub duplicate_keys: DuplicateKeyPolicy,
81 /// Limits for alias replay to harden against alias bombs.
82 pub alias_limits: AliasLimits,
83 /// Enable legacy octal parsing where values starting with `00` are treated as base-8.
84 /// They are deprecated in YAML 1.2. Default: false.
85 pub legacy_octal_numbers: bool,
86 /// If true, interpret only the exact literals `true` and `false` as booleans.
87 /// YAML 1.1 forms like `yes`/`no`/`on`/`off` will be rejected and not inferred.
88 /// Default: false (accept YAML 1.1 boolean forms).
89 pub strict_booleans: bool,
90 /// When a field marked with the `!!binary` tag is deserialized into a `String`,
91 /// `serde-saphyr` normally expects the value to be base64-encoded UTF-8.
92 /// If you want to treat the value as a plain string and ignore the `!!binary` tag,
93 /// set this to `true` (the default is `false`).
94 pub ignore_binary_tag_for_string: bool,
95 /// Activates YAML conventions common in robotics community. These extensions support
96 /// conversion functions (deg, rad) and simple mathematical expressions such as deg(180),
97 /// rad(pi), 1 + 2*(3 - 4/5), or rad(pi/2). [robotics] feature must also be enabled.
98 pub angle_conversions: bool,
99 /// If true, values that can be parsed as booleans or numbers are rejected as
100 /// unquoted strings. This flag is intended for teams that want to enforce
101 /// compatibility with YAML parsers that infer types from unquoted values,
102 /// requiring such strings to be explicitly quoted.
103 /// The default is false (a number or boolean will be stored in the string
104 /// field exactly as provided, without quoting).
105 pub no_schema: bool,
106}
107
108impl Default for Options {
109 fn default() -> Self {
110 Self {
111 budget: Some(Budget::default()),
112 duplicate_keys: DuplicateKeyPolicy::Error,
113 alias_limits: AliasLimits::default(),
114 legacy_octal_numbers: false,
115 strict_booleans: false,
116 angle_conversions: false,
117 ignore_binary_tag_for_string: false,
118 no_schema: false,
119 }
120 }
121}