Skip to content
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

adds ion-schema-tests and related fixes for ISL 2.0 #200

Merged
merged 9 commits into from
Sep 26, 2023
2 changes: 1 addition & 1 deletion ion-schema-tests-runner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ struct MacroArgs {
///
/// ion_schema_tests!(
/// // The root directory of a test suite.
/// root = "../ion-schema-tests/ion_schema_1_0/",
/// root = "ion-schema-tests/ion_schema_1_0/",
/// // Optional, a list of tests to mark as `#[ignore]`.
/// // Strings can be test names or regexes that match test names.
/// ignored(
Expand Down
9 changes: 0 additions & 9 deletions ion-schema-tests-runner/tests/ion-schema-tests-2-0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,8 @@ ion_schema_tests!(
// Support for ISL 2.0 is not completely implemented yet, so some tests are ignored.
ignored(
"imports",
"schema::*",
"null_or::*",
"constraints::contains",
"constraints::ordered_elements",
"constraints::precision",
"constraints::regex::value_should_be_invalid_for_type_regex_unescaped_newline__2_", // https://github.com/amazon-ion/ion-rust/issues/399
"constraints::timestamp_precision",
// following tests are related to: https://github.com/amazon-ion/ion-rust/pull/553
"constraints::valid_values_ranges::value_should_be_valid_for_type_valid_values_range_timestamp_known_offset__12_",
"constraints::valid_values_ranges::value_should_be_valid_for_type_valid_values_range_timestamp_known_offset__13_",
"constraints::valid_values_ranges::value_should_be_valid_for_type_valid_values_range_timestamp_unknown_offset__12_",
"constraints::valid_values_ranges::value_should_be_valid_for_type_valid_values_range_timestamp_unknown_offset__13_"
)
);
14 changes: 10 additions & 4 deletions ion-schema/src/constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1175,14 +1175,21 @@ impl ConstraintValidator for ContainsConstraint {
// Create a peekable iterator for given sequence
let values: Vec<Element> = match &value {
IonSchemaElement::SingleElement(element) => {
match element.as_sequence() {
None => {
match element.value() {
Value::List(ion_sequence) | Value::SExp(ion_sequence) => {
ion_sequence.elements().map(|a| a.to_owned()).collect()
}
Value::Struct(ion_struct) => ion_struct
.fields()
.map(|(name, value)| value.to_owned())
.collect(),
_ => {
// return Violation if value is not an Ion sequence
return Err(Violation::new(
"contains",
ViolationCode::TypeMismatched,
&format!(
"expected list/sexp found {}",
"expected list/sexp/struct/document found {}",
if element.is_null() {
format!("{element}")
} else {
Expand All @@ -1192,7 +1199,6 @@ impl ConstraintValidator for ContainsConstraint {
ion_path,
));
}
Some(ion_sequence) => ion_sequence.elements().map(|a| a.to_owned()).collect(),
}
}
IonSchemaElement::Document(document) => document.to_owned(),
Expand Down
4 changes: 4 additions & 0 deletions ion-schema/src/isl/isl_constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,10 @@ impl IslConstraintImpl {
));
}

if !value.annotations().is_empty() {
return invalid_schema_error("contains list can not have any annotations");
}

let values: Vec<Element> = value
.as_sequence()
.unwrap()
Expand Down
9 changes: 8 additions & 1 deletion ion-schema/src/isl/isl_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,14 @@ impl IslTypeImpl {
"type names must be a symbol with defined text",
))
}
Some(name) => Some(name.to_owned()),
Some(name) => {
if !name_element.annotations().is_empty() {
return Err(invalid_schema_error_raw(
"type names must be a non null and unannotated symbol with defined text",
));
}
Some(name.to_owned())
}
},
None => {
return Err(invalid_schema_error_raw(
Expand Down
4 changes: 3 additions & 1 deletion ion-schema/src/isl/isl_type_reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,9 @@ impl IslTypeRefImpl {
}
IslTypeRefImpl::TypeImport(isl_import_type, type_ref_modifier) => {
// verify if the inline import type already exists in the type_store
match type_store.get_type_id_by_name(isl_import_type.type_name()) {
match type_store
.get_defined_type_id_or_imported_type_id_by_name(isl_import_type.type_name())
{
None => unresolvable_schema_error(format!(
"inline import type: {} does not exists",
isl_import_type.type_name()
Expand Down
19 changes: 18 additions & 1 deletion ion-schema/src/isl/ranges.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,24 @@ pub type TimestampRange = base::Range<Timestamp>;
impl RangeValidation<Timestamp> for TimestampRange {}

pub type TimestampPrecisionRange = base::Range<TimestampPrecision>;
impl RangeValidation<TimestampPrecision> for TimestampPrecisionRange {}
impl RangeValidation<TimestampPrecision> for TimestampPrecisionRange {
fn is_empty(start: &Limit<TimestampPrecision>, end: &Limit<TimestampPrecision>) -> bool {
match (start, end) {
(Limit::Inclusive(lower), Limit::Inclusive(upper)) => lower > upper,
(Limit::Exclusive(lower), Limit::Inclusive(upper))
| (Limit::Inclusive(lower), Limit::Exclusive(upper)) => lower >= upper,
(Limit::Exclusive(lower), Limit::Exclusive(upper)) => {
let start_value = lower.int_value();
let end_value = upper.int_value();

// Checking for e.g. range::[exclusive::1, exclusive::2] which is empty.
let adjusted_lower = start_value + 1;
adjusted_lower >= end_value
}
_ => false,
}
}
}

// usize does not implement Into<Element>
// TODO: Remove after https://github.com/amazon-ion/ion-rust/issues/573 is released
Expand Down
43 changes: 18 additions & 25 deletions ion-schema/src/isl/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,21 @@ impl TimestampPrecision {
TimestampPrecision::OtherFractionalSeconds(i) => format!("fractional second (10e{i})"),
}
}

pub(crate) fn int_value(&self) -> i64 {
use TimestampPrecision::*;
match self {
Year => -4,
Month => -3,
Day => -2,
Minute => -1,
Second => 0,
Millisecond => 3,
Microsecond => 6,
Nanosecond => 9,
OtherFractionalSeconds(scale) => *scale,
}
}
}

impl TryFrom<&str> for TimestampPrecision {
Expand All @@ -129,7 +144,7 @@ impl TryFrom<&str> for TimestampPrecision {
"year" => TimestampPrecision::Year,
"month" => TimestampPrecision::Month,
"day" => TimestampPrecision::Day,
"minute" | "hour" => TimestampPrecision::Minute,
"minute" => TimestampPrecision::Minute,
"second" => TimestampPrecision::Second,
"millisecond" => TimestampPrecision::Millisecond,
"microsecond" => TimestampPrecision::Microsecond,
Expand All @@ -145,30 +160,8 @@ impl TryFrom<&str> for TimestampPrecision {

impl PartialOrd for TimestampPrecision {
fn partial_cmp(&self, other: &TimestampPrecision) -> Option<Ordering> {
use TimestampPrecision::*;
let self_value = match self {
Year => -4,
Month => -3,
Day => -2,
Minute => -1,
Second => 0,
Millisecond => 3,
Microsecond => 6,
Nanosecond => 9,
OtherFractionalSeconds(scale) => *scale,
};

let other_value = match other {
Year => -4,
Month => -3,
Day => -2,
Minute => -1,
Second => 0,
Millisecond => 3,
Microsecond => 6,
Nanosecond => 9,
OtherFractionalSeconds(scale) => *scale,
};
let self_value = self.int_value();
let other_value = other.int_value();

Some(self_value.cmp(&other_value))
}
Expand Down
Loading
Loading