use alloc::{borrow::Cow, vec::Vec};
#[derive(Clone, Debug)]
pub enum IcalWirePart<'a> {
Fold {
crlf: bool,
wsp: u8,
},
Soft {
crlf: bool,
},
Skipped(Cow<'a, str>),
}
impl IcalWirePart<'_> {
fn write_bytes(&self, out: &mut Vec<u8>) {
match self {
Self::Fold { crlf, wsp } => {
write_eol(*crlf, out);
out.push(*wsp);
}
Self::Soft { crlf } => {
out.push(b'=');
write_eol(*crlf, out);
}
Self::Skipped(bytes) => out.extend_from_slice(bytes.as_bytes()),
}
}
fn into_static(self) -> IcalWirePart<'static> {
match self {
Self::Fold { crlf, wsp } => IcalWirePart::Fold { crlf, wsp },
Self::Soft { crlf } => IcalWirePart::Soft { crlf },
Self::Skipped(bytes) => IcalWirePart::Skipped(Cow::Owned(bytes.into_owned())),
}
}
}
fn write_eol(crlf: bool, out: &mut Vec<u8>) {
out.extend_from_slice(if crlf { b"\r\n" } else { b"\n" });
}
#[derive(Clone, Debug, Default)]
pub struct IcalWire<'a> {
parts: Vec<(usize, IcalWirePart<'a>)>,
len: usize,
}
impl<'a> IcalWire<'a> {
pub fn is_empty(&self) -> bool {
self.parts.is_empty()
}
pub(crate) fn fold(&mut self, offset: usize, crlf: bool, wsp: u8) {
self.parts.push((offset, IcalWirePart::Fold { crlf, wsp }));
}
pub(crate) fn soft(&mut self, offset: usize, crlf: bool) {
self.parts.push((offset, IcalWirePart::Soft { crlf }));
}
pub(crate) fn skipped(&mut self, offset: usize, bytes: &'a str) {
self.parts
.push((offset, IcalWirePart::Skipped(Cow::Borrowed(bytes))));
}
pub(crate) fn seal(&mut self, len: usize) {
self.len = len;
}
pub(crate) fn prepend(&mut self, mut earlier: IcalWire<'a>) {
if earlier.parts.is_empty() {
return;
}
earlier.parts.append(&mut self.parts);
earlier.parts.sort_by_key(|(offset, _)| *offset);
self.parts = earlier.parts;
}
pub(crate) fn write_bytes(&self, logical: &[u8], out: &mut Vec<u8>) {
if self.parts.is_empty() || self.len != logical.len() {
out.extend_from_slice(logical);
return;
}
let mut at = 0;
for (offset, part) in &self.parts {
let offset = (*offset).clamp(at, logical.len());
out.extend_from_slice(&logical[at..offset]);
part.write_bytes(out);
at = offset;
}
out.extend_from_slice(&logical[at..]);
}
pub(crate) fn into_static(self) -> IcalWire<'static> {
IcalWire {
parts: self
.parts
.into_iter()
.map(|(offset, part)| (offset, part.into_static()))
.collect(),
len: self.len,
}
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use crate::tree::wire::IcalWire;
fn written(wire: &IcalWire<'_>, logical: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
wire.write_bytes(logical, &mut out);
out
}
#[test]
fn re_inserts_a_fold_where_it_was() {
let mut wire = IcalWire::default();
wire.fold(3, true, b' ');
wire.seal(6);
assert_eq!(written(&wire, b"foobar"), b"foo\r\n bar");
}
#[test]
fn re_inserts_a_blank_line_before_the_name() {
let mut wire = IcalWire::default();
wire.skipped(0, "\r\n");
wire.seal(6);
assert_eq!(written(&wire, b"foobar"), b"\r\nfoobar");
}
#[test]
fn re_inserts_a_soft_break() {
let mut wire = IcalWire::default();
wire.soft(3, false);
wire.seal(6);
assert_eq!(written(&wire, b"foobar"), b"foo=\nbar");
}
#[test]
fn drops_a_shape_taken_against_other_bytes() {
let mut wire = IcalWire::default();
wire.fold(3, true, b' ');
wire.seal(6);
assert_eq!(written(&wire, b"foobarbaz"), b"foobarbaz");
}
#[test]
fn keeps_the_order_of_pieces_at_one_offset() {
let mut wire = IcalWire::default();
wire.skipped(0, "\r\n");
wire.skipped(0, " ");
wire.seal(3);
assert_eq!(written(&wire, b"foo"), b"\r\n foo");
}
#[test]
fn prepends_an_earlier_shape_before_a_later_one() {
let mut earlier = IcalWire::default();
earlier.skipped(0, "\r\n");
let mut wire = IcalWire::default();
wire.skipped(3, "=");
wire.seal(3);
wire.prepend(earlier);
assert_eq!(written(&wire, b"foo"), b"\r\nfoo=");
}
#[test]
fn orders_a_merged_shape_by_offset_rather_than_by_list() {
let mut earlier = IcalWire::default();
earlier.soft(4, true);
let mut wire = IcalWire::default();
wire.skipped(3, "=");
wire.seal(3);
wire.prepend(earlier);
assert_eq!(written(&wire, b"foo"), b"foo==\r\n");
}
}