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 implementation for WriteToIsl #183

Merged
merged 12 commits into from
Jun 15, 2023
212 changes: 211 additions & 1 deletion ion-schema/src/isl/isl_constraint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use crate::isl::isl_range::{Range, RangeType};
use crate::isl::isl_type_reference::{IslTypeRefImpl, IslVariablyOccurringTypeRef};
use crate::isl::util::{Annotation, Ieee754InterchangeFormat, TimestampOffset, ValidValue};
use crate::isl::IslVersion;
use crate::isl::WriteToIsl;
use crate::result::{invalid_schema_error, invalid_schema_error_raw, IonSchemaResult};
use ion_rs::element::writer::ElementWriter;
use ion_rs::element::Element;
use ion_rs::IonType;
use ion_rs::{IonType, IonWriter};
use std::collections::HashMap;
use std::convert::TryInto;

Expand Down Expand Up @@ -1055,6 +1057,149 @@ impl IslConstraintImpl {
}
}

impl WriteToIsl for IslConstraintImpl {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
match self {
IslConstraintImpl::AllOf(type_refs) => {
writer.set_field_name("all_of");
writer.step_in(IonType::List)?;
for type_ref in type_refs {
type_ref.write_to(writer)?;
}
writer.step_out()?;
}
IslConstraintImpl::Annotations(annotations) => {
annotations.write_to(writer)?;
}
IslConstraintImpl::AnyOf(type_refs) => {
writer.set_field_name("any_of");
writer.step_in(IonType::List)?;
for type_ref in type_refs {
type_ref.write_to(writer)?;
}
writer.step_out()?;
}
IslConstraintImpl::ByteLength(range) => {
writer.set_field_name("byte_length");
range.write_to(writer)?;
}
IslConstraintImpl::CodepointLength(range) => {
writer.set_field_name("codepoint_length");
range.write_to(writer)?;
}
IslConstraintImpl::Contains(elements) => {
writer.set_field_name("contains");
writer.step_in(IonType::List)?;
for element in elements {
writer.write_element(element)?;
}
writer.step_out()?;
}
IslConstraintImpl::ContentClosed => {
writer.set_field_name("content");
writer.write_symbol("closed")?;
}
IslConstraintImpl::ContainerLength(range) => {
writer.set_field_name("container_length");
range.write_to(writer)?;
}
IslConstraintImpl::Element(type_ref, is_distinct) => {
writer.set_field_name("element");
if *is_distinct {
writer.set_annotations(["distinct"]);
}
type_ref.write_to(writer)?;
}
IslConstraintImpl::Exponent(range) => {
writer.set_field_name("exponent");
range.write_to(writer)?;
}
IslConstraintImpl::Fields(fields, content_closed) => {
writer.set_field_name("fields");
if *content_closed {
writer.set_annotations(["closed"]);
}
writer.step_in(IonType::Struct)?;
for (field_name, type_ref) in fields.iter() {
writer.set_field_name(field_name);
type_ref.write_to(writer)?;
}
writer.step_out()?;
}
IslConstraintImpl::FieldNames(type_ref, is_distinct) => {
writer.set_field_name("field_names");
if *is_distinct {
writer.set_annotations(["distinct"]);
}
type_ref.write_to(writer)?;
}
IslConstraintImpl::Ieee754Float(format) => {
writer.set_field_name("ieee754_float");
format.write_to(writer)?;
}
IslConstraintImpl::Not(type_ref) => {
writer.set_field_name("not");
type_ref.write_to(writer)?;
}
IslConstraintImpl::OneOf(type_refs) => {
writer.set_field_name("one_of");
writer.step_in(IonType::List)?;
for type_ref in type_refs {
type_ref.write_to(writer)?;
}
writer.step_out()?;
}
IslConstraintImpl::OrderedElements(type_refs) => {
writer.set_field_name("ordered_elements");
writer.step_in(IonType::List)?;
for type_ref in type_refs {
type_ref.write_to(writer)?;
}
writer.step_out()?;
}
IslConstraintImpl::Precision(range) => {
writer.set_field_name("precision");
range.write_to(writer)?;
}
IslConstraintImpl::Regex(regex) => {
regex.write_to(writer)?;
}
IslConstraintImpl::Scale(range) => {
writer.set_field_name("scale");
range.write_to(writer)?;
}
IslConstraintImpl::TimestampOffset(timestamp_offset) => {
timestamp_offset.write_to(writer)?;
}
IslConstraintImpl::TimestampPrecision(range) => {
writer.set_field_name("timestamp_precision");
range.write_to(writer)?;
}
IslConstraintImpl::Type(type_ref) => {
writer.set_field_name("type");
type_ref.write_to(writer)?;
}
IslConstraintImpl::Unknown(field_name, value) => {
writer.set_field_name(field_name);
writer.write_element(value)?;
}
IslConstraintImpl::Utf8ByteLength(range) => {
writer.set_field_name("utf8_byte_length");
range.write_to(writer)?;
}
IslConstraintImpl::ValidValues(valid_values) => {
writer.set_field_name("valid_values");
writer.step_in(IonType::List)?;
for valid_value in &valid_values.valid_values {
valid_value.write_to(writer)?;
}
writer.step_out()?;
}
}
Ok(())
}
}

