use std::io::Write;
use crate::lazy::encoder::text::v1_0::writer::LazyRawTextWriter_1_0;
use crate::lazy::encoder::text::v1_1::value_writer::TextValueWriter_1_1;
use crate::lazy::encoder::value_writer::internal::MakeValueWriter;
use crate::lazy::encoder::value_writer::SequenceWriter;
use crate::lazy::encoder::writer::WriterMacroTable;
use crate::lazy::encoder::LazyRawWriter;
use crate::lazy::encoding::{Encoding, TextEncoding_1_1};
use crate::text::whitespace_config::{
COMPACT_WHITESPACE_CONFIG, LINES_WHITESPACE_CONFIG, PRETTY_WHITESPACE_CONFIG,
};
use crate::write_config::WriteConfigKind;
use crate::{ContextWriter, IonResult, IonVersion, MacroTable, TextFormat, WriteConfig};
pub struct LazyRawTextWriter_1_1<W: Write> {
pub(crate) writer_1_0: LazyRawTextWriter_1_0<W>,
macros: WriterMacroTable,
}
impl<W: Write> SequenceWriter for LazyRawTextWriter_1_1<W> {
type Resources = W;
fn close(self) -> IonResult<Self::Resources> {
self.writer_1_0.close()
}
}
impl<W: Write> ContextWriter for LazyRawTextWriter_1_1<W> {
type NestedValueWriter<'a>
= TextValueWriter_1_1<'a, W>
where
Self: 'a;
}
impl<W: Write> MakeValueWriter for LazyRawTextWriter_1_1<W> {
fn make_value_writer(&mut self) -> Self::NestedValueWriter<'_> {
let value_writer_1_0 = self.writer_1_0.make_value_writer();
TextValueWriter_1_1 {
value_writer_1_0,
macros: &self.macros,
}
}
}
impl<W: Write> LazyRawWriter<W> for LazyRawTextWriter_1_1<W> {
fn new(output: W) -> IonResult<Self>
where
Self: Sized,
{
Self::build(
WriteConfig::<TextEncoding_1_1>::new(TextFormat::default()),
output,
)
}
fn build<E: Encoding>(config: WriteConfig<E>, mut output: W) -> IonResult<Self>
where
Self: Sized,
{
match &config.kind {
WriteConfigKind::Text(text_config) => {
let whitespace_config = match text_config.text_kind {
TextFormat::Compact => &COMPACT_WHITESPACE_CONFIG,
TextFormat::Lines => &LINES_WHITESPACE_CONFIG,
TextFormat::Pretty => &PRETTY_WHITESPACE_CONFIG,
};
write!(
output,
"$ion_1_1{}",
whitespace_config.space_between_top_level_values
)?;
Ok(LazyRawTextWriter_1_1 {
writer_1_0: LazyRawTextWriter_1_0 {
output,
whitespace_config,
},
macros: WriterMacroTable::new(MacroTable::with_system_macros(IonVersion::v1_1)),
})
}
WriteConfigKind::Binary(_) => {
unreachable!("Binary writer can not be created from text encoding")
}
}
}
fn flush(&mut self) -> IonResult<()> {
self.writer_1_0.flush()
}
fn output(&self) -> &W {
self.writer_1_0.output()
}
fn output_mut(&mut self) -> &mut W {
self.writer_1_0.output_mut()
}
fn macro_table(&self) -> &WriterMacroTable {
&self.macros
}
fn macro_table_mut(&mut self) -> Option<&mut WriterMacroTable> {
Some(&mut self.macros)
}
fn write_version_marker(&mut self) -> IonResult<()> {
let space_between = self
.writer_1_0
.whitespace_config
.space_between_top_level_values;
write!(self.writer_1_0.output, "$ion_1_1{space_between}")?;
Ok(())
}
}
#[cfg(feature = "experimental-ion-1-1")]
#[cfg(test)]
mod tests {
use crate::lazy::any_encoding::IonVersion;
use crate::lazy::decoder::{LazyRawReader, LazyRawValue};
use crate::lazy::encoder::text::v1_1::writer::LazyRawTextWriter_1_1;
use crate::lazy::encoder::value_writer::{SequenceWriter, StructWriter, ValueWriter};
use crate::lazy::encoder::write_as_ion::WriteAsSExp;
use crate::lazy::encoder::LazyRawWriter;
use crate::lazy::expanded::compiler::TemplateCompiler;
use crate::lazy::expanded::macro_evaluator::RawEExpression;
use crate::lazy::expanded::EncodingContext;
use crate::lazy::text::raw::v1_1::reader::{LazyRawTextReader_1_1, MacroIdRef};
use crate::symbol_ref::AsSymbolRef;
use crate::{
v1_1, Annotatable, Decimal, EExpWriter, ElementReader, IonData, IonResult, IonType, Null,
RawSymbolRef, Reader, Timestamp, Writer,
};
#[test]
fn write_scalars() -> IonResult<()> {
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
writer
.write(Null(IonType::String))?
.write(true)?
.write(1)?
.write(1.5f64)?
.write(Decimal::new(123, -2))?
.write(Timestamp::with_ymd(2024, 4, 23).build()?)?
.write("foo")?
.write("foo".as_symbol_ref())?
.write([0xEAu8, 0x01, 0x01, 0xEE])?
.write_clob([0xEAu8, 0x01, 0x01, 0xEE])?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let expected_ion = r#"
$ion_1_1
null.string
true
1
1.5e0
1.23
2024-04-23T
"foo"
foo
{{6gEB7g==}}
{{"\xea\x01\x01\xee"}}
"#;
let mut reader = Reader::new(v1_1::Text, encoded_text)?;
let actual = reader.read_all_elements()?;
let mut reader = Reader::new(v1_1::Text, expected_ion)?;
let expected = reader.read_all_elements()?;
assert!(IonData::eq(&expected, &actual));
Ok(())
}
#[test]
fn write_list() -> IonResult<()> {
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
let empty_list: [i64; 0] = [];
writer
.write(empty_list)?
.write([1])?
.write([1, 2])?
.write([[1, 2], [3, 4], [5, 6]])?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let expected_ion = r#"
$ion_1_1
[]
[1]
[1, 2]
[[1, 2], [3, 4], [5, 6]]
"#;
let mut reader = Reader::new(v1_1::Text, encoded_text)?;
let actual = reader.read_all_elements()?;
let mut reader = Reader::new(v1_1::Text, expected_ion)?;
let expected = reader.read_all_elements()?;
assert!(IonData::eq(&expected, &actual));
Ok(())
}
#[test]
fn write_sexp() -> IonResult<()> {
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
let empty_sexp: [i64; 0] = [];
writer
.write_sexp(empty_sexp)?
.write_sexp([1])?
.write_sexp([1, 2])?
.write_sexp([[1, 2].as_sexp(), [3, 4].as_sexp(), [5, 6].as_sexp()])?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let expected_ion = r#"
$ion_1_1
()
(1)
(1 2)
((1 2) (3 4) (5 6))
"#;
let mut reader = Reader::new(v1_1::Text, encoded_text)?;
let actual = reader.read_all_elements()?;
let mut reader = Reader::new(v1_1::Text, expected_ion)?;
let expected = reader.read_all_elements()?;
assert!(IonData::eq(&expected, &actual));
Ok(())
}
#[test]
fn write_struct() -> IonResult<()> {
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
let empty_field_list: [(&str, i64); 0] = [];
writer
.write_struct(empty_field_list)?
.write_struct([("foo", 1)])?
.write_struct([("foo", 1), ("bar", 2)])?
.write_struct([("foo", 1), ("bar", 2), ("baz", 3)])?;
let mut parent_struct = writer.struct_writer()?;
parent_struct
.field_writer("quux")
.write_struct([("quuz", 4)])?;
parent_struct.close()?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let expected_ion = r#"
$ion_1_1
{}
{foo: 1}
{foo: 1, bar: 2}
{foo: 1, bar: 2, baz: 3}
{quux: {quuz: 4}}
"#;
let mut reader = Reader::new(v1_1::Text, encoded_text)?;
let actual = reader.read_all_elements()?;
let mut reader = Reader::new(v1_1::Text, expected_ion)?;
let expected = reader.read_all_elements()?;
assert!(IonData::eq(&expected, &actual));
Ok(())
}
#[test]
fn test_eexp() -> IonResult<()> {
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
writer.compile_macro("(macro foo (a b c d) (.values (%a) (%b) (%c) (%d)))")?;
let mut macro_args = writer.eexp_writer("foo")?;
macro_args
.write(1)?
.write([2, 3, 4])?
.write("bar")?
.write_symbol("+++")?;
macro_args.close()?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let mut context = EncodingContext::for_ion_version(IonVersion::v1_1);
let macro_foo =
TemplateCompiler::compile_from_source(context.macro_table(), "(macro foo (x*) null)")?;
context.macro_table_mut().add_template_macro(macro_foo)?;
let mut reader =
LazyRawTextReader_1_1::new(context.get_ref(), encoded_text.as_bytes(), true);
let _marker = reader.next()?.expect_ivm()?;
let eexp = reader.next()?.expect_eexp()?;
assert_eq!(MacroIdRef::LocalName("foo"), eexp.id());
let mut args = eexp.raw_arguments();
let x = args.next().unwrap()?.expr().expect_arg_group()?;
let mut x_values = x.into_iter();
let int_value = x_values
.next()
.unwrap()?
.expect_value()
.unwrap()
.read()?
.expect_i64()?;
assert_eq!(int_value, 1);
let list_value = x_values
.next()
.unwrap()?
.expect_value()?
.read()?
.expect_list()?;
assert_eq!(list_value.iter().count(), 3);
let string_value = x_values
.next()
.unwrap()?
.expect_value()?
.read()?
.expect_string()?;
assert_eq!(string_value, "bar");
let symbol_value = x_values
.next()
.unwrap()?
.expect_value()?
.read()?
.expect_symbol()?;
assert_eq!(symbol_value, RawSymbolRef::Text("+++"));
Ok(())
}
#[test]
fn write_annotated_values() -> IonResult<()> {
const NO_ANNOTATIONS: [&str; 0] = [];
let mut writer = LazyRawTextWriter_1_1::new(vec![])?;
writer
.write(1)?
.write(2.annotated_with(NO_ANNOTATIONS))?
.write(3.annotated_with("foo"))?
.write(4.annotated_with(["foo", "bar", "baz"]))?;
let encoded_bytes = writer.close()?;
let encoded_text = String::from_utf8(encoded_bytes).unwrap();
println!("{encoded_text}");
let expected_ion = r#"
$ion_1_1
1
2 // An explicitly empty annotations sequence results in an unannotated value
foo::3
foo::bar::baz::4
"#;
let mut reader = Reader::new(v1_1::Text, encoded_text)?;
let actual = reader.read_all_elements()?;
let mut reader = Reader::new(v1_1::Text, expected_ion)?;
let expected = reader.read_all_elements()?;
assert!(IonData::eq(&expected, &actual));
Ok(())
}
#[test]
fn test_system_eexp() -> IonResult<()> {
let mut writer = Writer::new(v1_1::Text, vec![])?;
let make_string = writer.get_macro("make_string")?;
let mut macro_args = writer.eexp_writer(&make_string)?;
let mut exp_group = macro_args.expr_group_writer()?;
exp_group
.write("foo")?
.write("bar")?
.write("baz")?
.write_symbol("+++")?;
exp_group.close()?;
macro_args.close()?;
let encoded_bytes = writer.close()?;
let mut reader = Reader::new(v1_1::Text, encoded_bytes)?;
let element = reader.read_next_element()?;
assert_eq!("foobarbaz+++", element.unwrap().as_string().unwrap());
Ok(())
}
}