1use crate::ion_path::IonPath;
2use crate::violation::{Violation, ViolationCode};
3use ion_rs::{Element, IonType, Struct};
4use std::fmt::{Display, Formatter};
5
6#[derive(Debug, Clone, PartialEq, Copy)]
8pub(crate) enum IonSchemaElementType {
9 Null,
10 Bool,
11 Int,
12 Float,
13 Decimal,
14 Timestamp,
15 Symbol,
16 String,
17 Clob,
18 Blob,
19 List,
20 SExp,
21 Struct,
22 Document,
23}
24
25impl Display for IonSchemaElementType {
26 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27 let text = match self {
28 IonSchemaElementType::Null => "null",
29 IonSchemaElementType::Bool => "bool",
30 IonSchemaElementType::Int => "int",
31 IonSchemaElementType::Float => "float",
32 IonSchemaElementType::Decimal => "decimal",
33 IonSchemaElementType::Timestamp => "timestamp",
34 IonSchemaElementType::Symbol => "symbol",
35 IonSchemaElementType::String => "string",
36 IonSchemaElementType::Clob => "clob",
37 IonSchemaElementType::Blob => "blob",
38 IonSchemaElementType::List => "list",
39 IonSchemaElementType::SExp => "sexp",
40 IonSchemaElementType::Struct => "struct",
41 IonSchemaElementType::Document => "document",
42 };
43 f.write_str(text)
44 }
45}
46
47impl From<IonType> for IonSchemaElementType {
48 fn from(value: IonType) -> Self {
49 match value {
50 IonType::Null => IonSchemaElementType::Null,
51 IonType::Bool => IonSchemaElementType::Bool,
52 IonType::Int => IonSchemaElementType::Int,
53 IonType::Float => IonSchemaElementType::Float,
54 IonType::Decimal => IonSchemaElementType::Decimal,
55 IonType::Timestamp => IonSchemaElementType::Timestamp,
56 IonType::Symbol => IonSchemaElementType::Symbol,
57 IonType::String => IonSchemaElementType::String,
58 IonType::Clob => IonSchemaElementType::Clob,
59 IonType::Blob => IonSchemaElementType::Blob,
60 IonType::List => IonSchemaElementType::List,
61 IonType::SExp => IonSchemaElementType::SExp,
62 IonType::Struct => IonSchemaElementType::Struct,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq)]
69enum IonSchemaElementKind<'a> {
70 SingleElement(&'a Element),
71 Document(&'a [Element]),
72}
73
74#[derive(Debug, Clone, PartialEq)]
84pub struct IonSchemaElement<'a> {
85 content: IonSchemaElementKind<'a>,
86}
87
88impl<'a> IonSchemaElement<'a>
89where
90 Self: 'a,
91{
92 pub(crate) fn as_sequence_iter(&'a self) -> Option<impl Iterator<Item = &'a Element>> {
93 match &self.content {
94 IonSchemaElementKind::SingleElement(e) => e
95 .as_sequence()
96 .map(|s| AsRef::<[Element]>::as_ref(s).iter()),
97 IonSchemaElementKind::Document(d) => Some(d.iter()),
98 }
99 }
100
101 pub(crate) fn as_struct(&'a self) -> Option<&'a Struct> {
102 match self.content {
103 IonSchemaElementKind::SingleElement(e) => e.as_struct(),
104 _ => None,
105 }
106 }
107
108 pub(crate) fn as_element(&'a self) -> Option<&'a Element> {
109 match self.content {
110 IonSchemaElementKind::SingleElement(element) => Some(element),
111 _ => None,
112 }
113 }
114
115 pub(crate) fn as_document(&'a self) -> Option<impl Iterator<Item = &'a Element>> {
116 match &self.content {
117 IonSchemaElementKind::Document(d) => Some(d.iter()),
118 _ => None,
119 }
120 }
121
122 pub(crate) fn ion_schema_type(&self) -> IonSchemaElementType {
123 match self.as_element() {
124 Some(e) => e.ion_type().into(),
125 _ => IonSchemaElementType::Document,
126 }
127 }
128
129 pub(crate) fn is_null(&self) -> bool {
130 match self.as_element() {
131 Some(e) => e.is_null(),
132 _ => false,
133 }
134 }
135
136 pub(crate) fn expect_element_of_type(
137 &self,
138 types: &[IonType],
139 constraint_name: &str,
140 ion_path: &mut IonPath,
141 ) -> Result<&Element, Violation> {
142 match self.as_element() {
143 Some(element) => {
144 if !types.contains(&element.ion_type()) || element.is_null() {
145 return Err(Violation::new(
148 constraint_name,
149 ViolationCode::TypeMismatched,
150 format!("expected {:?} but found {}", types, element.ion_type()),
151 ion_path,
152 ));
153 }
154 Ok(element)
156 }
157 None => {
158 Err(Violation::new(
160 constraint_name,
161 ViolationCode::TypeMismatched,
162 format!("expected {types:?} but found document"),
163 ion_path,
164 ))
165 }
166 }
167 }
168}
169
170impl<'a> Display for IonSchemaElement<'a> {
171 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
172 match &self.content {
173 IonSchemaElementKind::SingleElement(element) => {
174 write!(f, "{element}")
175 }
176 IonSchemaElementKind::Document(document) => {
177 write!(f, "/* Ion document */ ")?;
178 for value in document.iter() {
179 write!(f, "{value} ")?;
180 }
181 write!(f, "/* end */")
182 }
183 }
184 }
185}
186
187impl<'a> From<&'a Element> for IonSchemaElement<'a> {
188 fn from(value: &'a Element) -> Self {
189 IonSchemaElement {
190 content: IonSchemaElementKind::SingleElement(value),
191 }
192 }
193}
194
195pub struct DocumentHint<'a>(&'a [Element]);
197
198pub trait AsDocumentHint<'a> {
199 fn as_document(&'a self) -> DocumentHint<'a>;
200}
201
202impl<'a, T> AsDocumentHint<'a> for T
203where
204 T: AsRef<[Element]> + 'a,
205{
206 fn as_document(&'a self) -> DocumentHint<'a> {
207 DocumentHint(self.as_ref())
208 }
209}
210
211impl<'a> From<DocumentHint<'a>> for IonSchemaElement<'a> {
212 fn from(value: DocumentHint<'a>) -> Self {
213 let content: IonSchemaElementKind = IonSchemaElementKind::Document(value.0);
214 IonSchemaElement { content }
215 }
216}