#![allow(non_camel_case_types)]
use crate::element::builders::StructBuilder;
use crate::lazy::decoder::{Decoder, LazyRawContainer};
use crate::lazy::expanded::r#struct::{
ExpandedStructIterator, ExpandedStructSource, LazyExpandedField, LazyExpandedStruct,
};
use crate::lazy::expanded::LazyExpandedValue;
use crate::lazy::value::{AnnotationsIterator, LazyValue};
use crate::lazy::value_ref::ValueRef;
use crate::result::IonFailure;
use crate::{Annotations, Element, IntoAnnotatedElement, IonError, IonResult, Struct, SymbolRef};
use std::fmt;
use std::fmt::{Debug, Formatter};
#[derive(Copy, Clone)]
pub struct LazyStruct<'top, D: Decoder> {
pub(crate) expanded_struct: LazyExpandedStruct<'top, D>,
}
impl<D: Decoder> Debug for LazyStruct<'_, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{{")?;
for field in self {
let field = field?;
let name = field.name()?;
let lazy_value = field.value();
let value = lazy_value.read()?;
write!(f, "{}:{:?},", name.text().unwrap_or("$0"), value)?;
}
write!(f, "}}")?;
Ok(())
}
}
impl<'top, D: Decoder> LazyStruct<'top, D> {
pub(crate) fn new(expanded_struct: LazyExpandedStruct<'top, D>) -> Self {
Self { expanded_struct }
}
pub fn iter(&self) -> StructIterator<'top, D> {
StructIterator {
expanded_struct_iter: self.expanded_struct.iter(),
}
}
#[cfg(feature = "experimental-tooling-apis")]
pub fn expanded(&self) -> LazyExpandedStruct<'top, D> {
self.expanded_struct
}
pub fn as_value(&self) -> LazyValue<'top, D> {
let context = self.expanded_struct.context;
let expanded_value = match self.expanded_struct.source {
ExpandedStructSource::ValueLiteral(v) => {
LazyExpandedValue::from_literal(context, v.as_value())
}
ExpandedStructSource::Template(env, element, _) => {
LazyExpandedValue::from_template(context, env, element)
}
_ => {
let value_ref = context.allocator().alloc_with(|| ValueRef::Struct(*self));
let annotations = &[];
LazyExpandedValue::from_constructed(context, annotations, value_ref)
}
};
LazyValue::new(expanded_value)
}
pub fn find(&self, name: &str) -> IonResult<Option<LazyValue<'top, D>>> {
let Some(expanded_value) = self.expanded_struct.find(name)? else {
return Ok(None);
};
let value = LazyValue::new(expanded_value);
Ok(Some(value))
}
pub fn find_expected(&self, name: &str) -> IonResult<LazyValue<'top, D>> {
self.find(name)?
.ok_or_else(|| IonError::decoding_error(format!("missing required field {name}")))
}
pub fn get(&self, name: &str) -> IonResult<Option<ValueRef<'top, D>>> {
self.find(name)?.map(|f| f.read()).transpose()
}
pub fn get_expected(&self, name: &str) -> IonResult<ValueRef<'top, D>> {
self.get(name)?
.ok_or_else(move || IonError::decoding_error(format!("missing required field {name}")))
}
pub fn annotations(&self) -> AnnotationsIterator<'top, D> {
AnnotationsIterator {
expanded_annotations: self.expanded_struct.annotations(),
context: self.expanded_struct.context,
}
}
}
#[derive(Copy, Clone)]
pub struct LazyField<'top, D: Decoder> {
pub(crate) expanded_field: LazyExpandedField<'top, D>,
}
impl<D: Decoder> Debug for LazyField<'_, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{}: {:?}",
self.name()?.text().unwrap_or("$0"),
self.value().read()?,
)
}
}
impl<'top, D: Decoder> LazyField<'top, D> {
pub fn name(&self) -> IonResult<SymbolRef<'top>> {
self.expanded_field.name().read()
}
pub fn value(&self) -> LazyValue<'top, D> {
LazyValue {
expanded_value: self.expanded_field.value(),
}
}
#[inline]
#[cfg(any(test, feature = "experimental-tooling-apis"))]
pub fn raw_name(&self) -> Option<D::FieldName<'top>> {
if let crate::LazyExpandedFieldName::RawName(_context, raw_name) =
self.expanded_field.name()
{
Some(raw_name)
} else {
None
}
}
#[cfg(feature = "experimental-tooling-apis")]
pub fn raw_value(&self) -> Option<D::Value<'top>> {
if let crate::ExpandedValueSource::ValueLiteral(literal) =
self.expanded_field.value().source()
{
Some(literal)
} else {
None
}
}
#[cfg(feature = "experimental-tooling-apis")]
pub fn range(&self) -> Option<std::ops::Range<usize>> {
use crate::HasRange;
match (self.raw_name(), self.raw_value()) {
(Some(raw_name), Some(raw_value)) => {
Some(raw_name.range().start..raw_value.range().end)
}
_ => None,
}
}
}
pub struct StructIterator<'top, D: Decoder> {
pub(crate) expanded_struct_iter: ExpandedStructIterator<'top, D>,
}
impl<'top, D: Decoder> Iterator for StructIterator<'top, D> {
type Item = IonResult<LazyField<'top, D>>;
fn next(&mut self) -> Option<Self::Item> {
match StructIterator::next_field(self) {
Ok(Some(field)) => Some(Ok(field)),
Ok(None) => None,
Err(e) => Some(Err(e)),
}
}
}
impl<'top, D: Decoder> StructIterator<'top, D> {
pub fn next_field(&mut self) -> IonResult<Option<LazyField<'top, D>>> {
let expanded_field = match self.expanded_struct_iter.next() {
Some(expanded_field) => expanded_field?,
None => return Ok(None),
};
let lazy_field = LazyField { expanded_field };
Ok(Some(lazy_field))
}
}
impl<'top, D: Decoder> TryFrom<LazyStruct<'top, D>> for Struct {
type Error = IonError;
fn try_from(lazy_struct: LazyStruct<'top, D>) -> Result<Self, Self::Error> {
let mut builder = StructBuilder::new();
for field in &lazy_struct {
let field = field?;
builder = builder.with_field(field.name()?, Element::try_from(field.value())?);
}
Ok(builder.build())
}
}
impl<'top, D: Decoder> TryFrom<LazyStruct<'top, D>> for Element {
type Error = IonError;
fn try_from(lazy_struct: LazyStruct<'top, D>) -> Result<Self, Self::Error> {
let annotations: Annotations = lazy_struct.annotations().try_into()?;
let struct_: Struct = lazy_struct.try_into()?;
Ok(struct_.with_annotations(annotations))
}
}
impl<'top, D: Decoder> IntoIterator for &LazyStruct<'top, D> {
type Item = IonResult<LazyField<'top, D>>;
type IntoIter = StructIterator<'top, D>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'top, D: Decoder> IntoIterator for LazyStruct<'top, D> {
type Item = IonResult<LazyField<'top, D>>;
type IntoIter = StructIterator<'top, D>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod tests {
use crate::lazy::binary::test_utilities::to_binary_ion;
use crate::{v1_0, Reader};
use super::*;
#[test]
fn find() -> IonResult<()> {
let ion_data = to_binary_ion("{foo: 1, bar: 2, baz: 3}")?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
let baz = struct_.find("baz")?;
assert!(baz.is_some());
assert_eq!(baz.unwrap().read()?, ValueRef::Int(3.into()));
let quux = struct_.get("quux")?;
assert_eq!(quux, None);
Ok(())
}
#[test]
fn find_expected() -> IonResult<()> {
let ion_data = to_binary_ion("{foo: 1, bar: 2, baz: 3}")?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
let baz = struct_.find_expected("baz");
assert!(baz.is_ok());
assert_eq!(baz.unwrap().read()?, ValueRef::Int(3.into()));
let quux = struct_.find_expected("quux");
assert!(quux.is_err());
Ok(())
}
#[test]
fn get() -> IonResult<()> {
let ion_data = to_binary_ion("{foo: 1, bar: 2, baz: 3}")?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
let baz = struct_.get("baz")?;
assert_eq!(baz, Some(ValueRef::Int(3.into())));
let quux = struct_.get("quux")?;
assert_eq!(quux, None);
Ok(())
}
#[test]
fn get_expected() -> IonResult<()> {
let ion_data = to_binary_ion("{foo: 1, bar: 2, baz: 3}")?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
let baz = struct_.get_expected("baz");
assert_eq!(baz, Ok(ValueRef::Int(3.into())));
let quux = struct_.get_expected("quux");
assert!(quux.is_err());
Ok(())
}
#[test]
fn annotations() -> IonResult<()> {
let ion_data = to_binary_ion("a::b::c::{foo: 1, bar: 2, baz: quux::quuz::3}")?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
assert!(struct_.annotations().are(["a", "b", "c"])?);
let baz = struct_.find_expected("baz")?;
assert!(baz.annotations().are(["quux", "quuz"])?);
Ok(())
}
#[test]
fn try_into_element() -> IonResult<()> {
let ion_text = "foo::baz::baz::{a: 1, b: 2, c: 3}";
let binary_ion = to_binary_ion(ion_text)?;
let mut reader = Reader::new(v1_0::Binary, binary_ion)?;
let struct_ = reader.expect_next()?.read()?.expect_struct()?;
let result: IonResult<Element> = struct_.try_into();
assert!(result.is_ok());
assert_eq!(result?, Element::read_one(ion_text)?);
Ok(())
}
}