-
Notifications
You must be signed in to change notification settings - Fork 6
Adds data models and builder functions for remaining constraints #238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
040f88e
0ab2607
e6bf8de
b25ec1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::internal_traits::{ | ||
LoaderContext, ValidateInternal, ValidationContext, WriteAsIsl, WriteContext, | ||
}; | ||
use crate::ion_schema_version::Versioned; | ||
use crate::model::constraints::{ConstraintName, ReadConstraint}; | ||
use crate::model::type_argument::TypeArgument; | ||
use crate::model::{TypeDefinitionBuilder, VersionedTypeArgumentList}; | ||
use crate::result::IonSchemaResult; | ||
use crate::{IonSchemaElement, IslVersion, ViolationRecorder}; | ||
use ion_rs::{Element, ValueWriter}; | ||
use std::ops::ControlFlow; | ||
|
||
impl ConstraintName for AnyOf { | ||
const CONSTRAINT_NAME: &'static str = "any_of"; | ||
} | ||
|
||
/// Represents the `any_of` constraint (ref. [ISL 1.0], [ISL 2.0]). | ||
/// | ||
/// [ISL 1.0]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#any_of | ||
/// [ISL 2.0]: https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#any_of | ||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct AnyOf { | ||
type_arguments: Vec<TypeArgument>, | ||
} | ||
|
||
impl AnyOf { | ||
fn new(mut type_arguments: Vec<TypeArgument>) -> Self { | ||
// Sorting the vec allows us to get Bag-like equality semantics. | ||
// TODO: Replace the vec with a HashBag or come up with a less hacky way to sort the type arguments. | ||
type_arguments.sort_by_cached_key(|item| format!("{item:?}")); | ||
|
||
Self { type_arguments } | ||
} | ||
|
||
pub fn type_arguments(&self) -> impl Iterator<Item = &TypeArgument> { | ||
self.type_arguments.iter() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> TypeDefinitionBuilder<V> { | ||
pub fn any_of<T: Into<VersionedTypeArgumentList<V>>>(self, type_arguments: T) -> Self { | ||
let type_arguments = Versioned::into_inner(type_arguments.into()); | ||
self.with_constraint(AnyOf::new(type_arguments).into()) | ||
} | ||
} | ||
|
||
impl ValidateInternal for AnyOf { | ||
fn validate_internal<'top: 'call, 'call, R>( | ||
&'top self, | ||
value: &IonSchemaElement<'top>, | ||
ctx: &ValidationContext, | ||
recorder: &'call mut R, | ||
) -> ControlFlow<()> | ||
where | ||
R: ViolationRecorder<'top>, | ||
{ | ||
todo!("Implement this once the ValidationContext allows looking up a type") | ||
} | ||
} | ||
|
||
impl<V: IslVersion> WriteAsIsl<V> for AnyOf | ||
where | ||
TypeArgument: WriteAsIsl<V>, | ||
{ | ||
fn write_as_isl<W: ValueWriter>( | ||
&self, | ||
writer: W, | ||
ctx: &WriteContext<V>, | ||
) -> IonSchemaResult<()> { | ||
todo!() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> ReadConstraint<V> for AnyOf { | ||
fn read_constraint(ion: &Element, ctx: &LoaderContext<V>) -> IonSchemaResult<Option<Self>> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::isl::isl_type_reference::NullabilityModifier; | ||
use crate::model::{IntoTypeArgument, TypeDefinition, TypeReference}; | ||
use crate::{ISL_1_0, ISL_2_0}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn test_builder() { | ||
let type_ = TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(( | ||
// This is a heterogeneous "list" (really a tuple) of things that can become a type arg. | ||
"foo_type", | ||
TypeDefinitionBuilder::new().build(), | ||
TypeReference::imported("bar.isl", "baz"), | ||
"quux_type".or_null(true), | ||
)) | ||
.build(); | ||
|
||
assert_eq!( | ||
type_.constraints().cloned().collect::<Vec<_>>(), | ||
vec![AnyOf::new(vec![ | ||
TypeArgument::from_type_ref( | ||
NullabilityModifier::Nothing, | ||
TypeReference::from("foo_type") | ||
), | ||
TypeArgument::from_type_def( | ||
NullabilityModifier::Nothing, | ||
TypeDefinition::new(vec![], vec![]) | ||
), | ||
TypeArgument::from_type_ref( | ||
NullabilityModifier::Nothing, | ||
TypeReference::imported("bar.isl", "baz") | ||
), | ||
TypeArgument::from_type_ref( | ||
NullabilityModifier::Nullable, | ||
TypeReference::from("quux_type") | ||
), | ||
],) | ||
Comment on lines
+105
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These explicitly-constructed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could be, but I'm intentionally not using it here so that if there's something wrong with the tuple-of- |
||
.into()] | ||
); | ||
|
||
let type_ = TypeDefinitionBuilder::<ISL_2_0>::new() | ||
.any_of(("foo_type", "quux_type".or_null(true))) | ||
.build(); | ||
|
||
assert_eq!( | ||
type_.constraints().cloned().collect::<Vec<_>>(), | ||
vec![AnyOf::new(vec![ | ||
TypeArgument::from_type_ref( | ||
NullabilityModifier::Nothing, | ||
TypeReference::from("foo_type") | ||
), | ||
TypeArgument::from_type_ref( | ||
NullabilityModifier::NullOr, | ||
TypeReference::from("quux_type") | ||
), | ||
],) | ||
.into()] | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_equality() { | ||
// NOTE: this is checking for _bag_ equivalence. | ||
assert_eq!( | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("a", "b", "c")) | ||
.build(), | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("b", "c", "a")) | ||
.build(), | ||
); | ||
assert_eq!( | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("a", "b", "c", "a", "a")) | ||
.build(), | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("a", "b", "a", "c", "a")) | ||
.build(), | ||
); | ||
assert_ne!( | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("a", "b", "c", "a")) | ||
.build(), | ||
TypeDefinitionBuilder::<ISL_1_0>::new() | ||
.any_of(("a", "b", "c", "b")) | ||
.build(), | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::internal_traits::{ | ||
LoaderContext, ValidateInternal, ValidationContext, WriteAsIsl, WriteContext, | ||
}; | ||
use crate::model::constraints::{ConstraintName, ReadConstraint}; | ||
use crate::model::{IonSchemaRange, TypeDefinitionBuilder}; | ||
use crate::result::IonSchemaResult; | ||
use crate::{IonSchemaElement, IslVersion, ViolationRecorder}; | ||
use ion_rs::{Element, ValueWriter}; | ||
use std::ops::ControlFlow; | ||
|
||
impl ConstraintName for ByteLength { | ||
const CONSTRAINT_NAME: &'static str = "byte_length"; | ||
} | ||
|
||
/// Represents the `byte_length` constraint (ref. [ISL 1.0], [ISL 2.0]). | ||
/// | ||
/// [ISL 1.0]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#byte_length | ||
/// [ISL 2.0]: https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#byte_length | ||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct ByteLength { | ||
range: IonSchemaRange<usize>, | ||
} | ||
|
||
impl ByteLength { | ||
pub(crate) fn new<T: Into<IonSchemaRange<usize>>>(range: T) -> Self { | ||
Self { | ||
range: range.into(), | ||
} | ||
} | ||
|
||
pub fn range(&self) -> IonSchemaRange<usize> { | ||
self.range | ||
} | ||
} | ||
|
||
impl<V: IslVersion> TypeDefinitionBuilder<V> { | ||
pub fn byte_length<T: Into<IonSchemaRange<usize>>>(self, range: T) -> Self { | ||
let constraint = ByteLength::new(range.into()); | ||
self.with_constraint(constraint.into()) | ||
} | ||
} | ||
|
||
impl ValidateInternal for ByteLength { | ||
fn validate_internal<'top: 'call, 'call, R>( | ||
&'top self, | ||
value: &'top IonSchemaElement, | ||
ctx: &ValidationContext, | ||
recorder: &'call mut R, | ||
) -> ControlFlow<()> | ||
where | ||
R: ViolationRecorder<'top>, | ||
{ | ||
todo!() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> WriteAsIsl<V> for ByteLength { | ||
fn write_as_isl<W: ValueWriter>( | ||
&self, | ||
writer: W, | ||
ctx: &WriteContext<V>, | ||
) -> IonSchemaResult<()> { | ||
todo!() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> ReadConstraint<V> for ByteLength { | ||
fn read_constraint(ion: &Element, ctx: &LoaderContext<V>) -> IonSchemaResult<Option<Self>> { | ||
todo!() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::internal_traits::{ | ||
LoaderContext, ValidateInternal, ValidationContext, WriteAsIsl, WriteContext, | ||
}; | ||
use crate::model::constraints::{ConstraintName, ReadConstraint}; | ||
use crate::model::{IonSchemaRange, TypeDefinitionBuilder}; | ||
use crate::result::IonSchemaResult; | ||
use crate::{IonSchemaElement, IslVersion, ViolationRecorder}; | ||
use ion_rs::{Element, ValueWriter}; | ||
use std::ops::ControlFlow; | ||
|
||
impl ConstraintName for CodepointLength { | ||
const CONSTRAINT_NAME: &'static str = "codepoint_length"; | ||
} | ||
|
||
/// Represents the `codepoint_length` constraint (ref. [ISL 1.0], [ISL 2.0]). | ||
/// | ||
/// [ISL 1.0]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#codepoint_length | ||
/// [ISL 2.0]: https://amazon-ion.github.io/ion-schema/docs/isl-2-0/spec#codepoint_length | ||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct CodepointLength { | ||
range: IonSchemaRange<usize>, | ||
} | ||
|
||
impl CodepointLength { | ||
pub(crate) fn new<T: Into<IonSchemaRange<usize>>>(range: T) -> Self { | ||
Self { | ||
range: range.into(), | ||
} | ||
} | ||
|
||
pub fn range(&self) -> IonSchemaRange<usize> { | ||
self.range | ||
} | ||
} | ||
|
||
impl<V: IslVersion> TypeDefinitionBuilder<V> { | ||
pub fn codepoint_length<T: Into<IonSchemaRange<usize>>>(self, range: T) -> Self { | ||
let constraint = CodepointLength::new(range.into()); | ||
self.with_constraint(constraint.into()) | ||
} | ||
} | ||
|
||
impl ValidateInternal for CodepointLength { | ||
fn validate_internal<'top: 'call, 'call, R>( | ||
&'top self, | ||
value: &'top IonSchemaElement, | ||
ctx: &ValidationContext, | ||
recorder: &'call mut R, | ||
) -> ControlFlow<()> | ||
where | ||
R: ViolationRecorder<'top>, | ||
{ | ||
todo!() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> WriteAsIsl<V> for CodepointLength { | ||
fn write_as_isl<W: ValueWriter>( | ||
&self, | ||
writer: W, | ||
ctx: &WriteContext<V>, | ||
) -> IonSchemaResult<()> { | ||
todo!() | ||
} | ||
} | ||
|
||
impl<V: IslVersion> ReadConstraint<V> for CodepointLength { | ||
fn read_constraint(ion: &Element, ctx: &LoaderContext<V>) -> IonSchemaResult<Option<Self>> { | ||
todo!() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is used in a few places, consider making a naive
Bag
type wrapping a sortedVec<TypeArgument>
that you can optimize later.