use crate::tape::{self, StringBuffer, TapeBuffer};
use crate::value::Value;
#[derive(Debug)]
pub struct Document {
tape: TapeBuffer,
strings: StringBuffer,
}
impl Document {
pub(crate) fn from_parts(tape: TapeBuffer, strings: StringBuffer) -> Self {
debug_assert!(
tape.len() >= 3,
"a valid tape has at least 3 words (root, value, root)"
);
debug_assert_eq!(
tape::tag(tape[0]),
tape::TAG_ROOT,
"tape[0] must be the root word"
);
debug_assert_eq!(
tape::root_final_index(tape[0]) as usize,
tape.len() - 1,
"tape[0] must point at the final root word"
);
Self { tape, strings }
}
#[must_use]
pub fn root(&self) -> Value<'_> {
Value::new(self, 1)
}
#[must_use]
pub fn tape(&self) -> &[u64] {
self.tape.as_words()
}
#[must_use]
pub fn strings(&self) -> &StringBuffer {
&self.strings
}
#[cfg(feature = "serde")]
pub fn deserialize<'de, T>(&'de self) -> crate::serde::Result<T>
where
T: ::serde::Deserialize<'de>,
{
crate::serde::from_document(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tape::{
TAG_ROOT, make_final_root, make_null, make_root, make_string, root_final_index, tag,
};
use crate::value::ValueKind;
fn null_doc() -> Document {
let mut tape = TapeBuffer::new();
tape.push(make_root(2));
tape.push(make_null());
tape.push(make_final_root());
Document::from_parts(tape, StringBuffer::new())
}
#[test]
fn document_is_send_and_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Document>();
assert_send_sync::<TapeBuffer>();
assert_send_sync::<StringBuffer>();
}
#[test]
fn root_is_the_word_after_tape_zero() {
let doc = null_doc();
assert_eq!(doc.root().kind(), ValueKind::Null);
assert!(doc.root().is_null());
}
#[test]
fn tape_exposes_the_raw_words() {
let doc = null_doc();
let words = doc.tape();
assert_eq!(words.len(), 3);
assert_eq!(tag(words[0]), TAG_ROOT);
assert_eq!(root_final_index(words[0]), 2);
assert_eq!(words, &[make_root(2), make_null(), make_final_root()]);
}
#[test]
fn strings_exposes_the_string_buffer() {
let mut tape = TapeBuffer::new();
let mut strings = StringBuffer::new();
tape.push(make_root(2));
let off = strings.append_record("hé".as_bytes());
tape.push(make_string(off));
tape.push(make_final_root());
let doc = Document::from_parts(tape, strings);
assert_eq!(doc.root().kind(), ValueKind::String);
assert_eq!(doc.root().as_str(), Some("hé"));
assert_eq!(doc.strings().record_str(off), "hé");
assert_eq!(
doc.strings().as_bytes(),
&[3, 0, 0, 0, 0x68, 0xC3, 0xA9, 0x00]
);
}
}