diff --git a/layouts/src/abs/syntax/layout/intersection.rs b/layouts/src/abs/syntax/layout/intersection.rs index 7d41c75..3e26017 100644 --- a/layouts/src/abs/syntax/layout/intersection.rs +++ b/layouts/src/abs/syntax/layout/intersection.rs @@ -2,7 +2,7 @@ use json_syntax::TryFromJsonObject; use serde::{Deserialize, Serialize}; use crate::{ - abs::syntax::{check_type, Build, BuildError, Context, Error, Scope}, + abs::syntax::{check_type, Build, BuildError, Context, Error, ObjectUnusedEntries, Scope}, layout::LayoutType, Ref, }; @@ -27,11 +27,25 @@ impl TryFromJsonObject for IntersectionLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, IntersectionLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + IntersectionLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: IntersectionLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + }; + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/layout/list.rs b/layouts/src/abs/syntax/layout/list.rs index 3a2f73c..b8f727f 100644 --- a/layouts/src/abs/syntax/layout/list.rs +++ b/layouts/src/abs/syntax/layout/list.rs @@ -6,8 +6,8 @@ use crate::abs::{ self, syntax::{ check_type, expect_object, get_entry, require_entry, require_type, Build, BuildError, - CompactIri, Context, Dataset, Error, ExpectedType, Pattern, Scope, ValueFormatOrLayout, - ValueIntro, + CompactIri, Context, Dataset, Error, ExpectedType, ObjectUnusedEntries, Pattern, Scope, + ValueFormatOrLayout, ValueIntro, }, }; @@ -41,7 +41,7 @@ impl TryFromJsonObject for ListLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - let ty = require_type(object, code_map, offset)?; + let ty = require_type(object, None, code_map, offset)?; match ty.value { OrderedListLayoutType::NAME => { OrderedListLayout::try_from_json_object_at(object, code_map, offset) @@ -118,16 +118,30 @@ impl TryFromJsonObject for OrderedListLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, OrderedListLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + OrderedListLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: OrderedListLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - node: require_entry(object, "node", code_map, offset)?, - head: get_entry(object, "head", code_map, offset)? + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + node: require_entry(object, "node", &mut unused_entries, code_map, offset)?, + head: get_entry(object, "head", &mut unused_entries, code_map, offset)? .unwrap_or_else(Pattern::default_head), - tail: get_entry(object, "tail", code_map, offset)? + tail: get_entry(object, "tail", &mut unused_entries, code_map, offset)? .unwrap_or_else(Pattern::default_tail), - }) + }; + unused_entries.check()?; + Ok(result) } } @@ -271,13 +285,19 @@ impl TryFromJsonObject for ListNode { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - head: get_entry(object, "head", code_map, offset)?.unwrap_or_else(Self::default_head), - rest: get_entry(object, "rest", code_map, offset)?.unwrap_or_else(Self::default_rest), - intro: get_entry(object, "intro", code_map, offset)?.unwrap_or_default(), - value: require_entry(object, "value", code_map, offset)?, - dataset: get_entry(object, "dataset", code_map, offset)?, - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + head: get_entry(object, "head", &mut unused_entries, code_map, offset)? + .unwrap_or_else(Self::default_head), + rest: get_entry(object, "rest", &mut unused_entries, code_map, offset)? + .unwrap_or_else(Self::default_rest), + intro: get_entry(object, "intro", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + value: require_entry(object, "value", &mut unused_entries, code_map, offset)?, + dataset: get_entry(object, "dataset", &mut unused_entries, code_map, offset)?, + }; + unused_entries.check()?; + Ok(result) } } @@ -359,12 +379,26 @@ impl TryFromJsonObject for UnorderedListLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, UnorderedListLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + UnorderedListLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: UnorderedListLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - item: require_entry(object, "item", code_map, offset)?, - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + item: require_entry(object, "item", &mut unused_entries, code_map, offset)?, + }; + unused_entries.check()?; + Ok(result) } } @@ -404,12 +438,17 @@ impl TryFromJsonObject for ListItem { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - intro: get_entry(object, "intro", code_map, offset)?.unwrap_or_default(), - value: require_entry(object, "value", code_map, offset)?, - dataset: get_entry(object, "dataset", code_map, offset)?.unwrap_or_default(), - property: get_entry(object, "property", code_map, offset)?, - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + intro: get_entry(object, "intro", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + value: require_entry(object, "value", &mut unused_entries, code_map, offset)?, + dataset: get_entry(object, "dataset", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + property: get_entry(object, "property", &mut unused_entries, code_map, offset)?, + }; + unused_entries.check()?; + Ok(result) } } @@ -504,12 +543,27 @@ impl TryFromJsonObject for SizedListLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, SizedListLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + SizedListLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: SizedListLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - items: get_entry(object, "items", code_map, offset)?.unwrap_or_default(), - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + items: get_entry(object, "items", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/layout/literal.rs b/layouts/src/abs/syntax/layout/literal.rs index 451dbb2..4d053d5 100644 --- a/layouts/src/abs/syntax/layout/literal.rs +++ b/layouts/src/abs/syntax/layout/literal.rs @@ -6,7 +6,7 @@ use crate::{ self, syntax::{ check_type, expect_object, get_entry, require_entry, require_type, Build, BuildError, - CompactIri, Context, Error, ExpectedType, Pattern, Scope, + CompactIri, Context, Error, ExpectedType, ObjectUnusedEntries, Pattern, Scope, }, RegExp, }, @@ -42,7 +42,7 @@ impl TryFromJsonObject for LiteralLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - let ty = require_type(object, code_map, offset)?; + let ty = require_type(object, None, code_map, offset)?; match ty.value { IdLayoutType::NAME => { IdLayout::try_from_json_object_at(object, code_map, offset).map(Self::Id) @@ -127,7 +127,7 @@ impl TryFromJsonObject for DataLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - let ty = require_type(object, code_map, offset)?; + let ty = require_type(object, None, code_map, offset)?; match ty.value { UnitLayoutType::NAME => { UnitLayout::try_from_json_object_at(object, code_map, offset).map(Self::Unit) @@ -205,10 +205,19 @@ impl TryFromJsonObject for UnitLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, UnitLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let const_ = get_entry(object, "const", code_map, offset)?.unwrap_or_default(); - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + UnitLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let const_ = + get_entry(object, "const", &mut unused_entries, code_map, offset)?.unwrap_or_default(); + unused_entries.check()?; Ok(Self { type_: UnitLayoutType, header, @@ -306,11 +315,19 @@ impl TryFromJsonObject for BooleanLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, BooleanLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let resource = get_entry(object, "resource", code_map, offset)?; - let datatype = get_entry(object, "datatype", code_map, offset)?; - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + BooleanLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let resource = get_entry(object, "resource", &mut unused_entries, code_map, offset)?; + let datatype = get_entry(object, "datatype", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; Ok(Self { type_: BooleanLayoutType, header, @@ -367,11 +384,19 @@ impl TryFromJsonObject for NumberLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, NumberLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let resource = get_entry(object, "resource", code_map, offset)?; - let datatype = require_entry(object, "datatype", code_map, offset)?; - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + NumberLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let resource = get_entry(object, "resource", &mut unused_entries, code_map, offset)?; + let datatype = require_entry(object, "datatype", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; Ok(Self { type_: NumberLayoutType, header, @@ -428,11 +453,19 @@ impl TryFromJsonObject for ByteStringLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, ByteStringLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let resource = get_entry(object, "resource", code_map, offset)?; - let datatype = require_entry(object, "datatype", code_map, offset)?; - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + ByteStringLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let resource = get_entry(object, "resource", &mut unused_entries, code_map, offset)?; + let datatype = require_entry(object, "datatype", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; Ok(Self { type_: ByteStringLayoutType, header, @@ -468,12 +501,20 @@ impl TryFromJsonObject for TextStringLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, TextStringLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let pattern = get_entry(object, "pattern", code_map, offset)?; - let resource = get_entry(object, "resource", code_map, offset)?; - let datatype = get_entry(object, "datatype", code_map, offset)?; - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + TextStringLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let pattern = get_entry(object, "pattern", &mut unused_entries, code_map, offset)?; + let resource = get_entry(object, "resource", &mut unused_entries, code_map, offset)?; + let datatype = get_entry(object, "datatype", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; Ok(Self { type_: TextStringLayoutType, header, @@ -538,11 +579,19 @@ impl TryFromJsonObject for IdLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, IdLayoutType::NAME, code_map, offset)?; - let header = LayoutHeader::try_from_json_object_at(object, code_map, offset)?; - let pattern = get_entry(object, "pattern", code_map, offset)?; - let resource = get_entry(object, "resource", code_map, offset)?; - + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + IdLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let header = + LayoutHeader::try_from_json_object_at(object, &mut unused_entries, code_map, offset)?; + let pattern = get_entry(object, "pattern", &mut unused_entries, code_map, offset)?; + let resource = get_entry(object, "resource", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; Ok(Self { type_: IdLayoutType, header, diff --git a/layouts/src/abs/syntax/layout/mod.rs b/layouts/src/abs/syntax/layout/mod.rs index 9882e58..8908a05 100644 --- a/layouts/src/abs/syntax/layout/mod.rs +++ b/layouts/src/abs/syntax/layout/mod.rs @@ -31,7 +31,8 @@ use crate::{ use super::{ get_entry, require_type, Build, BuildError, CompactIri, Context, Dataset, Error, ExpectedType, - InvalidCompactIri, OneOrMany, Pattern, Resource, Scope, ValueFormat, VariableName, + InvalidCompactIri, ObjectUnusedEntries, OneOrMany, Pattern, Resource, Scope, ValueFormat, + VariableName, }; /// Abstract syntax layout. @@ -175,7 +176,7 @@ impl TryFromJsonObject for Layout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - let ty = require_type(object, code_map, offset)?; + let ty = require_type(object, None, code_map, offset)?; match ty.value { IdLayoutType::NAME | UnitLayoutType::NAME @@ -413,17 +414,23 @@ pub struct LayoutHeader { impl LayoutHeader { fn try_from_json_object_at( object: &json_syntax::Object, + unused_entries: &mut ObjectUnusedEntries, code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { Ok(Self { - base: get_entry(object, "base", code_map, offset)?, - prefixes: get_entry(object, "prefixes", code_map, offset)?.unwrap_or_default(), - id: get_entry(object, "id", code_map, offset)?, - input: get_entry(object, "input", code_map, offset)?.unwrap_or_default(), - intro: get_entry(object, "intro", code_map, offset)?.unwrap_or_default(), - dataset: get_entry(object, "dataset", code_map, offset)?.unwrap_or_default(), - extra: get_entry(object, "extra", code_map, offset)?.unwrap_or_default(), + base: get_entry(object, "base", unused_entries, code_map, offset)?, + prefixes: get_entry(object, "prefixes", unused_entries, code_map, offset)? + .unwrap_or_default(), + id: get_entry(object, "id", unused_entries, code_map, offset)?, + input: get_entry(object, "input", unused_entries, code_map, offset)? + .unwrap_or_default(), + intro: get_entry(object, "intro", unused_entries, code_map, offset)? + .unwrap_or_default(), + dataset: get_entry(object, "dataset", unused_entries, code_map, offset)? + .unwrap_or_default(), + extra: get_entry(object, "extra", unused_entries, code_map, offset)? + .unwrap_or_default(), }) } } diff --git a/layouts/src/abs/syntax/layout/product.rs b/layouts/src/abs/syntax/layout/product.rs index f0a75b8..fa0ddc0 100644 --- a/layouts/src/abs/syntax/layout/product.rs +++ b/layouts/src/abs/syntax/layout/product.rs @@ -7,7 +7,7 @@ use crate::abs::{ self, syntax::{ check_type, expect_object, get_entry, require_entry, Build, BuildError, Context, Dataset, - Error, Pattern, Scope, ValueFormatOrLayout, ValueIntro, + Error, ObjectUnusedEntries, Pattern, Scope, ValueFormatOrLayout, ValueIntro, }, }; @@ -47,12 +47,27 @@ impl TryFromJsonObject for ProductLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, ProductLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + ProductLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: ProductLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - fields: get_entry(object, "fields", code_map, offset)?.unwrap_or_default(), - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + fields: get_entry(object, "fields", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } @@ -149,12 +164,18 @@ impl TryFromJsonObject for Field { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - intro: get_entry(object, "intro", code_map, offset)?.unwrap_or_default(), - value: require_entry(object, "value", code_map, offset)?, - dataset: get_entry(object, "dataset", code_map, offset)?.unwrap_or_default(), - property: get_entry(object, "property", code_map, offset)?, - required: get_entry(object, "required", code_map, offset)?.unwrap_or_default(), - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + intro: get_entry(object, "intro", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + value: require_entry(object, "value", &mut unused_entries, code_map, offset)?, + dataset: get_entry(object, "dataset", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + property: get_entry(object, "property", &mut unused_entries, code_map, offset)?, + required: get_entry(object, "required", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/layout/sum.rs b/layouts/src/abs/syntax/layout/sum.rs index c117f11..a24085a 100644 --- a/layouts/src/abs/syntax/layout/sum.rs +++ b/layouts/src/abs/syntax/layout/sum.rs @@ -7,7 +7,7 @@ use crate::abs::{ self, syntax::{ check_type, expect_object, get_entry, require_entry, Build, BuildError, Context, Dataset, - Error, OneOrMany, Pattern, Scope, VariableName, + Error, ObjectUnusedEntries, OneOrMany, Pattern, Scope, VariableName, }, }; @@ -47,12 +47,27 @@ impl TryFromJsonObject for SumLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, SumLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + SumLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: SumLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - variants: get_entry(object, "variants", code_map, offset)?.unwrap_or_default(), - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + variants: get_entry(object, "variants", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } @@ -89,11 +104,16 @@ impl TryFromJsonObject for Variant { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - intro: get_entry(object, "intro", code_map, offset)?.unwrap_or_default(), - value: require_entry(object, "value", code_map, offset)?, - dataset: get_entry(object, "dataset", code_map, offset)?.unwrap_or_default(), - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + intro: get_entry(object, "intro", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + value: require_entry(object, "value", &mut unused_entries, code_map, offset)?, + dataset: get_entry(object, "dataset", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } @@ -214,11 +234,16 @@ impl TryFromJsonObject for VariantFormat { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - layout: require_entry(object, "layout", code_map, offset)?, - input: get_entry(object, "input", code_map, offset)?.unwrap_or_default(), - graph: get_entry(object, "graph", code_map, offset)?.unwrap_or_default(), - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + layout: require_entry(object, "layout", &mut unused_entries, code_map, offset)?, + input: get_entry(object, "input", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + graph: get_entry(object, "graph", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/layout/union.rs b/layouts/src/abs/syntax/layout/union.rs index 8108c54..a1a5a2c 100644 --- a/layouts/src/abs/syntax/layout/union.rs +++ b/layouts/src/abs/syntax/layout/union.rs @@ -2,7 +2,7 @@ use json_syntax::TryFromJsonObject; use serde::{Deserialize, Serialize}; use crate::{ - abs::syntax::{check_type, Build, BuildError, Context, Error, Scope}, + abs::syntax::{check_type, Build, BuildError, Context, Error, ObjectUnusedEntries, Scope}, layout::LayoutType, Ref, }; @@ -27,11 +27,25 @@ impl TryFromJsonObject for UnionLayout { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - check_type(object, UnionLayoutType::NAME, code_map, offset)?; - Ok(Self { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + check_type( + object, + UnionLayoutType::NAME, + &mut unused_entries, + code_map, + offset, + )?; + let result = Self { type_: UnionLayoutType, - header: LayoutHeader::try_from_json_object_at(object, code_map, offset)?, - }) + header: LayoutHeader::try_from_json_object_at( + object, + &mut unused_entries, + code_map, + offset, + )?, + }; + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/mod.rs b/layouts/src/abs/syntax/mod.rs index 03f315f..1fcbed6 100644 --- a/layouts/src/abs/syntax/mod.rs +++ b/layouts/src/abs/syntax/mod.rs @@ -36,6 +36,9 @@ pub enum Error { other_offset: usize, }, + #[error("unexpected entry `{key}`")] + UnexpectedEntry { offset: usize, key: String }, + #[error("missing `type` entry")] MissingType(usize), @@ -74,12 +77,14 @@ pub enum Error { impl Error { pub fn duplicate<'a>( key: &str, - ) -> impl '_ + FnOnce(json_syntax::object::Duplicate>) -> Self - { + ) -> impl '_ + + FnOnce( + json_syntax::object::Duplicate>, + ) -> Self { move |e| Self::DuplicateEntry { - offset: e.0.value.key.offset, + offset: e.0 .1.value.key.offset, key: key.to_owned(), - other_offset: e.1.value.key.offset, + other_offset: e.1 .1.value.key.offset, } } @@ -88,6 +93,7 @@ impl Error { Self::Unexpected { offset, .. } => *offset, Self::MissingRequiredEntry { offset, .. } => *offset, Self::DuplicateEntry { offset, .. } => *offset, + Self::UnexpectedEntry { offset, .. } => *offset, Self::MissingType(offset) => *offset, Self::InvalidType { offset, .. } => *offset, Self::InvalidRegex(offset, _) => *offset, @@ -235,9 +241,46 @@ pub(crate) fn expect_string(json: &json_syntax::Value, offset: usize) -> Result< } } +pub(crate) struct ObjectUnusedEntries<'a> { + object: &'a json_syntax::Object, + entries: Vec<(usize, bool)>, +} + +impl<'a> ObjectUnusedEntries<'a> { + pub fn new( + object: &'a json_syntax::Object, + code_map: &json_syntax::CodeMap, + offset: usize, + ) -> Self { + let mut entries = Vec::with_capacity(object.len()); + for e in object.iter_mapped(code_map, offset) { + entries.push((e.value.key.offset, true)) + } + Self { object, entries } + } + + pub fn remove(&mut self, index: usize) { + self.entries[index].1 = false; + } + + pub fn check(self) -> Result<(), Error> { + for (i, (offset, unused)) in self.entries.into_iter().enumerate() { + if unused { + return Err(Error::UnexpectedEntry { + offset, + key: self.object.entries()[i].key.to_string(), + }); + } + } + + Ok(()) + } +} + pub(crate) fn get_entry( object: &json_syntax::Object, key: &str, + unused_entries: &mut ObjectUnusedEntries, code_map: &json_syntax::CodeMap, offset: usize, ) -> Result, Error> @@ -245,11 +288,12 @@ where T::Error: Into, { let entry = object - .get_unique_mapped_entry(code_map, offset, key) + .get_unique_mapped_entry_with_index(code_map, offset, key) .map_err(Error::duplicate(key))?; match entry { - Some(entry) => { + Some((i, entry)) => { + unused_entries.remove(i); let t = T::try_from_json_at(entry.value.value.value, code_map, entry.value.value.offset) .map_err(Into::into)?; @@ -262,6 +306,7 @@ where pub(crate) fn require_entry( object: &json_syntax::Object, key: &str, + unused_entries: &mut ObjectUnusedEntries, code_map: &json_syntax::CodeMap, offset: usize, ) -> Result @@ -269,11 +314,12 @@ where T::Error: Into, { let entry = object - .get_unique_mapped_entry(code_map, offset, key) + .get_unique_mapped_entry_with_index(code_map, offset, key) .map_err(Error::duplicate(key))?; match entry { - Some(entry) => { + Some((i, entry)) => { + unused_entries.remove(i); T::try_from_json_at(entry.value.value.value, code_map, entry.value.value.offset) .map_err(Into::into) } @@ -286,13 +332,17 @@ where pub(crate) fn require_type<'a>( object: &'a json_syntax::Object, + unused_entries: Option<&mut ObjectUnusedEntries>, code_map: &json_syntax::CodeMap, offset: usize, ) -> Result, Error> { - let entry = object - .get_unique_mapped_entry(code_map, offset, "type") + let (i, entry) = object + .get_unique_mapped_entry_with_index(code_map, offset, "type") .map_err(Error::duplicate("type"))? .ok_or(Error::MissingType(offset))?; + if let Some(unused_entries) = unused_entries { + unused_entries.remove(i); + } match entry.value.value.value { json_syntax::Value::String(found) => Ok(json_syntax::code_map::Mapped::new( @@ -310,10 +360,11 @@ pub(crate) fn require_type<'a>( pub(crate) fn check_type( object: &json_syntax::Object, expected: &'static str, + unused_entries: &mut ObjectUnusedEntries, code_map: &json_syntax::CodeMap, offset: usize, ) -> Result<(), Error> { - let found = require_type(object, code_map, offset)?; + let found = require_type(object, Some(unused_entries), code_map, offset)?; if found.value == expected { Ok(()) } else { @@ -464,11 +515,17 @@ impl TryFromJsonObject for ValueFormat { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - layout: require_entry(object, "layout", code_map, offset)?, - input: get_entry(object, "input", code_map, offset)?.unwrap_or_default(), - graph: get_entry(object, "graph", code_map, offset)?.unwrap_or_default(), - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + layout: require_entry(object, "layout", &mut unused_entries, code_map, offset)?, + input: get_entry(object, "input", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + graph: get_entry(object, "graph", &mut unused_entries, code_map, offset)? + .unwrap_or_default(), + }; + + unused_entries.check()?; + Ok(result) } } diff --git a/layouts/src/abs/syntax/pattern/literal.rs b/layouts/src/abs/syntax/pattern/literal.rs index ecfa932..b7e967f 100644 --- a/layouts/src/abs/syntax/pattern/literal.rs +++ b/layouts/src/abs/syntax/pattern/literal.rs @@ -2,7 +2,7 @@ use iref::IriBuf; use langtag::LangTagBuf; use rdf_types::XSD_STRING; -use crate::abs::syntax::{get_entry, require_entry, BuildError, Error, Scope}; +use crate::abs::syntax::{get_entry, require_entry, BuildError, Error, ObjectUnusedEntries, Scope}; use super::CompactIri; @@ -22,13 +22,15 @@ impl LiteralValue { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - let type_ = match get_entry(object, "type", code_map, offset)? { + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let type_ = match get_entry(object, "type", &mut unused_entries, code_map, offset)? { Some(ty) => { // TODO check if language is present. LiteralType::Iri(LiteralTypeIri { type_: ty }) } None => { - let language: String = require_entry(object, "language", code_map, offset)?; + let language: String = + require_entry(object, "language", &mut unused_entries, code_map, offset)?; match LangTagBuf::new(language) { Ok(language) => LiteralType::Language(LiteralTypeLanguage { language }), Err(e) => return Err(Error::InvalidLangTag(offset, e.0)), @@ -36,10 +38,10 @@ impl LiteralValue { } }; - Ok(Self { - value: require_entry(object, "value", code_map, offset)?, - type_, - }) + let value = require_entry(object, "value", &mut unused_entries, code_map, offset)?; + unused_entries.check()?; + + Ok(Self { value, type_ }) } } diff --git a/layouts/src/abs/syntax/resource.rs b/layouts/src/abs/syntax/resource.rs index 63608ec..dc85340 100644 --- a/layouts/src/abs/syntax/resource.rs +++ b/layouts/src/abs/syntax/resource.rs @@ -3,7 +3,9 @@ use rdf_types::LexicalLiteralTypeRef; use serde::{Deserialize, Serialize}; use xsd_types::{XSD_BOOLEAN, XSD_STRING}; -use super::{require_entry, Build, BuildError, CompactIri, Context, Error, Scope}; +use super::{ + require_entry, Build, BuildError, CompactIri, Context, Error, ObjectUnusedEntries, Scope, +}; /// RDF Resource description. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] @@ -102,10 +104,13 @@ impl TypedString { code_map: &json_syntax::CodeMap, offset: usize, ) -> Result { - Ok(Self { - value: require_entry(object, "value", code_map, offset)?, - type_: require_entry(object, "type", code_map, offset)?, - }) + let mut unused_entries = ObjectUnusedEntries::new(object, code_map, offset); + let result = Self { + value: require_entry(object, "value", &mut unused_entries, code_map, offset)?, + type_: require_entry(object, "type", &mut unused_entries, code_map, offset)?, + }; + unused_entries.check()?; + Ok(result) } }