use super::error::{BinaryError, BinaryResult};
use super::mapping::LinoDocument;
use crate::{parse_lino_to_links, LiNo};
pub fn parse_document(text: &str) -> BinaryResult<LinoDocument> {
if text
.bytes()
.all(|byte| matches!(byte, b' ' | b'\t' | b'\r' | b'\n'))
{
return Ok(Vec::new());
}
let links =
parse_lino_to_links(text).map_err(|error| BinaryError::InvalidLino(error.to_string()))?;
Ok(links.into_iter().map(canonical).collect())
}
pub fn canonical(link: LiNo<String>) -> LiNo<String> {
match link {
LiNo::Link {
id: None,
mut values,
} if values.len() == 1 && matches!(values[0], LiNo::Ref(_)) => {
values.pop().expect("one value")
}
LiNo::Link { id, values } => LiNo::Link {
id,
values: values.into_iter().map(canonical).collect(),
},
reference => reference,
}
}
pub fn format_document(document: &[LiNo<String>]) -> String {
document
.iter()
.map(format_top_level)
.collect::<Vec<_>>()
.join("\n")
}
fn format_top_level(link: &LiNo<String>) -> String {
match link {
LiNo::Link { id: None, values } if values.len() >= 2 => join_values(values),
link => format_link(link),
}
}
pub fn format_link(link: &LiNo<String>) -> String {
match link {
LiNo::Ref(reference) => format_reference(reference),
LiNo::Link { id: None, values } => match values.as_slice() {
[LiNo::Ref(reference)] => format!("(({}))", format_reference(reference)),
values => format!("({})", join_values(values)),
},
LiNo::Link {
id: Some(id),
values,
} => {
if values.is_empty() {
format!("({}:)", format_reference(id))
} else {
format!("({}: {})", format_reference(id), join_values(values))
}
}
}
}
fn join_values(values: &[LiNo<String>]) -> String {
values.iter().map(format_link).collect::<Vec<_>>().join(" ")
}
pub fn format_reference(reference: &str) -> String {
crate::reference_literal::format_reference(reference)
}