use crate::{BorrowedNode, Error, Node, Result, Span, de, parse};
use serde::de::DeserializeOwned;
use std::io::Read;
pub const DEFAULT_MAX_INPUT_BYTES: usize = 64 * 1024 * 1024;
pub const DEFAULT_ALIAS_EXPANSION_FACTOR: usize = 64;
pub const DEFAULT_MIN_ALIAS_EXPANSION_NODES: usize = 1024;
pub const DEFAULT_MAX_NESTING_DEPTH: usize = 128;
pub const DEFAULT_MAX_SCALAR_BYTES: usize = 1024 * 1024;
pub const DEFAULT_MAX_COLLECTION_ITEMS: usize = 16 * 1024;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Schema {
#[default]
Yaml12,
Core,
Json,
Failsafe,
Yaml11,
LegacySerdeYaml,
YamlVersionDirective,
}
impl Schema {
pub(crate) const fn is_legacy_compatible(self) -> bool {
matches!(self, Self::Yaml11 | Self::LegacySerdeYaml)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LoadOptions {
pub(crate) schema: Schema,
max_input_bytes: Option<usize>,
max_alias_expansion_nodes: Option<usize>,
max_nesting_depth: Option<usize>,
max_scalar_bytes: Option<usize>,
max_collection_items: Option<usize>,
}
impl Default for LoadOptions {
fn default() -> Self {
Self::new()
}
}
impl LoadOptions {
pub const fn new() -> Self {
Self {
schema: Schema::Yaml12,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn core() -> Self {
Self {
schema: Schema::Core,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn json() -> Self {
Self {
schema: Schema::Json,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn failsafe() -> Self {
Self {
schema: Schema::Failsafe,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn yaml_1_1() -> Self {
Self {
schema: Schema::Yaml11,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn legacy_serde_yaml() -> Self {
Self {
schema: Schema::LegacySerdeYaml,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn yaml_version_directive() -> Self {
Self {
schema: Schema::YamlVersionDirective,
max_input_bytes: Some(DEFAULT_MAX_INPUT_BYTES),
max_alias_expansion_nodes: None,
max_nesting_depth: Some(DEFAULT_MAX_NESTING_DEPTH),
max_scalar_bytes: Some(DEFAULT_MAX_SCALAR_BYTES),
max_collection_items: Some(DEFAULT_MAX_COLLECTION_ITEMS),
}
}
pub const fn schema(mut self, schema: Schema) -> Self {
self.schema = schema;
self
}
pub const fn selected_schema(self) -> Schema {
self.schema
}
pub const fn max_input_bytes(mut self, max_input_bytes: usize) -> Self {
self.max_input_bytes = Some(max_input_bytes);
self
}
pub const fn without_input_limit(mut self) -> Self {
self.max_input_bytes = None;
self
}
pub const fn selected_max_input_bytes(self) -> Option<usize> {
self.max_input_bytes
}
pub const fn max_alias_expansion_nodes(mut self, max_alias_expansion_nodes: usize) -> Self {
self.max_alias_expansion_nodes = Some(max_alias_expansion_nodes);
self
}
pub const fn selected_max_alias_expansion_nodes(self) -> Option<usize> {
self.max_alias_expansion_nodes
}
pub const fn max_nesting_depth(mut self, max_nesting_depth: usize) -> Self {
self.max_nesting_depth = Some(max_nesting_depth);
self
}
pub const fn without_nesting_depth_limit(mut self) -> Self {
self.max_nesting_depth = None;
self
}
pub const fn selected_max_nesting_depth(self) -> Option<usize> {
self.max_nesting_depth
}
pub const fn max_scalar_bytes(mut self, max_scalar_bytes: usize) -> Self {
self.max_scalar_bytes = Some(max_scalar_bytes);
self
}
pub const fn without_scalar_limit(mut self) -> Self {
self.max_scalar_bytes = None;
self
}
pub const fn selected_max_scalar_bytes(self) -> Option<usize> {
self.max_scalar_bytes
}
pub const fn max_collection_items(mut self, max_collection_items: usize) -> Self {
self.max_collection_items = Some(max_collection_items);
self
}
pub const fn without_collection_limit(mut self) -> Self {
self.max_collection_items = None;
self
}
pub const fn selected_max_collection_items(self) -> Option<usize> {
self.max_collection_items
}
pub(crate) fn alias_expansion_budget(self, input_len: usize) -> usize {
self.max_alias_expansion_nodes.unwrap_or_else(|| {
input_len
.saturating_mul(DEFAULT_ALIAS_EXPANSION_FACTOR)
.max(DEFAULT_MIN_ALIAS_EXPANSION_NODES)
})
}
pub(crate) fn check_input_len(self, len: usize) -> Result<()> {
if self.max_input_bytes.is_some_and(|max| len > max) {
return Err(self.input_limit_error());
}
Ok(())
}
pub(crate) fn input_limit_error(self) -> Error {
let max = self.max_input_bytes.unwrap_or(DEFAULT_MAX_INPUT_BYTES);
Error::limit(
format!("YAML input exceeds configured limit of {max} bytes"),
Span::default(),
)
}
pub(crate) fn check_nesting_depth(self, depth: usize, span: Span) -> Result<()> {
if self.max_nesting_depth.is_some_and(|max| depth > max) {
return Err(Error::limit("maximum YAML nesting depth exceeded", span));
}
Ok(())
}
pub(crate) fn check_scalar_bytes(self, len: usize, span: Span) -> Result<()> {
if let Some(max) = self.max_scalar_bytes
&& len > max
{
return Err(Error::limit(
format!("YAML scalar exceeds configured limit of {max} bytes"),
span,
));
}
Ok(())
}
pub(crate) fn check_collection_items(self, len: usize, span: Span) -> Result<()> {
if let Some(max) = self.max_collection_items
&& len > max
{
return Err(Error::limit(
format!("YAML collection exceeds configured limit of {max} entries"),
span,
));
}
Ok(())
}
pub fn parse_bytes(self, input: &[u8]) -> Result<Node> {
parse::parse_bytes_with_options(input, self)
}
pub fn parse_str(self, input: &str) -> Result<Node> {
parse::parse_str_with_options(input, self)
}
pub fn parse_documents(self, input: &str) -> Result<Vec<Node>> {
parse::parse_documents_with_options(input, self)
}
pub fn parse_borrowed_documents<'de>(self, input: &'de str) -> Result<Vec<BorrowedNode<'de>>> {
parse::parse_borrowed_documents_with_options(input, self)
}
pub fn stream_events(self, input: &str) -> Result<parse::EventStream> {
parse::EventStream::from_str_with_options(input, self)
}
pub fn stream_events_slice(self, input: &[u8]) -> Result<parse::EventStream> {
parse::EventStream::from_slice_with_options(input, self)
}
pub fn stream_events_reader<R>(self, reader: R) -> Result<parse::EventStream>
where
R: Read,
{
parse::EventStream::from_reader_with_options(reader, self)
}
pub fn stream_documents(self, input: &str) -> Result<parse::DocumentStream> {
parse::DocumentStream::from_str_with_options(input, self)
}
pub fn stream_documents_slice(self, input: &[u8]) -> Result<parse::DocumentStream> {
parse::DocumentStream::from_slice_with_options(input, self)
}
pub fn stream_documents_reader<R>(self, reader: R) -> Result<parse::DocumentStream>
where
R: Read,
{
parse::DocumentStream::from_reader_with_options(reader, self)
}
pub fn from_str<'de, T>(self, input: &'de str) -> Result<T>
where
T: serde::Deserialize<'de>,
{
de::from_str_with_options(input, self)
}
pub fn from_slice<'de, T>(self, input: &'de [u8]) -> Result<T>
where
T: serde::Deserialize<'de>,
{
de::from_slice_with_options(input, self)
}
pub fn from_reader<R, T>(self, reader: R) -> Result<T>
where
R: Read,
T: DeserializeOwned,
{
de::from_reader_with_options(reader, self)
}
pub fn from_documents_str<T>(self, input: &str) -> Result<Vec<T>>
where
T: DeserializeOwned,
{
de::from_documents_str_with_options(input, self)
}
pub fn from_documents_slice<T>(self, input: &[u8]) -> Result<Vec<T>>
where
T: DeserializeOwned,
{
de::from_documents_slice_with_options(input, self)
}
pub fn from_documents_reader<T, R>(self, reader: R) -> Result<Vec<T>>
where
T: DeserializeOwned,
R: Read,
{
de::from_documents_reader_with_options(reader, self)
}
pub fn deserializer_from_str<'de>(self, input: &'de str) -> de::Deserializer<'de> {
de::Deserializer::from_str_with_options(input, self)
}
pub fn deserializer_from_slice<'de>(self, input: &'de [u8]) -> de::Deserializer<'de> {
de::Deserializer::from_slice_with_options(input, self)
}
pub fn deserializer_from_reader<R>(self, reader: R) -> de::Deserializer<'static>
where
R: Read,
{
de::Deserializer::from_reader_with_options(reader, self)
}
}