use crate::EdifactError;
use crate::event::{EdifactEvent, EventEmitter, WriterEmitter};
use std::io::Write;
pub trait EdifactSerialize {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError>;
}
pub trait EdifactCompositeSerialize {
fn edifact_serialize_composite<E: EventEmitter>(
&self,
emitter: &mut E,
) -> Result<(), EdifactError>;
}
impl EdifactCompositeSerialize for Vec<String> {
fn edifact_serialize_composite<E: EventEmitter>(
&self,
emitter: &mut E,
) -> Result<(), EdifactError> {
if self.is_empty() {
return emitter.emit(EdifactEvent::element(""));
}
emitter.emit(EdifactEvent::element(&self[0]))?;
for component in self.iter().skip(1) {
emitter.emit(EdifactEvent::component(component))?;
}
Ok(())
}
}
impl EdifactSerialize for str {
#[inline]
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
emitter.emit(EdifactEvent::element(self))
}
}
impl EdifactSerialize for String {
#[inline]
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
emitter.emit(EdifactEvent::element(self.as_str()))
}
}
impl<T: EdifactSerialize> EdifactSerialize for Option<T> {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
match self {
Some(v) => v.edifact_serialize(emitter),
None => emitter.emit(EdifactEvent::element("")),
}
}
}
impl<T: EdifactSerialize> EdifactSerialize for Vec<T> {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
for item in self {
item.edifact_serialize(emitter)?;
}
Ok(())
}
}
impl<T: EdifactSerialize> EdifactSerialize for [T] {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
for item in self {
item.edifact_serialize(emitter)?;
}
Ok(())
}
}
macro_rules! impl_serialize_int {
($($t:ty),+ $(,)?) => {
$(
impl EdifactSerialize for $t {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
use std::io::Write as _;
let mut buf = [0u8; 42];
let mut w: &mut [u8] = &mut buf;
if write!(w, "{self}").is_ok() {
let written = 42 - w.len();
let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
emitter.emit(EdifactEvent::element(s))
} else {
let s = format!("{self}");
emitter.emit(EdifactEvent::element(&s))
}
}
}
)+
};
}
impl_serialize_int!(
u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, bool
);
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DecimalFloat<T>(pub T);
fn serialize_with_decimal_mark<E: EventEmitter>(
display: &dyn std::fmt::Display,
emitter: &mut E,
) -> Result<(), EdifactError> {
use std::io::Write as _;
let mark = emitter.decimal_mark();
if mark == b'.' {
let mut buf = [0u8; 320];
let mut w: &mut [u8] = &mut buf;
if write!(w, "{display}").is_ok() {
let written = 320 - w.len();
let s = std::str::from_utf8(&buf[..written]).map_err(|_| EdifactError::InvalidUtf8)?;
reject_non_finite(s)?;
return emitter.emit(EdifactEvent::element(s));
}
let s = format!("{display}");
reject_non_finite(&s)?;
return emitter.emit(EdifactEvent::element(&s));
}
let s = format!("{display}");
reject_non_finite(&s)?;
if s.contains('.') {
let mut mark_buf = [0u8; 4];
let mark_str = (mark as char).encode_utf8(&mut mark_buf);
let replaced = s.replace('.', mark_str);
emitter.emit(EdifactEvent::element(&replaced))
} else {
emitter.emit(EdifactEvent::element(&s))
}
}
#[inline]
fn reject_non_finite(rendered: &str) -> Result<(), EdifactError> {
let bare = rendered.strip_prefix(['+', '-']).unwrap_or(rendered);
if bare.eq_ignore_ascii_case("nan")
|| bare.eq_ignore_ascii_case("inf")
|| bare.eq_ignore_ascii_case("infinity")
{
return Err(EdifactError::NonFiniteNumber {
value: rendered.to_owned(),
});
}
Ok(())
}
impl<T: std::fmt::Display> EdifactSerialize for DecimalFloat<T> {
#[inline]
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
serialize_with_decimal_mark(&self.0, emitter)
}
}
pub fn emit_sparse_segment<E: EventEmitter>(
emitter: &mut E,
tag: &str,
parts: &mut [(usize, usize, std::borrow::Cow<'_, str>)],
) -> Result<(), EdifactError> {
parts.sort_by_key(|(element, component, _)| (*element, *component));
emitter.emit(EdifactEvent::start(tag))?;
let mut cursor = 0usize;
let element_count = parts.last().map_or(0, |(element, _, _)| *element + 1);
for element in 0..element_count {
let mut next_component = 0usize;
let mut opened = false;
while cursor < parts.len() && parts[cursor].0 == element {
let (_, component, value) = &parts[cursor];
if *component < next_component {
cursor += 1;
continue;
}
while next_component < *component {
let event = if opened {
EdifactEvent::component("")
} else {
EdifactEvent::element("")
};
emitter.emit(event)?;
opened = true;
next_component += 1;
}
let event = if opened {
EdifactEvent::component(value.as_ref())
} else {
EdifactEvent::element(value.as_ref())
};
emitter.emit(event)?;
opened = true;
next_component += 1;
cursor += 1;
}
if !opened {
emitter.emit(EdifactEvent::element(""))?;
}
}
emitter.emit(EdifactEvent::EndSegment)
}
pub fn to_writer<T, W>(inner: W, value: &T) -> Result<(), EdifactError>
where
T: EdifactSerialize,
W: Write,
{
let mut emitter = WriterEmitter::new(inner);
value.edifact_serialize(&mut emitter)?;
emitter.finish().map(|_| ())
}
pub fn to_bytes<T: EdifactSerialize>(value: &T) -> Result<Vec<u8>, EdifactError> {
let mut buf = Vec::new();
to_writer(&mut buf, value)?;
Ok(buf)
}
pub fn to_edifact_string<T: EdifactSerialize>(value: &T) -> Result<String, EdifactError> {
let bytes = to_bytes(value)?;
String::from_utf8(bytes).map_err(|_| EdifactError::InvalidUtf8)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{EdifactEvent, VecEmitter};
use std::borrow::Cow;
fn sparse_to_wire(parts: &mut [(usize, usize, Cow<'_, str>)], tag: &str) -> String {
let mut buf = Vec::new();
{
let mut emitter = crate::WriterEmitter::new(&mut buf);
emit_sparse_segment(&mut emitter, tag, parts).expect("emit");
emitter.finish().expect("finish");
}
String::from_utf8(buf).expect("utf-8")
}
#[test]
fn emit_sparse_segment_fills_gaps_in_elements_and_components() {
let mut parts = vec![
(1, 2, Cow::Borrowed("293")),
(0, 0, Cow::Borrowed("MS")),
(3, 1, Cow::Borrowed("late")),
];
assert_eq!(sparse_to_wire(&mut parts, "NAD"), "NAD+MS+::293++:late'");
}
#[test]
fn emit_sparse_segment_first_value_wins_on_a_duplicate_slot() {
let mut parts = vec![
(0, 0, Cow::Borrowed("MS")),
(1, 0, Cow::Borrowed("first")),
(1, 0, Cow::Borrowed("second")),
(1, 2, Cow::Borrowed("293")),
];
assert_eq!(sparse_to_wire(&mut parts, "NAD"), "NAD+MS+first::293'");
}
#[test]
fn emit_sparse_segment_with_no_parts_writes_a_bare_tag() {
assert_eq!(sparse_to_wire(&mut [], "UNS"), "UNS'");
}
struct BgmSegment {
doc_name_code: String,
pruef_id: String,
msg_function: Option<String>,
}
impl EdifactSerialize for BgmSegment {
fn edifact_serialize<E: EventEmitter>(&self, emitter: &mut E) -> Result<(), EdifactError> {
emitter.emit(EdifactEvent::start("BGM"))?;
emitter.emit(EdifactEvent::element(&self.doc_name_code))?;
emitter.emit(EdifactEvent::element(&self.pruef_id))?;
self.msg_function.edifact_serialize(emitter)?;
emitter.emit(EdifactEvent::EndSegment)?;
Ok(())
}
}
#[test]
fn vec_emitter_captures_segment_events() {
let seg = BgmSegment {
doc_name_code: "E03".to_owned(),
pruef_id: "11042".to_owned(),
msg_function: None,
};
let mut emitter = VecEmitter::default();
seg.edifact_serialize(&mut emitter).unwrap();
assert_eq!(emitter.events[0], EdifactEvent::start("BGM".to_owned()));
assert_eq!(emitter.events.last(), Some(&EdifactEvent::EndSegment));
}
#[test]
fn to_bytes_produces_valid_edifact() {
let seg = BgmSegment {
doc_name_code: "E03".to_owned(),
pruef_id: "11042".to_owned(),
msg_function: Some("9".to_owned()),
};
let bytes = to_bytes(&seg).unwrap();
assert_eq!(std::str::from_utf8(&bytes).unwrap(), "BGM+E03+11042+9'");
}
#[test]
fn option_none_emits_empty_element() {
let val: Option<String> = None;
let mut emitter = VecEmitter::default();
val.edifact_serialize(&mut emitter).unwrap();
assert_eq!(emitter.events[0], EdifactEvent::element(String::new()));
}
#[test]
fn option_some_emits_value() {
let val: Option<String> = Some("TEST".to_owned());
let mut emitter = VecEmitter::default();
val.edifact_serialize(&mut emitter).unwrap();
assert_eq!(emitter.events[0], EdifactEvent::element("TEST".to_owned()));
}
#[test]
fn integer_types_serialize_without_alloc() {
let mut emitter = VecEmitter::default();
42u32.edifact_serialize(&mut emitter).unwrap();
assert_eq!(emitter.events[0], EdifactEvent::element("42".to_owned()));
let mut emitter2 = VecEmitter::default();
i128::MIN.edifact_serialize(&mut emitter2).unwrap();
assert_eq!(
emitter2.events[0],
EdifactEvent::element("-170141183460469231731687303715884105728".to_owned())
);
}
#[test]
fn float_extremes_do_not_panic() {
use super::DecimalFloat;
let mut emitter = VecEmitter::default();
DecimalFloat(f64::MAX)
.edifact_serialize(&mut emitter)
.unwrap();
let s = match &emitter.events[0] {
EdifactEvent::Element { value } => value.clone(),
_ => panic!("expected Element event"),
};
assert!(!s.is_empty());
let mut emitter2 = VecEmitter::default();
DecimalFloat(f32::MAX)
.edifact_serialize(&mut emitter2)
.unwrap();
assert!(matches!(&emitter2.events[0], EdifactEvent::Element { .. }));
}
#[test]
fn vec_serializes_each_item() {
let segments = vec![
BgmSegment {
doc_name_code: "E03".to_owned(),
pruef_id: "11042".to_owned(),
msg_function: None,
},
BgmSegment {
doc_name_code: "E01".to_owned(),
pruef_id: "11043".to_owned(),
msg_function: None,
},
];
let bytes = to_bytes(&segments).unwrap();
let s = std::str::from_utf8(&bytes).unwrap();
assert!(s.contains("BGM+E03+11042"));
assert!(s.contains("BGM+E01+11043"));
}
}