use crate::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum YamlVersion {
#[default]
V1_2,
V1_1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum ParserProfile {
#[default]
Standard,
Strict,
SerdeYaml,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub struct ParserLimits {
pub max_depth: usize,
pub max_document_length: usize,
pub max_stream_bytes: usize,
pub max_alias_expansions: usize,
pub max_mapping_keys: usize,
pub max_sequence_length: usize,
pub max_events: usize,
pub max_nodes: usize,
pub max_total_scalar_bytes: usize,
pub max_documents: usize,
pub max_merge_keys: usize,
pub alias_anchor_ratio: Option<f64>,
pub alias_jump_event_factor: Option<usize>,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_include_depth: usize,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_include_sources: usize,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_total_include_bytes: usize,
}
impl Default for ParserLimits {
fn default() -> Self {
Self {
max_depth: 128,
max_document_length: 64 * 1024 * 1024,
max_stream_bytes: 256 * 1024 * 1024,
max_alias_expansions: 1_024,
max_mapping_keys: 64 * 1024,
max_sequence_length: 64 * 1024,
max_events: 1_000_000,
max_nodes: 250_000,
max_total_scalar_bytes: 64 * 1024 * 1024,
max_documents: 1_000,
max_merge_keys: 10_000,
alias_anchor_ratio: Some(10.0),
alias_jump_event_factor: None,
#[cfg(feature = "include")]
max_include_depth: 24,
#[cfg(feature = "include")]
max_include_sources: 256,
#[cfg(feature = "include")]
max_total_include_bytes: 64 * 1024 * 1024,
}
}
}
impl ParserLimits {
#[must_use]
pub fn profile(profile: ParserProfile) -> Self {
match profile {
ParserProfile::Standard => Self::default(),
ParserProfile::Strict => Self::strict(),
ParserProfile::SerdeYaml => Self {
alias_jump_event_factor: Some(100),
..Self::default()
},
}
}
#[must_use]
pub const fn strict() -> Self {
Self {
max_depth: 64,
max_document_length: 1024 * 1024,
max_stream_bytes: 16 * 1024 * 1024,
max_alias_expansions: 100,
max_mapping_keys: 1_024,
max_sequence_length: 1_024,
max_events: 100_000,
max_nodes: 25_000,
max_total_scalar_bytes: 1024 * 1024,
max_documents: 100,
max_merge_keys: 1_000,
alias_anchor_ratio: Some(5.0),
alias_jump_event_factor: None,
#[cfg(feature = "include")]
max_include_depth: 8,
#[cfg(feature = "include")]
max_include_sources: 32,
#[cfg(feature = "include")]
max_total_include_bytes: 1024 * 1024,
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ParserConfig {
pub yaml_version: YamlVersion,
pub max_depth: usize,
pub max_document_length: usize,
pub max_stream_bytes: usize,
pub max_alias_expansions: usize,
pub max_mapping_keys: usize,
pub max_sequence_length: usize,
pub max_events: usize,
pub max_nodes: usize,
pub max_total_scalar_bytes: usize,
pub max_documents: usize,
pub max_merge_keys: usize,
pub alias_anchor_ratio: Option<f64>,
pub duplicate_key_policy: DuplicateKeyPolicy,
pub strict_booleans: bool,
pub legacy_booleans: bool,
pub tag_registry: Option<Arc<crate::TagRegistry>>,
pub merge_key_policy: MergeKeyPolicy,
pub no_schema: bool,
pub legacy_octal_numbers: bool,
pub ignore_binary_tag_for_string: bool,
pub plain_scalar_strings: bool,
pub legacy_sexagesimal: bool,
#[cfg(feature = "lossless-u64")]
#[cfg_attr(docsrs, doc(cfg(feature = "lossless-u64")))]
pub lossless_u64_integers: bool,
pub leading_zero_integer_strings: bool,
pub legacy_binary_numbers: bool,
pub float_overflow_strings: bool,
pub integer_overflow_errors: bool,
pub non_scalar_key_policy: NonScalarKeyPolicy,
pub alias_jump_event_factor: Option<usize>,
pub require_indent: RequireIndent,
pub policies: Vec<Arc<dyn crate::policy::Policy>>,
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub properties: Option<Arc<std::collections::HashMap<String, String>>>,
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub strict_properties: bool,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub include_resolver: Option<crate::include::IncludeResolver>,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_include_depth: usize,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_include_sources: usize,
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
pub max_total_include_bytes: usize,
}
impl Default for ParserConfig {
fn default() -> Self {
let limits = ParserLimits::default();
Self {
yaml_version: YamlVersion::V1_2,
max_depth: limits.max_depth,
max_document_length: limits.max_document_length,
max_stream_bytes: limits.max_stream_bytes,
max_alias_expansions: limits.max_alias_expansions,
max_mapping_keys: limits.max_mapping_keys,
max_sequence_length: limits.max_sequence_length,
max_events: limits.max_events,
max_nodes: limits.max_nodes,
max_total_scalar_bytes: limits.max_total_scalar_bytes,
max_documents: limits.max_documents,
max_merge_keys: limits.max_merge_keys,
alias_anchor_ratio: limits.alias_anchor_ratio,
duplicate_key_policy: DuplicateKeyPolicy::default(),
strict_booleans: false,
legacy_booleans: false,
tag_registry: None,
merge_key_policy: MergeKeyPolicy::default(),
no_schema: false,
legacy_octal_numbers: false,
ignore_binary_tag_for_string: false,
plain_scalar_strings: false,
legacy_sexagesimal: false,
#[cfg(feature = "lossless-u64")]
lossless_u64_integers: false,
leading_zero_integer_strings: false,
legacy_binary_numbers: false,
float_overflow_strings: false,
integer_overflow_errors: false,
non_scalar_key_policy: NonScalarKeyPolicy::Stringify,
alias_jump_event_factor: limits.alias_jump_event_factor,
require_indent: RequireIndent::Unchecked,
policies: Vec::new(),
#[cfg(feature = "std")]
properties: None,
#[cfg(feature = "std")]
strict_properties: false,
#[cfg(feature = "include")]
include_resolver: None,
#[cfg(feature = "include")]
max_include_depth: limits.max_include_depth,
#[cfg(feature = "include")]
max_include_sources: limits.max_include_sources,
#[cfg(feature = "include")]
max_total_include_bytes: limits.max_total_include_bytes,
}
}
}
impl ParserConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn profile(profile: ParserProfile) -> Self {
match profile {
ParserProfile::Standard => Self::default(),
ParserProfile::Strict => Self::strict(),
ParserProfile::SerdeYaml => Self::serde_yaml_compat(),
}
}
#[must_use]
pub fn strict() -> Self {
Self {
strict_booleans: true,
duplicate_key_policy: DuplicateKeyPolicy::Error,
require_indent: RequireIndent::Even,
#[cfg(feature = "std")]
strict_properties: true,
..Self::default().with_limits(ParserLimits::profile(ParserProfile::Strict))
}
}
#[must_use]
pub fn serde_yaml_compat() -> Self {
Self {
merge_key_policy: MergeKeyPolicy::AsOrdinary,
leading_zero_integer_strings: true,
legacy_binary_numbers: true,
float_overflow_strings: true,
integer_overflow_errors: true,
non_scalar_key_policy: NonScalarKeyPolicy::Error,
#[cfg(feature = "lossless-u64")]
lossless_u64_integers: true,
..Self::default().with_limits(ParserLimits::profile(ParserProfile::SerdeYaml))
}
}
#[must_use]
pub fn limits(&self) -> ParserLimits {
ParserLimits {
max_depth: self.max_depth,
max_document_length: self.max_document_length,
max_stream_bytes: self.max_stream_bytes,
max_alias_expansions: self.max_alias_expansions,
max_mapping_keys: self.max_mapping_keys,
max_sequence_length: self.max_sequence_length,
max_events: self.max_events,
max_nodes: self.max_nodes,
max_total_scalar_bytes: self.max_total_scalar_bytes,
max_documents: self.max_documents,
max_merge_keys: self.max_merge_keys,
alias_anchor_ratio: self.alias_anchor_ratio,
alias_jump_event_factor: self.alias_jump_event_factor,
#[cfg(feature = "include")]
max_include_depth: self.max_include_depth,
#[cfg(feature = "include")]
max_include_sources: self.max_include_sources,
#[cfg(feature = "include")]
max_total_include_bytes: self.max_total_include_bytes,
}
}
#[must_use]
pub fn with_limits(mut self, limits: ParserLimits) -> Self {
self.max_depth = limits.max_depth;
self.max_document_length = limits.max_document_length;
self.max_stream_bytes = limits.max_stream_bytes;
self.max_alias_expansions = limits.max_alias_expansions;
self.max_mapping_keys = limits.max_mapping_keys;
self.max_sequence_length = limits.max_sequence_length;
self.max_events = limits.max_events;
self.max_nodes = limits.max_nodes;
self.max_total_scalar_bytes = limits.max_total_scalar_bytes;
self.max_documents = limits.max_documents;
self.max_merge_keys = limits.max_merge_keys;
self.alias_anchor_ratio = limits.alias_anchor_ratio;
self.alias_jump_event_factor = limits.alias_jump_event_factor;
#[cfg(feature = "include")]
{
self.max_include_depth = limits.max_include_depth;
self.max_include_sources = limits.max_include_sources;
self.max_total_include_bytes = limits.max_total_include_bytes;
}
self
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use]
pub fn properties(
mut self,
properties: Arc<std::collections::HashMap<String, String>>,
) -> Self {
self.properties = Some(properties);
self
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use]
pub fn strict_properties(mut self, strict: bool) -> Self {
self.strict_properties = strict;
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn include_resolver(mut self, resolver: crate::include::IncludeResolver) -> Self {
self.include_resolver = Some(resolver);
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn max_include_depth(mut self, depth: usize) -> Self {
self.max_include_depth = depth;
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn max_include_sources(mut self, max: usize) -> Self {
self.max_include_sources = max;
self
}
#[cfg(feature = "include")]
#[cfg_attr(docsrs, doc(cfg(feature = "include")))]
#[must_use]
pub fn max_total_include_bytes(mut self, max: usize) -> Self {
self.max_total_include_bytes = max;
self
}
#[must_use]
pub fn version(mut self, version: YamlVersion) -> Self {
self.yaml_version = version;
match version {
YamlVersion::V1_1 => {
self.legacy_booleans = true;
self.legacy_octal_numbers = true;
self.legacy_sexagesimal = true;
}
YamlVersion::V1_2 => {
self.legacy_booleans = false;
self.legacy_octal_numbers = false;
self.legacy_sexagesimal = false;
}
}
self
}
#[must_use]
pub fn with_policy<P>(mut self, policy: P) -> Self
where
P: crate::policy::Policy + 'static,
{
self.policies.push(Arc::new(policy));
self
}
#[must_use]
pub fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
#[must_use]
pub fn max_document_length(mut self, len: usize) -> Self {
self.max_document_length = len;
self
}
#[must_use]
pub fn max_alias_expansions(mut self, expansions: usize) -> Self {
self.max_alias_expansions = expansions;
self
}
#[must_use]
pub fn max_mapping_keys(mut self, max: usize) -> Self {
self.max_mapping_keys = max;
self
}
#[must_use]
pub fn max_sequence_length(mut self, max: usize) -> Self {
self.max_sequence_length = max;
self
}
#[must_use]
pub fn max_events(mut self, max: usize) -> Self {
self.max_events = max;
self
}
#[must_use]
pub fn max_nodes(mut self, max: usize) -> Self {
self.max_nodes = max;
self
}
#[must_use]
pub fn max_total_scalar_bytes(mut self, max: usize) -> Self {
self.max_total_scalar_bytes = max;
self
}
#[must_use]
pub fn max_documents(mut self, max: usize) -> Self {
self.max_documents = max;
self
}
#[must_use]
pub fn max_stream_bytes(mut self, max: usize) -> Self {
self.max_stream_bytes = max;
self
}
#[must_use]
pub fn max_merge_keys(mut self, max: usize) -> Self {
self.max_merge_keys = max;
self
}
#[must_use]
pub fn require_indent(mut self, mode: RequireIndent) -> Self {
self.require_indent = mode;
self
}
#[must_use]
pub fn alias_anchor_ratio(mut self, ratio: Option<f64>) -> Self {
self.alias_anchor_ratio = ratio;
self
}
#[must_use]
pub fn duplicate_key_policy(mut self, policy: DuplicateKeyPolicy) -> Self {
self.duplicate_key_policy = policy;
self
}
#[must_use]
pub fn strict_booleans(mut self, strict: bool) -> Self {
self.strict_booleans = strict;
self
}
#[must_use]
pub fn legacy_booleans(mut self, legacy: bool) -> Self {
self.legacy_booleans = legacy;
self
}
#[must_use]
pub fn tag_registry(mut self, registry: Arc<crate::TagRegistry>) -> Self {
self.tag_registry = Some(registry);
self
}
#[must_use]
pub fn merge_key_policy(mut self, policy: MergeKeyPolicy) -> Self {
self.merge_key_policy = policy;
self
}
#[must_use]
pub fn no_schema(mut self, no_schema: bool) -> Self {
self.no_schema = no_schema;
self
}
#[must_use]
pub fn legacy_octal_numbers(mut self, on: bool) -> Self {
self.legacy_octal_numbers = on;
self
}
#[must_use]
pub fn ignore_binary_tag_for_string(mut self, on: bool) -> Self {
self.ignore_binary_tag_for_string = on;
self
}
#[must_use]
pub fn plain_scalar_strings(mut self, on: bool) -> Self {
self.plain_scalar_strings = on;
self
}
#[must_use]
pub fn legacy_sexagesimal(mut self, on: bool) -> Self {
self.legacy_sexagesimal = on;
self
}
#[cfg(feature = "lossless-u64")]
#[cfg_attr(docsrs, doc(cfg(feature = "lossless-u64")))]
#[must_use]
pub fn lossless_u64_integers(mut self, on: bool) -> Self {
self.lossless_u64_integers = on;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum RequireIndent {
#[default]
Unchecked,
Even,
Divisible(usize),
Uniform(Option<usize>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum MergeKeyPolicy {
#[default]
Auto,
AsOrdinary,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum NonScalarKeyPolicy {
#[default]
Stringify,
Error,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum DuplicateKeyPolicy {
First,
#[default]
Last,
Error,
}