pub mod executor;
pub mod parser;
use std::borrow::Cow;
enum PathSegment<'a> {
Key(Cow<'a, str>),
Index(usize),
}
fn split_path_segments(path: &str) -> Option<Vec<PathSegment<'_>>> {
let mut segments = Vec::new();
let bytes = path.as_bytes();
let mut start = 0;
let mut i = 0;
while i < bytes.len() {
match bytes[i] {
b'"' if i == start => {
let (name, end) = quoted_name(path, i)?;
segments.push(PathSegment::Key(name));
i = end;
start = i;
}
b'.' => {
if start < i {
segments.push(PathSegment::Key(Cow::Borrowed(&path[start..i])));
}
i += 1;
start = i;
}
b'[' => {
if start < i {
segments.push(PathSegment::Key(Cow::Borrowed(&path[start..i])));
}
i += 1;
let idx_start = i;
while i < bytes.len() && bytes[i] != b']' {
i += 1;
}
let idx = path[idx_start..i].parse::<usize>().ok()?;
segments.push(PathSegment::Index(idx));
if i < bytes.len() {
i += 1; }
start = i;
if i < bytes.len() && bytes[i] == b'.' {
i += 1;
start = i;
}
}
_ => {
i += 1;
}
}
}
if start < bytes.len() {
segments.push(PathSegment::Key(Cow::Borrowed(&path[start..])));
}
Some(segments)
}
fn quoted_name(path: &str, open: usize) -> Option<(Cow<'_, str>, usize)> {
let bytes = path.as_bytes();
let mut i = open + 1;
let mut doubled = false;
loop {
match bytes.get(i)? {
b'"' if bytes.get(i + 1) == Some(&b'"') => {
doubled = true;
i += 2;
}
b'"' => break,
_ => i += 1,
}
}
let inner = &path[open + 1..i];
let name = if doubled {
Cow::Owned(inner.replace("\"\"", "\""))
} else {
Cow::Borrowed(inner)
};
Some((name, i + 1))
}
pub(crate) fn attribute_path(name: &str) -> Cow<'_, str> {
if name.contains(['.', '[']) || name.starts_with('"') {
Cow::Owned(format!("\"{}\"", name.replace('"', "\"\"")))
} else {
Cow::Borrowed(name)
}
}
pub(crate) fn names_attribute(path: &str, name: &str) -> bool {
path == attribute_path(name)
}
const MAX_STATEMENT_LENGTH: usize = 8192;
pub(crate) fn statement_length_violation(statement: &str, path: &str) -> Option<String> {
let constraint = match statement.chars().count() {
0 => "Member must have length greater than or equal to 1",
n if n > MAX_STATEMENT_LENGTH => "Member must have length less than or equal to 8192",
_ => return None,
};
Some(format!(
"Value '{statement}' at '{path}' failed to satisfy constraint: {constraint}"
))
}
pub(crate) fn parameters_violation<T>(parameters: Option<&[T]>, path: &str) -> Option<String> {
parameters.filter(|p| p.is_empty()).map(|_| {
format!(
"Value '[]' at '{path}' failed to satisfy constraint: Member must have length \
greater than or equal to 1"
)
})
}
pub(crate) fn member_rejection(violations: &[String]) -> Option<crate::errors::DynoxideError> {
crate::validation::format_validation_errors(violations)
.map(crate::errors::DynoxideError::ValidationException)
}