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}