use crate::lazy::expanded::EncodingContextRef;
use crate::lazy::value::AnnotationsIterator;
use crate::{
AnyEncoding, Decoder, Element, EncodingContext, ExpandedValueSource, IonError, IonResult,
IonType, LazyExpandedValue, LazyValue, ValueRef,
};
use delegate::delegate;
use std::mem;
use std::ops::Deref;
pub struct LazyResource<'a, Encoding: Decoder, Resource> {
#[allow(dead_code)]
value: LazyValue<'a, Encoding>,
resource: Resource,
}
impl<Encoding: Decoder, Resource> Deref for LazyResource<'_, Encoding, Resource> {
type Target = Resource;
fn deref(&self) -> &Self::Target {
&self.resource
}
}
pub struct LazyElement<Encoding: Decoder = AnyEncoding> {
context: EncodingContext,
source: ExpandedValueSource<'static, Encoding>,
}
impl<Encoding: Decoder> LazyElement<Encoding> {
pub(crate) unsafe fn new(
context: EncodingContext,
source: ExpandedValueSource<'_, Encoding>,
) -> Self {
let static_source: ExpandedValueSource<'static, Encoding> =
unsafe { mem::transmute(source) };
Self {
context,
source: static_source,
}
}
pub(crate) fn as_lazy_value<'top>(&'top self) -> LazyValue<'top, Encoding> {
let expanded: LazyExpandedValue<'top, Encoding> = LazyExpandedValue {
context: self.context.get_ref(),
source: unsafe {
mem::transmute::<
ExpandedValueSource<'static, Encoding>,
ExpandedValueSource<'top, Encoding>,
>(self.source)
},
variable: None,
};
LazyValue::new(expanded)
}
}
impl<Encoding: Decoder> LazyElement<Encoding> {
delegate! {
to self.as_lazy_value() {
pub fn ion_type(&self) -> IonType;
pub fn is_null(&self) -> bool;
pub fn is_container(&self) -> bool;
pub fn is_scalar(&self) -> bool;
pub fn has_annotations(&self) -> bool;
}
}
pub fn read(&self) -> IonResult<LazyResource<'_, Encoding, ValueRef<'_, Encoding>>> {
let value = self.as_lazy_value();
let resource = value.read()?;
Ok(LazyResource { value, resource })
}
pub fn annotations(
&self,
) -> IonResult<LazyResource<'_, Encoding, AnnotationsIterator<'_, Encoding>>> {
let value = self.as_lazy_value();
let resource = value.annotations();
Ok(LazyResource { value, resource })
}
#[allow(dead_code)]
pub(crate) fn context(&self) -> LazyResource<'_, Encoding, EncodingContextRef<'_>> {
let value = self.as_lazy_value();
let resource = value.context();
LazyResource { value, resource }
}
}
impl<'top, Encoding: Decoder> From<LazyValue<'top, Encoding>> for LazyElement<Encoding> {
fn from(value: LazyValue<'top, Encoding>) -> Self {
value.to_owned()
}
}
impl<Encoding: Decoder> TryFrom<LazyElement<Encoding>> for Element {
type Error = IonError;
fn try_from(lazy_element: LazyElement<Encoding>) -> Result<Self, Self::Error> {
lazy_element.as_lazy_value().try_into()
}
}
impl<Encoding: Decoder> TryFrom<&LazyElement<Encoding>> for Element {
type Error = IonError;
fn try_from(lazy_element: &LazyElement<Encoding>) -> Result<Self, Self::Error> {
lazy_element.as_lazy_value().try_into()
}
}
#[cfg(test)]
mod tests {
use crate::lazy::expanded::lazy_element::LazyElement;
use crate::{AnyEncoding, Element, IonResult, Reader, Sequence};
#[cfg(feature = "experimental-ion-1-1")]
fn test_data() -> String {
let test_data = r#"
$ion_1_1
// === Values backed by `ExpandedValueSource::ValueLiteral` ===
foo
true
baz::5
[(), {}, ()]
2025T
"Hello"
// === Macro output backed by `ExpandedValueSource::ValueLiteral` ===
(:values 1 2 3)
(:add_macros
(macro foo () 'singleton value')
(macro greet (name) (.make_string "Hello, " (%name))))
// === Produces a value backed by an `ExpandedValueSource::SingletonEExp` ===
(:foo)
// === Produces a value backed by an `ExpandedValueSource::Constructed` ===
(:greet "Alice")
"#;
test_data.to_owned()
}
#[cfg(not(feature = "experimental-ion-1-1"))]
fn test_data() -> String {
let test_data = r#"
// === Values backed by `ExpandedValueSource::ValueLiteral` ===
foo
true
baz::5
[(), {}, ()]
2025T
"Hello"
"#;
test_data.to_owned()
}
fn lazy_element_test<TestFn>(test: TestFn) -> IonResult<()>
where
TestFn: FnOnce(Sequence, &mut dyn Iterator<Item = IonResult<LazyElement>>) -> IonResult<()>,
{
let test_data = test_data();
let expected = Element::read_all(&test_data)?;
let mut reader = Reader::new(AnyEncoding, &test_data)?;
test(expected, &mut reader)
}
#[test]
fn equivalent_to_lazy_value_when_reading_forward() -> IonResult<()> {
lazy_element_test(|expected, reader| {
let actual = reader
.map(|result| result.and_then(Element::try_from))
.collect::<IonResult<Vec<Element>>>()?;
assert!(expected.iter().eq(&actual));
Ok(())
})
}
#[test]
fn equivalent_to_lazy_value_when_read_backward() -> IonResult<()> {
lazy_element_test(|expected, reader| {
let lazy_elements_vec = reader.collect::<IonResult<Vec<LazyElement>>>()?;
let actual = lazy_elements_vec
.iter()
.rev()
.map(Element::try_from)
.collect::<IonResult<Vec<Element>>>()?;
assert!(expected.into_iter().rev().eq(actual));
Ok(())
})
}
#[test]
fn values_survive_after_reader_drops() -> IonResult<()> {
let mut reader = Reader::new(AnyEncoding, "foo")?;
let lazy_element = reader.expect_next()?.to_owned();
drop(reader);
assert_eq!(Element::symbol("foo"), Element::try_from(lazy_element)?);
Ok(())
}
}