/// Represents the [annotations] constraint
///
/// [annotations]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#annotations
Expand All @@ -1064,6 +1209,21 @@ pub(crate) enum IslAnnotationsConstraint {
StandardAnnotations(IslTypeRefImpl),
}

impl WriteToIsl for IslAnnotationsConstraint {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.set_field_name("annotations");
match self {
IslAnnotationsConstraint::SimpleAnnotations(simple_annotations) => {
simple_annotations.write_to(writer)?;
}
IslAnnotationsConstraint::StandardAnnotations(standard_annotations) => {
standard_annotations.write_to(writer)?;
}
}
Ok(())
}
}

/// Represents the [simple syntax] for annotations constraint
/// ```ion
/// Grammar: <ANNOTATIONS> ::= annotations: <ANNOTATIONS_MODIFIER>... [ <SYMBOL>... ]
Expand Down Expand Up @@ -1180,6 +1340,28 @@ impl IslSimpleAnnotationsConstraint {
}
}

impl WriteToIsl for IslSimpleAnnotationsConstraint {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
let mut annotation_modifiers = vec![];
if self.is_ordered {
annotation_modifiers.push("ordered");
}
if self.is_closed {
annotation_modifiers.push("closed");
}
if self.is_required {
annotation_modifiers.push("required");
}
writer.set_annotations(annotation_modifiers);
writer.step_in(IonType::List)?;
for annotation in &self.annotations {
annotation.write_to(writer)?;
}
writer.step_out()?;
Ok(())
}
}

/// Represents the [valid_values] constraint
///
/// [valid_values]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#annotations
Expand Down Expand Up @@ -1273,6 +1455,22 @@ impl IslRegexConstraint {
}
}

impl WriteToIsl for IslRegexConstraint {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.set_field_name("regex");
let mut regex_modifiers = vec![];
if self.case_insensitive {
regex_modifiers.push("i");
}
if self.multi_line {
regex_modifiers.push("m");
}
writer.set_annotations(regex_modifiers);
writer.write_string(&self.expression)?;
Ok(())
}
}

/// Represents the [timestamp_offset] constraint
///
/// [timestamp_offset]: https://amazon-ion.github.io/ion-schema/docs/isl-1-0/spec#timestamp_offset
Expand All @@ -1290,3 +1488,15 @@ impl IslTimestampOffsetConstraint {
&self.valid_offsets
}
}

impl WriteToIsl for IslTimestampOffsetConstraint {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.set_field_name("timestamp_offset");
writer.step_in(IonType::List)?;
for timestamp_offset in &self.valid_offsets {
timestamp_offset.write_to(writer)?;
}
writer.step_out()?;
Ok(())
}
}
36 changes: 36 additions & 0 deletions ion-schema/src/isl/isl_import.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::isl::WriteToIsl;
use crate::result::{invalid_schema_error, invalid_schema_error_raw, IonSchemaResult};
use ion_rs::element::Element;
use ion_rs::{IonType, IonWriter};

/// Represents an [import] in an ISL schema.
///
Expand Down Expand Up @@ -56,6 +58,26 @@ impl IslImport {
}
}

