extern crate alloc;
pub mod object_key;
pub mod record;
pub mod tuple;
pub mod union;
pub mod variant_path;
use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use indexmap::{IndexMap, IndexSet};
pub use object_key::ParseObjectKey;
pub use record::RecordParser;
pub use tuple::TupleParser;
pub use union::UnionParser;
pub use variant_path::VariantPath;
use alloc::format;
use alloc::rc::Rc;
use core::cell::RefCell;
use num_bigint::BigInt;
use core::marker::PhantomData;
use std::collections::{BTreeMap, HashMap, HashSet};
use crate::{
document::node::{Node, NodeArray},
identifier::IdentifierError,
prelude_internal::*,
value::ValueKind,
};
pub type AccessedSnapshot = (HashSet<String>, HashSet<Identifier>);
#[derive(Debug, Clone)]
pub struct AccessedSet(Rc<RefCell<Vec<AccessedSnapshot>>>);
impl AccessedSet {
pub fn new() -> Self {
Self(Rc::new(RefCell::new(vec![(
HashSet::new(),
HashSet::new(),
)])))
}
pub fn add_field(&self, field: impl Into<String>) {
self.0
.borrow_mut()
.last_mut()
.unwrap()
.0
.insert(field.into());
}
pub fn add_ext(&self, ext: Identifier) {
self.0.borrow_mut().last_mut().unwrap().1.insert(ext);
}
pub fn has_field(&self, field: &str) -> bool {
self.0.borrow().last().unwrap().0.contains(field)
}
pub fn has_ext(&self, ext: &Identifier) -> bool {
self.0.borrow().last().unwrap().1.contains(ext)
}
pub fn get_accessed_exts(&self) -> HashSet<Identifier> {
self.0.borrow().last().unwrap().1.clone()
}
pub fn push_snapshot(&self) {
let mut stack = self.0.borrow_mut();
let snapshot = stack.last().unwrap().clone();
let len = stack.len();
stack.insert(len - 1, snapshot);
}
pub fn restore_to_current_snapshot(&self) {
let mut stack = self.0.borrow_mut();
if stack.len() >= 2 {
let snapshot = stack[stack.len() - 2].clone();
*stack.last_mut().unwrap() = snapshot;
}
}
pub fn capture_current_state(&self) -> AccessedSnapshot {
self.0.borrow().last().unwrap().clone()
}
pub fn restore_to_state(&self, state: AccessedSnapshot) {
*self.0.borrow_mut().last_mut().unwrap() = state;
}
pub fn pop_and_restore(&self) {
let mut stack = self.0.borrow_mut();
if stack.len() >= 2 {
stack.pop(); }
}
pub fn pop_without_restore(&self) {
let mut stack = self.0.borrow_mut();
if stack.len() >= 2 {
let snapshot_idx = stack.len() - 2;
stack.remove(snapshot_idx); }
}
}
impl Default for AccessedSet {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ParserScope {
Record,
Extension,
}
#[derive(Debug, Clone)]
pub struct FlattenContext {
accessed: AccessedSet,
scope: ParserScope,
}
impl FlattenContext {
pub fn new(accessed: AccessedSet, scope: ParserScope) -> Self {
Self { accessed, scope }
}
pub fn scope(&self) -> ParserScope {
self.scope
}
pub fn accessed_set(&self) -> &AccessedSet {
&self.accessed
}
pub fn add_field(&self, field: impl Into<String>) {
self.accessed.add_field(field);
}
pub fn add_ext(&self, ext: Identifier) {
self.accessed.add_ext(ext);
}
pub fn has_field(&self, field: &str) -> bool {
self.accessed.has_field(field)
}
pub fn has_ext(&self, ext: &Identifier) -> bool {
self.accessed.has_ext(ext)
}
pub fn push_snapshot(&self) {
self.accessed.push_snapshot();
}
pub fn restore_to_current_snapshot(&self) {
self.accessed.restore_to_current_snapshot();
}
pub fn capture_current_state(&self) -> AccessedSnapshot {
self.accessed.capture_current_state()
}
pub fn restore_to_state(&self, state: AccessedSnapshot) {
self.accessed.restore_to_state(state);
}
pub fn pop_and_restore(&self) {
self.accessed.pop_and_restore();
}
pub fn pop_without_restore(&self) {
self.accessed.pop_without_restore();
}
}
#[derive(Clone, Debug)]
pub struct ParseContext<'doc> {
doc: &'doc EureDocument,
node_id: NodeId,
variant_path: Option<VariantPath>,
flatten_ctx: Option<FlattenContext>,
accessed: AccessedSet,
}
impl<'doc> ParseContext<'doc> {
pub fn new(doc: &'doc EureDocument, node_id: NodeId) -> Self {
Self {
doc,
node_id,
variant_path: None,
flatten_ctx: None,
accessed: AccessedSet::new(),
}
}
pub fn with_flatten_ctx(
doc: &'doc EureDocument,
node_id: NodeId,
flatten_ctx: FlattenContext,
) -> Self {
let accessed = flatten_ctx.accessed_set().clone();
Self {
doc,
node_id,
variant_path: None,
flatten_ctx: Some(flatten_ctx),
accessed,
}
}
pub fn flatten_ctx(&self) -> Option<&FlattenContext> {
self.flatten_ctx.as_ref()
}
pub fn is_flattened(&self) -> bool {
self.flatten_ctx.is_some()
}
pub fn parser_scope(&self) -> Option<ParserScope> {
self.flatten_ctx.as_ref().map(|fc| fc.scope())
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub(crate) fn doc(&self) -> &'doc EureDocument {
self.doc
}
pub fn node(&self) -> &'doc Node {
self.doc.node(self.node_id)
}
pub(crate) fn at(&self, node_id: NodeId) -> Self {
Self {
doc: self.doc,
node_id,
variant_path: None,
flatten_ctx: None,
accessed: AccessedSet::new(),
}
}
pub fn flatten(&self) -> Self {
let flatten_ctx = match &self.flatten_ctx {
Some(fc) => FlattenContext::new(fc.accessed_set().clone(), ParserScope::Record),
None => FlattenContext::new(self.accessed.clone(), ParserScope::Record),
};
Self {
doc: self.doc,
node_id: self.node_id,
variant_path: self.variant_path.clone(),
flatten_ctx: Some(flatten_ctx),
accessed: self.accessed.clone(),
}
}
pub fn parse<T: FromEure<'doc, T>>(&self) -> Result<T, T::Error> {
T::parse(self)
}
pub fn parse_via<M, T>(&self) -> Result<T, M::Error>
where
M: FromEure<'doc, T>,
{
M::parse(self)
}
pub fn parse_with<T: DocumentParser<'doc>>(
&self,
mut parser: T,
) -> Result<T::Output, T::Error> {
parser.parse(self)
}
pub fn parse_union<T, E>(&self) -> Result<UnionParser<'doc, '_, T, E>, E>
where
E: UnionParseError,
{
UnionParser::new(self).map_err(Into::into)
}
pub fn parse_record(&self) -> Result<RecordParser<'doc>, ParseError> {
self.ensure_no_variant_path()?;
RecordParser::new(self)
}
pub fn parse_tuple(&self) -> Result<TupleParser<'doc>, ParseError> {
self.ensure_no_variant_path()?;
TupleParser::new(self)
}
pub fn parse_primitive(&self) -> Result<&'doc PrimitiveValue, ParseError> {
self.ensure_no_variant_path()?;
match &self.node().content {
NodeValue::Primitive(p) => Ok(p),
_ => Err(ParseError {
node_id: self.node_id(),
kind: ParseErrorKind::NotPrimitive {
actual: self.node().content.value_kind(),
},
}),
}
}
pub(crate) fn accessed(&self) -> &AccessedSet {
&self.accessed
}
pub fn node_subtree_to_document_excluding_accessed(&self) -> EureDocument {
let mut doc = self.doc.node_subtree_to_document(self.node_id);
let root_id = doc.get_root_id();
let accessed_exts = self.accessed.get_accessed_exts();
for ext in accessed_exts {
doc.node_mut(root_id).extensions.remove_fast(&ext);
}
doc
}
fn mark_ext_accessed(&self, ident: Identifier) {
self.accessed.add_ext(ident);
}
pub fn parse_ext<T>(&self, name: &str) -> Result<T, T::Error>
where
T: FromEure<'doc>,
T::Error: From<ParseError>,
{
self.parse_ext_with(name, T::parse)
}
pub fn parse_ext_with<T>(&self, name: &str, mut parser: T) -> Result<T::Output, T::Error>
where
T: DocumentParser<'doc>,
T::Error: From<ParseError>,
{
let ident: Identifier = name.parse().map_err(|e| ParseError {
node_id: self.node_id,
kind: ParseErrorKind::InvalidIdentifier(e),
})?;
self.mark_ext_accessed(ident.clone());
let ext_node_id = self
.node()
.extensions
.get(&ident)
.ok_or_else(|| ParseError {
node_id: self.node_id,
kind: ParseErrorKind::MissingExtension(name.to_string()),
})?;
let ctx = ParseContext::new(self.doc, *ext_node_id);
parser.parse(&ctx)
}
pub fn parse_ext_optional<T>(&self, name: &str) -> Result<Option<T>, T::Error>
where
T: FromEure<'doc>,
T::Error: From<ParseError>,
{
self.parse_ext_optional_with(name, T::parse)
}
pub fn parse_ext_optional_with<T>(
&self,
name: &str,
mut parser: T,
) -> Result<Option<T::Output>, T::Error>
where
T: DocumentParser<'doc>,
T::Error: From<ParseError>,
{
let ident: Identifier = name.parse().map_err(|e| ParseError {
node_id: self.node_id,
kind: ParseErrorKind::InvalidIdentifier(e),
})?;
self.mark_ext_accessed(ident.clone());
match self.node().extensions.get(&ident) {
Some(ext_node_id) => {
let ctx = ParseContext::new(self.doc, *ext_node_id);
Ok(Some(parser.parse(&ctx)?))
}
None => Ok(None),
}
}
pub fn ext(&self, name: &str) -> Result<ParseContext<'doc>, ParseError> {
let ident: Identifier = name.parse().map_err(|e| ParseError {
node_id: self.node_id,
kind: ParseErrorKind::InvalidIdentifier(e),
})?;
self.mark_ext_accessed(ident.clone());
let ext_node_id =
self.node()
.extensions
.get(&ident)
.copied()
.ok_or_else(|| ParseError {
node_id: self.node_id,
kind: ParseErrorKind::MissingExtension(name.to_string()),
})?;
Ok(ParseContext::new(self.doc, ext_node_id))
}
pub fn ext_optional(&self, name: &str) -> Option<ParseContext<'doc>> {
let ident: Identifier = name.parse().ok()?;
self.mark_ext_accessed(ident.clone());
self.node()
.extensions
.get(&ident)
.map(|&node_id| ParseContext::new(self.doc, node_id))
}
pub fn deny_unknown_extensions(&self) -> Result<(), ParseError> {
if self.flatten_ctx.is_some() {
return Ok(());
}
for (ident, _) in self.node().extensions.iter() {
if !self.accessed.has_ext(ident) {
return Err(ParseError {
node_id: self.node_id,
kind: ParseErrorKind::UnknownExtension(ident.clone()),
});
}
}
Ok(())
}
pub fn unknown_extensions(
&self,
) -> impl Iterator<Item = (&'doc Identifier, ParseContext<'doc>)> + '_ {
let doc = self.doc;
let accessed = self.accessed.clone();
self.node()
.extensions
.iter()
.filter_map(move |(ident, &node_id)| {
if !accessed.has_ext(ident) {
Some((ident, ParseContext::new(doc, node_id)))
} else {
None
}
})
}
pub fn flatten_ext(&self) -> ParseContext<'doc> {
let flatten_ctx = match &self.flatten_ctx {
Some(fc) => FlattenContext::new(fc.accessed_set().clone(), ParserScope::Extension),
None => FlattenContext::new(self.accessed.clone(), ParserScope::Extension),
};
ParseContext::with_flatten_ctx(self.doc, self.node_id, flatten_ctx)
}
pub fn is_null(&self) -> bool {
matches!(
&self.node().content,
NodeValue::Primitive(PrimitiveValue::Null)
)
}
pub(crate) fn with_variant_rest(&self, rest: Option<VariantPath>) -> Self {
Self {
doc: self.doc,
node_id: self.node_id,
variant_path: rest,
flatten_ctx: self.flatten_ctx.clone(),
accessed: self.accessed.clone(),
}
}
pub(crate) fn variant_path(&self) -> Option<&VariantPath> {
self.variant_path.as_ref()
}
fn ensure_no_variant_path(&self) -> Result<(), ParseError> {
if let Some(vp) = &self.variant_path
&& !vp.is_empty()
{
return Err(ParseError {
node_id: self.node_id,
kind: ParseErrorKind::UnexpectedVariantPath(vp.clone()),
});
}
Ok(())
}
fn unexpected_kind(&self, expected: ValueKind) -> ParseError {
ParseError {
node_id: self.node_id(),
kind: ParseErrorKind::TypeMismatch {
expected,
actual: self.node().content.value_kind(),
},
}
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be parsed from Eure document",
label = "this type does not implement `FromEure`",
note = "consider adding `#[derive(FromEure)]` to `{Self}`"
)]
pub trait FromEure<'doc, T = Self>: Sized {
type Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<T, Self::Error>;
}
pub trait UnionParseError: From<ParseError> {
fn as_parse_error(&self) -> Option<&ParseError>;
fn from_no_matching_variant(
node_id: NodeId,
variant: Option<String>,
best_match: Option<BestParseVariantMatch>,
_failures: &[(String, Self)],
) -> Self {
ParseError {
node_id,
kind: ParseErrorKind::NoMatchingVariant {
variant,
best_match: best_match.map(Box::new),
},
}
.into()
}
}
impl UnionParseError for ParseError {
fn as_parse_error(&self) -> Option<&ParseError> {
Some(self)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BestParseVariantMatch {
pub variant_name: String,
pub error: Box<ParseError>,
}
#[derive(Debug, thiserror::Error, Clone, PartialEq)]
#[error("parse error: {kind}")]
pub struct ParseError {
pub node_id: NodeId,
pub kind: ParseErrorKind,
}
#[derive(Debug, thiserror::Error, Clone, PartialEq)]
pub enum ParseErrorKind {
#[error("unexpected uninitialized value")]
UnexpectedHole,
#[error("type mismatch: expected {expected}, got {actual}")]
TypeMismatch {
expected: ValueKind,
actual: ValueKind,
},
#[error("missing field: {0}")]
MissingField(String),
#[error("missing extension: ${0}")]
MissingExtension(String),
#[error("unknown variant: {0}")]
UnknownVariant(String),
#[error("value out of range: {0}")]
OutOfRange(String),
#[error("invalid {kind}: {reason}")]
InvalidPattern { kind: String, reason: String },
#[error("at {path}: {source}")]
Nested {
path: String,
#[source]
source: Box<ParseErrorKind>,
},
#[error("invalid identifier: {0}")]
InvalidIdentifier(#[from] IdentifierError),
#[error("unexpected tuple length: expected {expected}, got {actual}")]
UnexpectedTupleLength { expected: usize, actual: usize },
#[error("unknown field: {0}")]
UnknownField(String),
#[error("unknown extension: ${0}")]
UnknownExtension(Identifier),
#[error("invalid key type in record: expected string key, got {0:?}")]
InvalidKeyType(crate::value::ObjectKey),
#[error("{}", format_no_matching_variant(variant, best_match))]
NoMatchingVariant {
variant: Option<String>,
best_match: Option<Box<BestParseVariantMatch>>,
},
#[error("conflicting variant tags: $variant = {explicit}, repr = {repr}")]
ConflictingVariantTags { explicit: String, repr: String },
#[error("ambiguous union: {0:?}")]
AmbiguousUnion(Vec<String>),
#[error("literal value mismatch: expected {expected}, got {actual}")]
LiteralMismatch { expected: String, actual: String },
#[error("unexpected variant path: {0}")]
UnexpectedVariantPath(VariantPath),
#[error("$variant must be a string, got {0}")]
InvalidVariantType(ValueKind),
#[error("invalid $variant path syntax: {0}")]
InvalidVariantPath(String),
#[error(
"cannot parse record in extension scope: use #[eure(flatten)] instead of #[eure(flatten_ext)]"
)]
RecordInExtensionScope,
#[error("unexpected array length: expected {expected}, got {actual}")]
UnexpectedArrayLength { expected: usize, actual: usize },
#[error("expected primitive value, got {actual}")]
NotPrimitive { actual: ValueKind },
}
impl ParseErrorKind {
pub fn at(self, path: impl Into<String>) -> Self {
ParseErrorKind::Nested {
path: path.into(),
source: Box::new(self),
}
}
}
fn format_no_matching_variant(
variant: &Option<String>,
best_match: &Option<Box<BestParseVariantMatch>>,
) -> String {
let mut message = "no matching variant".to_string();
if let Some(variant) = variant {
message.push_str(&format!(" (variant: {variant})"));
}
if let Some(best_match) = best_match {
message.push_str(&format!(
" (based on nearest variant '{}')",
best_match.variant_name
));
}
message
}
fn unwrap_best_variant_error<E: UnionParseError>(error: E) -> E {
if let Some(ParseError {
kind:
ParseErrorKind::NoMatchingVariant {
best_match: Some(best_match),
..
},
..
}) = error.as_parse_error()
{
return E::from(best_match.error.as_ref().clone());
}
error
}
impl<'doc> EureDocument {
pub fn parse<T: FromEure<'doc, T>>(&'doc self, node_id: NodeId) -> Result<T, T::Error> {
self.parse_with(node_id, T::parse)
}
pub fn parse_via<M, T>(&'doc self, node_id: NodeId) -> Result<T, M::Error>
where
M: FromEure<'doc, T>,
{
let ctx = self.parse_context(node_id);
M::parse(&ctx)
}
pub fn parse_with<T: DocumentParser<'doc>>(
&'doc self,
node_id: NodeId,
mut parser: T,
) -> Result<T::Output, T::Error> {
parser.parse(&self.parse_context(node_id))
}
pub fn parse_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
ParseContext::new(self, node_id)
}
pub fn parse_record(&'doc self, node_id: NodeId) -> Result<RecordParser<'doc>, ParseError> {
RecordParser::from_doc_and_node(self, node_id)
}
pub fn parse_extension_context(&'doc self, node_id: NodeId) -> ParseContext<'doc> {
ParseContext::new(self, node_id)
}
pub fn parse_tuple(&'doc self, node_id: NodeId) -> Result<TupleParser<'doc>, ParseError> {
TupleParser::from_doc_and_node(self, node_id)
}
}
impl<'doc> FromEure<'doc> for EureDocument {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
Ok(ctx.doc().node_subtree_to_document(ctx.node_id()))
}
}
impl<'doc> FromEure<'doc> for &'doc str {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
return Ok(text.as_str());
}
Err(ctx.unexpected_kind(ValueKind::Text))
}
}
impl FromEure<'_> for String {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
ctx.parse::<&str>().map(String::from)
}
}
impl FromEure<'_> for crate::layout::LayoutStyle {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
let value: &str = ctx.parse()?;
match value {
"auto" => Ok(crate::layout::LayoutStyle::Auto),
"passthrough" => Ok(crate::layout::LayoutStyle::Passthrough),
"section" => Ok(crate::layout::LayoutStyle::Section),
"nested" => Ok(crate::layout::LayoutStyle::Nested),
"binding" => Ok(crate::layout::LayoutStyle::Binding),
"section-binding" => Ok(crate::layout::LayoutStyle::SectionBinding),
"section-root-binding" => Ok(crate::layout::LayoutStyle::SectionRootBinding),
other => Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownVariant(other.to_string()),
}),
}
}
}
#[diagnostic::do_not_recommend]
impl<'doc, T> FromEure<'doc> for Cow<'static, T>
where
T: ToOwned + ?Sized,
T::Owned: FromEure<'doc>,
{
type Error = <T::Owned as FromEure<'doc>>::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
<T::Owned as FromEure<'doc>>::parse(ctx).map(Cow::Owned)
}
}
impl FromEure<'_> for Text {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
return Ok(text.clone());
}
Err(ctx.unexpected_kind(ValueKind::Text))
}
}
impl FromEure<'_> for bool {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
if let PrimitiveValue::Bool(b) = ctx.parse_primitive()? {
return Ok(*b);
}
Err(ctx.unexpected_kind(ValueKind::Bool))
}
}
impl FromEure<'_> for BigInt {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
if let PrimitiveValue::Integer(i) = ctx.parse_primitive()? {
return Ok(i.clone());
}
Err(ctx.unexpected_kind(ValueKind::Integer))
}
}
impl FromEure<'_> for f32 {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
if let PrimitiveValue::F32(f) = ctx.parse_primitive()? {
return Ok(*f);
}
Err(ctx.unexpected_kind(ValueKind::F32))
}
}
impl FromEure<'_> for f64 {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
match ctx.parse_primitive()? {
PrimitiveValue::F32(f) => Ok(*f as f64),
PrimitiveValue::F64(f) => Ok(*f),
_ => Err(ctx.unexpected_kind(ValueKind::F64)),
}
}
}
macro_rules! impl_from_eure_int {
($($ty:ty),*) => {
$(
impl FromEure<'_> for $ty {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
let value: BigInt = ctx.parse()?;
<$ty>::try_from(&value).map_err(|_| ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::OutOfRange(
format!("value {} out of {} range", value, stringify!($ty)),
),
})
}
}
)*
};
}
impl_from_eure_int!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
);
impl<'doc> FromEure<'doc> for &'doc PrimitiveValue {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
ctx.parse_primitive()
}
}
impl FromEure<'_> for PrimitiveValue {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
ctx.parse::<&PrimitiveValue>().cloned()
}
}
impl FromEure<'_> for Identifier {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
if let PrimitiveValue::Text(text) = ctx.parse_primitive()? {
return text
.content
.parse()
.map_err(ParseErrorKind::InvalidIdentifier)
.map_err(|kind| ParseError {
node_id: ctx.node_id(),
kind,
});
}
Err(ctx.unexpected_kind(ValueKind::Text))
}
}
impl<'doc> FromEure<'doc> for &'doc NodeArray {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
ctx.ensure_no_variant_path()?;
match &ctx.node().content {
NodeValue::Array(array) => Ok(array),
_ => Err(ctx.unexpected_kind(ValueKind::Array)),
}
}
}
#[diagnostic::do_not_recommend]
impl<'doc, M, T> FromEure<'doc, Vec<T>> for Vec<M>
where
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<Vec<T>, Self::Error> {
ctx.ensure_no_variant_path()?;
match &ctx.node().content {
NodeValue::Array(array) => array
.iter()
.map(|item| M::parse(&ctx.at(*item)))
.collect::<Result<Vec<_>, _>>(),
_ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
}
}
}
#[diagnostic::do_not_recommend]
impl<'doc, M, T, const N: usize> FromEure<'doc, [T; N]> for [M; N]
where
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<[T; N], Self::Error> {
ctx.ensure_no_variant_path()?;
match &ctx.node().content {
NodeValue::Array(array) => {
let node_ids: [NodeId; N] = array.try_into_array().ok_or_else(|| ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnexpectedArrayLength {
expected: N,
actual: array.len(),
},
})?;
let mut parsed = Vec::with_capacity(N);
for id in node_ids {
parsed.push(M::parse(&ctx.at(id))?);
}
let parsed: [T; N] = parsed
.try_into()
.unwrap_or_else(|_| unreachable!("length was asserted previously"));
Ok(parsed)
}
_ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
}
}
}
#[diagnostic::do_not_recommend]
impl<'doc, M, T> FromEure<'doc, IndexSet<T>> for IndexSet<M>
where
M: FromEure<'doc, T>,
T: Eq + std::hash::Hash,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<IndexSet<T>, Self::Error> {
ctx.ensure_no_variant_path()?;
match &ctx.node().content {
NodeValue::Array(array) => array
.iter()
.map(|item| M::parse(&ctx.at(*item)))
.collect::<Result<IndexSet<_>, _>>(),
_ => Err(ctx.unexpected_kind(ValueKind::Array).into()),
}
}
}
macro_rules! parse_tuple {
($n:expr, $($var:ident),*) => {
#[diagnostic::do_not_recommend]
impl<'doc, $($var),*, Err> FromEure<'doc> for ($($var),*,)
where $($var: FromEure<'doc, Error = Err>),*,
Err: From<ParseError>,
{
type Error = Err;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
ctx.ensure_no_variant_path()?;
let tuple = match &ctx.node().content {
NodeValue::Tuple(tuple) => tuple,
_ => return Err(ctx.unexpected_kind(ValueKind::Tuple).into()),
};
if tuple.len() != $n {
return Err(ParseError { node_id: ctx.node_id(), kind: ParseErrorKind::UnexpectedTupleLength { expected: $n, actual: tuple.len() } }.into());
}
let mut iter = tuple.iter();
Ok(($($var::parse(&ctx.at(*iter.next().unwrap()))?),*,))
}
}
}
}
parse_tuple!(1, A);
parse_tuple!(2, A, B);
parse_tuple!(3, A, B, C);
parse_tuple!(4, A, B, C, D);
parse_tuple!(5, A, B, C, D, E);
parse_tuple!(6, A, B, C, D, E, F);
parse_tuple!(7, A, B, C, D, E, F, G);
parse_tuple!(8, A, B, C, D, E, F, G, H);
parse_tuple!(9, A, B, C, D, E, F, G, H, I);
parse_tuple!(10, A, B, C, D, E, F, G, H, I, J);
parse_tuple!(11, A, B, C, D, E, F, G, H, I, J, K);
parse_tuple!(12, A, B, C, D, E, F, G, H, I, J, K, L);
parse_tuple!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
parse_tuple!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
parse_tuple!(15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
parse_tuple!(16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
macro_rules! parse_map {
($ctx:ident, $M:ty, $T:ty) => {{
$ctx.ensure_no_variant_path()?;
if $ctx.parser_scope() == Some(ParserScope::Extension) {
let node = $ctx.node();
let flatten_ctx = $ctx.flatten_ctx();
let accessed = flatten_ctx.map(|fc| fc.accessed_set());
node.extensions
.iter()
.filter(|(ident, _)| {
accessed.map_or(true, |a| !a.has_ext(ident))
})
.map(|(ident, &node_id)| {
if let Some(fc) = &flatten_ctx {
fc.add_ext((*ident).clone());
}
Ok((
K::from_extension_ident(ident).map_err(|kind| ParseError {
node_id: $ctx.node_id(),
kind,
})?,
<$M as FromEure<'doc, $T>>::parse(&$ctx.at(node_id))?,
))
})
.collect::<Result<_, _>>()
} else {
let map = match &$ctx.node().content {
NodeValue::Map(map) => map,
_ => {
return Err($ctx.unexpected_kind(ValueKind::Map).into());
}
};
let flatten_ctx = $ctx
.flatten_ctx()
.filter(|fc| fc.scope() == ParserScope::Record);
let accessed = flatten_ctx.map(|fc| fc.accessed_set().clone());
map.iter()
.filter(|(key, _)| {
match &accessed {
Some(acc) => match key {
ObjectKey::String(s) => !acc.has_field(s),
_ => true, },
None => true, }
})
.map(|(key, value)| {
if let Some(fc) = &flatten_ctx {
if let ObjectKey::String(s) = key {
fc.add_field(s);
}
}
Ok((
K::from_object_key(key).map_err(|kind| ParseError {
node_id: $ctx.node_id(),
kind,
})?,
<$M as FromEure<'doc, $T>>::parse(&$ctx.at(*value))?,
))
})
.collect::<Result<_, _>>()
}
}};
}
#[diagnostic::do_not_recommend]
impl<'doc, K, M, T> FromEure<'doc, Map<K, T>> for Map<K, M>
where
K: ParseObjectKey<'doc>,
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<Map<K, T>, Self::Error> {
parse_map!(ctx, M, T)
}
}
#[diagnostic::do_not_recommend]
impl<'doc, K, M, T> FromEure<'doc, BTreeMap<K, T>> for BTreeMap<K, M>
where
K: ParseObjectKey<'doc>,
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<BTreeMap<K, T>, Self::Error> {
parse_map!(ctx, M, T)
}
}
#[diagnostic::do_not_recommend]
impl<'doc, K, M, T> FromEure<'doc, HashMap<K, T>> for HashMap<K, M>
where
K: ParseObjectKey<'doc>,
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<HashMap<K, T>, Self::Error> {
parse_map!(ctx, M, T)
}
}
#[diagnostic::do_not_recommend]
impl<'doc, K, M, T> FromEure<'doc, IndexMap<K, T>> for IndexMap<K, M>
where
K: ParseObjectKey<'doc>,
M: FromEure<'doc, T>,
M::Error: From<ParseError>,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<IndexMap<K, T>, Self::Error> {
parse_map!(ctx, M, T)
}
}
impl FromEure<'_> for regex::Regex {
type Error = ParseError;
fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
let pattern: &str = ctx.parse()?;
regex::Regex::new(pattern).map_err(|e| ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::InvalidPattern {
kind: format!("regex '{}'", pattern),
reason: e.to_string(),
},
})
}
}
#[diagnostic::do_not_recommend]
impl<'doc, M, T> FromEure<'doc, Option<T>> for Option<M>
where
M: FromEure<'doc, T>,
M::Error: UnionParseError,
{
type Error = M::Error;
fn parse(ctx: &ParseContext<'doc>) -> Result<Option<T>, Self::Error> {
ctx.parse_union::<Option<T>, M::Error>()?
.variant("some", (M::parse).map(Some))
.variant("none", |ctx: &ParseContext<'_>| {
if ctx.is_null() {
Ok(None)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::TypeMismatch {
expected: ValueKind::Null,
actual: ctx.node().content.value_kind(),
},
}
.into())
}
})
.parse()
.map_err(unwrap_best_variant_error)
}
}
#[diagnostic::do_not_recommend]
impl<'doc, MT, T, ME, E, Err> FromEure<'doc, Result<T, E>> for Result<MT, ME>
where
MT: FromEure<'doc, T, Error = Err>,
ME: FromEure<'doc, E, Error = Err>,
Err: UnionParseError,
{
type Error = Err;
fn parse(ctx: &ParseContext<'doc>) -> Result<Result<T, E>, Self::Error> {
ctx.parse_union::<Result<T, E>, Self::Error>()?
.variant("ok", (MT::parse).map(Ok))
.variant("err", (ME::parse).map(Err))
.parse()
.map_err(unwrap_best_variant_error)
}
}
#[diagnostic::do_not_recommend]
impl<'doc> FromEure<'doc> for () {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
ctx.parse_tuple()?.finish()
}
}
impl<'doc> FromEure<'doc> for NodeId {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<Self, Self::Error> {
Ok(ctx.node_id())
}
}
pub trait DocumentParser<'doc> {
type Output;
type Error;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error>;
}
pub struct AlwaysParser<T, E>(T, PhantomData<E>);
impl<T, E> AlwaysParser<T, E> {
pub fn new(value: T) -> AlwaysParser<T, E> {
Self(value, PhantomData)
}
}
impl<'doc, T, E> DocumentParser<'doc> for AlwaysParser<T, E>
where
T: Clone,
{
type Output = T;
type Error = E;
fn parse(&mut self, _ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
Ok(self.0.clone())
}
}
impl<'doc, T, F, E> DocumentParser<'doc> for F
where
F: FnMut(&ParseContext<'doc>) -> Result<T, E>,
{
type Output = T;
type Error = E;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
(*self)(ctx)
}
}
pub struct LiteralParser<T>(pub T);
impl<'doc, T, E> DocumentParser<'doc> for LiteralParser<T>
where
T: FromEure<'doc, Error = E> + PartialEq + core::fmt::Debug,
E: From<ParseError>,
{
type Output = T;
type Error = E;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
let value: T = ctx.parse::<T>()?;
if value == self.0 {
Ok(value)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::LiteralMismatch {
expected: format!("{:?}", self.0),
actual: format!("{:?}", value),
},
}
.into())
}
}
}
pub struct VariantLiteralParser(pub &'static str);
impl<'doc> DocumentParser<'doc> for VariantLiteralParser {
type Output = &'static str;
type Error = ParseError;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
let value: &str = ctx.parse()?;
if value == self.0 {
Ok(self.0)
} else {
Err(ParseError {
node_id: ctx.node_id(),
kind: ParseErrorKind::UnknownVariant(value.to_string()),
})
}
}
}
pub struct MapParser<T, F> {
parser: T,
mapper: F,
}
impl<'doc, T, O, F> DocumentParser<'doc> for MapParser<T, F>
where
T: DocumentParser<'doc>,
F: FnMut(T::Output) -> O,
{
type Output = O;
type Error = T::Error;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
self.parser.parse(ctx).map(|value| (self.mapper)(value))
}
}
pub struct AndThenParser<T, F> {
parser: T,
mapper: F,
}
impl<'doc, T, O, F, E> DocumentParser<'doc> for AndThenParser<T, F>
where
T: DocumentParser<'doc, Error = E>,
F: Fn(T::Output) -> Result<O, E>,
{
type Output = O;
type Error = E;
fn parse(&mut self, ctx: &ParseContext<'doc>) -> Result<Self::Output, Self::Error> {
let value = self.parser.parse(ctx)?;
(self.mapper)(value)
}
}
pub trait DocumentParserExt<'doc>: DocumentParser<'doc> + Sized {
fn map<O, F>(self, mapper: F) -> MapParser<Self, F>
where
F: Fn(Self::Output) -> O,
{
MapParser {
parser: self,
mapper,
}
}
fn and_then<O, F>(self, mapper: F) -> AndThenParser<Self, F>
where
F: Fn(Self::Output) -> Result<O, Self::Error>,
{
AndThenParser {
parser: self,
mapper,
}
}
}
impl<'doc, T> DocumentParserExt<'doc> for T where T: DocumentParser<'doc> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::document::node::NodeValue;
use crate::eure;
use crate::identifier::Identifier;
use crate::text::Text;
use crate::value::ObjectKey;
use num_bigint::BigInt;
fn identifier(s: &str) -> Identifier {
s.parse().unwrap()
}
fn create_record_with_variant(
field_name: &str,
value: NodeValue,
variant: &str,
) -> EureDocument {
let mut doc = EureDocument::new();
let root_id = doc.get_root_id();
let field_id = doc
.add_map_child(ObjectKey::String(field_name.to_string()), root_id)
.unwrap()
.node_id;
doc.node_mut(field_id).content = value;
let variant_node_id = doc
.add_extension(identifier("variant"), field_id)
.unwrap()
.node_id;
doc.node_mut(variant_node_id).content =
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(variant.to_string())));
doc
}
#[test]
fn test_option_some_tagged() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
"some",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<i32> = rec.parse_field("value").unwrap();
assert_eq!(value, Some(42));
}
#[test]
fn test_option_none_tagged() {
let doc =
create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<i32> = rec.parse_field("value").unwrap();
assert_eq!(value, None);
}
#[test]
fn test_option_some_untagged() {
let doc = eure!({ value = 42 });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<i32> = rec.parse_field("value").unwrap();
assert_eq!(value, Some(42));
}
#[test]
fn test_option_none_untagged() {
let doc = eure!({ value = null });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<i32> = rec.parse_field("value").unwrap();
assert_eq!(value, None);
}
#[test]
fn test_result_ok_tagged() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
"ok",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Result<i32, String> = rec.parse_field("value").unwrap();
assert_eq!(value, Ok(42));
}
#[test]
fn test_result_err_tagged() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(
"error message".to_string(),
))),
"err",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Result<i32, String> = rec.parse_field("value").unwrap();
assert_eq!(value, Err("error message".to_string()));
}
#[test]
fn test_nested_result_option_ok_some() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
"ok.some",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
assert_eq!(value, Ok(Some(42)));
}
#[test]
fn test_nested_result_option_ok_none() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Null),
"ok.none",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
assert_eq!(value, Ok(None));
}
#[test]
fn test_nested_result_option_err() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext("error".to_string()))),
"err",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Result<Option<i32>, String> = rec.parse_field("value").unwrap();
assert_eq!(value, Err("error".to_string()));
}
#[test]
fn test_deeply_nested_option_option() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(42))),
"some.some",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
assert_eq!(value, Some(Some(42)));
}
#[test]
fn test_deeply_nested_option_none() {
let doc = create_record_with_variant(
"value",
NodeValue::Primitive(PrimitiveValue::Null),
"some.none",
);
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
assert_eq!(value, Some(None));
}
#[test]
fn test_outer_none() {
let doc =
create_record_with_variant("value", NodeValue::Primitive(PrimitiveValue::Null), "none");
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Option<Option<i32>> = rec.parse_field("value").unwrap();
assert_eq!(value, None);
}
#[test]
fn test_flatten_indexmap_marks_fields_as_accessed() {
use indexmap::IndexMap;
let doc = eure!({
name = "test"
foo = "bar"
baz = "qux"
});
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let _name: String = rec.parse_field("name").unwrap();
let extra: IndexMap<String, String> = rec.flatten().parse().unwrap();
assert_eq!(extra.get("foo"), Some(&"bar".to_string()));
assert_eq!(extra.get("baz"), Some(&"qux".to_string()));
rec.deny_unknown_fields().unwrap();
}
#[derive(Debug, PartialEq)]
struct RemoteDuration {
secs: u64,
nanos: u32,
}
struct RemoteDurationDef;
impl<'doc> FromEure<'doc, RemoteDuration> for RemoteDurationDef {
type Error = ParseError;
fn parse(ctx: &ParseContext<'doc>) -> Result<RemoteDuration, Self::Error> {
let rec = ctx.parse_record()?;
let secs: u64 = rec.parse_field("secs")?;
let nanos: u32 = rec.parse_field("nanos")?;
rec.deny_unknown_fields()?;
Ok(RemoteDuration { secs, nanos })
}
}
#[test]
fn test_remote_type_basic_parsing() {
let doc = eure!({ secs = 10, nanos = 500 });
let root_id = doc.get_root_id();
let duration: RemoteDuration = doc.parse_via::<RemoteDurationDef, _>(root_id).unwrap();
assert_eq!(
duration,
RemoteDuration {
secs: 10,
nanos: 500
}
);
}
#[test]
fn test_remote_type_in_option() {
let doc = eure!({ secs = 5, nanos = 0 });
let root_id = doc.get_root_id();
let duration: Option<RemoteDuration> = doc
.parse_via::<Option<RemoteDurationDef>, _>(root_id)
.unwrap();
assert_eq!(duration, Some(RemoteDuration { secs: 5, nanos: 0 }));
}
#[test]
fn test_remote_type_in_option_none() {
let doc = eure!({ = null });
let root_id = doc.get_root_id();
let duration: Option<RemoteDuration> = doc
.parse_via::<Option<RemoteDurationDef>, _>(root_id)
.unwrap();
assert_eq!(duration, None);
}
#[test]
fn test_remote_type_in_vec() {
let doc = eure!({
items[] { secs = 1, nanos = 0 }
items[] { secs = 2, nanos = 100 }
});
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items_ctx = rec.field("items").unwrap();
let durations: Vec<RemoteDuration> =
items_ctx.parse_via::<Vec<RemoteDurationDef>, _>().unwrap();
assert_eq!(
durations,
vec![
RemoteDuration { secs: 1, nanos: 0 },
RemoteDuration {
secs: 2,
nanos: 100
},
]
);
}
#[test]
fn test_remote_type_in_indexmap() {
let doc = eure!({
short { secs = 1, nanos = 0 }
long { secs = 10, nanos = 0 }
});
let root_id = doc.get_root_id();
let durations: IndexMap<String, RemoteDuration> = doc
.parse_via::<IndexMap<String, RemoteDurationDef>, _>(root_id)
.unwrap();
assert_eq!(durations.len(), 2);
assert_eq!(
durations.get("short"),
Some(&RemoteDuration { secs: 1, nanos: 0 })
);
assert_eq!(
durations.get("long"),
Some(&RemoteDuration { secs: 10, nanos: 0 })
);
}
#[test]
fn test_remote_type_in_nested_containers() {
let doc = eure!({
items[] { secs = 1, nanos = 0 }
});
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items_ctx = rec.field("items").unwrap();
let durations: Option<Vec<RemoteDuration>> = items_ctx
.parse_via::<Option<Vec<RemoteDurationDef>>, _>()
.unwrap();
assert_eq!(durations, Some(vec![RemoteDuration { secs: 1, nanos: 0 }]));
}
#[test]
fn test_parse_context_parse_via() {
let doc = eure!({ secs = 42, nanos = 123 });
let root_id = doc.get_root_id();
let ctx = doc.parse_context(root_id);
let duration: RemoteDuration = ctx.parse_via::<RemoteDurationDef, _>().unwrap();
assert_eq!(
duration,
RemoteDuration {
secs: 42,
nanos: 123
}
);
}
#[test]
fn test_array_basic_parsing() {
let doc = eure!({ items = [1, 2, 3] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items: [i32; 3] = rec.parse_field("items").unwrap();
assert_eq!(items, [1, 2, 3]);
}
#[test]
fn test_array_empty() {
let doc = eure!({ items = [] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items: [i32; 0] = rec.parse_field("items").unwrap();
assert_eq!(items, []);
}
#[test]
fn test_array_length_mismatch_too_few() {
let doc = eure!({ items = [1, 2] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let result: Result<[i32; 3], _> = rec.parse_field("items");
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err.kind,
ParseErrorKind::UnexpectedArrayLength {
expected: 3,
actual: 2
}
));
}
#[test]
fn test_array_length_mismatch_too_many() {
let doc = eure!({ items = [1, 2, 3, 4] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let result: Result<[i32; 3], _> = rec.parse_field("items");
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err.kind,
ParseErrorKind::UnexpectedArrayLength {
expected: 3,
actual: 4
}
));
}
#[test]
fn test_array_nested_types() {
let doc = eure!({ items = ["a", "b"] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items: [String; 2] = rec.parse_field("items").unwrap();
assert_eq!(items, ["a".to_string(), "b".to_string()]);
}
#[test]
fn test_array_in_option() {
let doc = eure!({ items = [1, 2, 3] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items: Option<[i32; 3]> = rec.parse_field("items").unwrap();
assert_eq!(items, Some([1, 2, 3]));
}
#[test]
fn test_array_of_arrays() {
let doc = eure!({ matrix = [[1, 2], [3, 4]] });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let matrix: [[i32; 2]; 2] = rec.parse_field("matrix").unwrap();
assert_eq!(matrix, [[1, 2], [3, 4]]);
}
#[test]
fn test_array_remote_type() {
let doc = eure!({
items[] { secs = 1, nanos = 0 }
items[] { secs = 2, nanos = 100 }
});
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let items_ctx = rec.field("items").unwrap();
let durations: [RemoteDuration; 2] =
items_ctx.parse_via::<[RemoteDurationDef; 2], _>().unwrap();
assert_eq!(
durations,
[
RemoteDuration { secs: 1, nanos: 0 },
RemoteDuration {
secs: 2,
nanos: 100
},
]
);
}
#[test]
fn test_cow_static_str_from_eure() {
use alloc::borrow::Cow;
let doc = eure!({ name = "hello" });
let root_id = doc.get_root_id();
let rec = doc.parse_record(root_id).unwrap();
let value: Cow<'static, str> = rec.parse_field("name").unwrap();
assert_eq!(value, Cow::<str>::Owned("hello".to_string()));
}
}