impl WriteToIsl for IslImport {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.step_in(IonType::Struct)?;
match self {
IslImport::Schema(schema_import) => {
writer.set_field_name("id");
writer.write_string(schema_import)?;
}
IslImport::Type(type_import) => {
type_import.write_to(writer)?;
}
IslImport::TypeAlias(type_alias_import) => {
type_alias_import.write_to(writer)?;
}
}
writer.step_out()?;
Ok(())
}
}

/// Represents typed and type aliased [IslImport]s
/// Typed import grammar: `{ id: <ID>, type: <TYPE_NAME> }`
/// Type aliased import grammar: `{ id: <ID>, type: <TYPE_NAME>, as: <TYPE_ALIAS> }`
Expand Down Expand Up @@ -87,3 +109,17 @@ impl IslImportType {
&self.alias
}
}

impl WriteToIsl for IslImportType {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.set_field_name("id");
writer.write_symbol(&self.id)?;
writer.set_field_name("type");
writer.write_symbol(&self.type_name)?;
if let Some(alias) = &self.alias {
writer.set_field_name("as");
writer.write_symbol(alias)?;
}
Ok(())
}
}
51 changes: 50 additions & 1 deletion ion-schema/src/isl/isl_range.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::isl::isl_range::RangeBoundaryValue::*;
use crate::isl::util::TimestampPrecision;
use crate::isl::IslVersion;
use crate::isl::WriteToIsl;
use crate::result::{
invalid_schema_error, invalid_schema_error_raw, IonSchemaError, IonSchemaResult,
};
use ion_rs::element::writer::ElementWriter;
use ion_rs::element::Element;
use ion_rs::external::bigdecimal::num_bigint::BigInt;
use ion_rs::external::bigdecimal::{BigDecimal, One};
use ion_rs::types::integer::IntAccess;
use ion_rs::{element, Decimal, Int, IonType, Timestamp};
use ion_rs::{element, Decimal, Int, IonType, IonWriter, Timestamp};
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use std::prelude::rust_2021::TryInto;
Expand Down Expand Up @@ -418,6 +420,26 @@ impl Display for Range {
}
}

impl WriteToIsl for Range {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.set_annotations(["range"]);
match &self {
Range::Integer(integer) => integer.write_to(writer)?,
Range::NonNegativeInteger(non_negative_integer) => {
non_negative_integer.write_to(writer)?
}
Range::TimestampPrecision(timestamp_precision) => {
timestamp_precision.write_to(writer)?
}
Range::Timestamp(timestamp) => timestamp.write_to(writer)?,
Range::Decimal(decimal) => decimal.write_to(writer)?,
Range::Float(float) => float.write_to(writer)?,
Range::Number(number) => number.write_to(writer)?,
}
Ok(())
}
}

/// Represents a generic range where some constraints can be defined using this range
// this is a generic implementation of ranges
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -736,6 +758,16 @@ impl<T: Display> Display for RangeImpl<T> {
}
}

impl<T: Display> WriteToIsl for RangeImpl<T> {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
writer.step_in(IonType::List)?;
self.start.write_to(writer)?;
self.end.write_to(writer)?;
writer.step_out()?;
Ok(())
}
}

// This lets us turn any `T` into a RangeBoundaryValue<T>::Value(_, Inclusive)
impl<T> From<T> for RangeBoundaryValue<T> {
fn from(value: T) -> RangeBoundaryValue<T> {
Expand Down Expand Up @@ -944,6 +976,23 @@ impl<T: Display> Display for RangeBoundaryValue<T> {
}
}

impl<T: Display> WriteToIsl for RangeBoundaryValue<T> {
fn write_to<W: IonWriter>(&self, writer: &mut W) -> IonSchemaResult<()> {
match &self {
Max => writer.write_symbol("max")?,
Min => writer.write_symbol("min")?,
Value(value, range_boundary_type) => {
if range_boundary_type == &RangeBoundaryType::Exclusive {
writer.set_annotations(["exclusive"]);
}
let element = Element::read_one(format!("{value}").as_bytes())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a concrete suggestion at the moment, but this bit feels a bit fragile to me. We're using the constraint T: Display but actually hoping that T is something that's serialized as text Ion, which is a narrower set of types.

writer.write_element(&element)?;
}
}
Ok(())
}
}

/// Represents the range boundary types in terms of exclusivity (i.e. inclusive or exclusive)
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub enum RangeBoundaryType {
Expand Down
Loading