use super::*;
use crate::{
nav::{Navigator, NodeValue},
Kserd,
};
use std::{error, fmt as stdfmt, ops::Deref};
pub struct Formatter<'a, 'k> {
nav: Navigator<'a, 'k>,
fmts: Vec<Fmt>,
}
impl<'a, 'k> Formatter<'a, 'k> {
pub fn new(root: &'a Kserd<'k>) -> Self {
let nav = Navigator::new(root);
let mut fmts = Vec::with_capacity(nav.len());
let mut indices = Vec::with_capacity(nav.len());
for node in nav.nodes() {
indices.push(node.index());
let mut line = max_line_repr(&node.value());
if node.is_key() {
line = std::cmp::min(line, Repr::Concise);
}
let id = true;
fmts.push(Fmt { id, line });
}
let mut fmtr = Self { nav, fmts };
match fmtr.nav.root().value() {
NodeValue::Seq(_) | NodeValue::Map(_) => {
fmtr.request_repr_alteration(0, Repr::Concise).ok()
}
_ => Some(()),
};
for i in indices {
if let Some(parent_idx) = fmtr.get(i).and_then(|x| x.parent()).map(|x| x.index()) {
let node_line = fmtr.fmts.get(i).unwrap().line;
let parent_line = fmtr.fmts.get(parent_idx).unwrap().line;
if parent_line < node_line {
fmtr.request_repr_alteration(i, parent_line).ok();
}
}
}
fmtr
}
pub fn apply_config(&mut self, config: FormattingConfig) -> &mut Self {
solver::apply_config(self, config);
self
}
pub fn fmts(&self) -> &[Fmt] {
self.fmts.as_slice()
}
pub fn id(&mut self, node_idx: usize, write: bool) -> Result<(), FmtError> {
self.fmts
.get_mut(node_idx)
.map(|fmt| fmt.id = write)
.ok_or(FmtError::IdxOutOfBounds)
}
pub fn inline(&mut self, node_idx: usize) -> Result<(), FmtError> {
self.request_repr_alteration(node_idx, Repr::Inline)
}
pub fn concise(&mut self, node_idx: usize) -> Result<(), FmtError> {
self.request_repr_alteration(node_idx, Repr::Concise)
}
pub fn verbose(&mut self, node_idx: usize) -> Result<(), FmtError> {
self.request_repr_alteration(node_idx, Repr::Verbose)
}
pub fn write_string(&self, buf: String) -> String {
write_node(buf, self.nav.root(), &self.fmts, 0)
}
fn line_repr_constraints(&self, node: &Node) -> (Repr, Option<Repr>) {
let max_line_repr = max_line_repr(&node.value());
let parent_line_repr = node
.parent()
.and_then(|p| self.fmts.get(p.index()))
.map(|f| f.line);
(max_line_repr, parent_line_repr)
}
pub(super) fn request_repr_alteration(
&mut self,
idx: usize,
requested: Repr,
) -> Result<(), FmtError> {
let node = self.nav.get(idx).ok_or(FmtError::IdxOutOfBounds)?;
let prev_repr = self.fmts.get(idx).expect("should exist").line;
if requested == prev_repr {
return Ok(()); }
if idx == 0 && requested == Repr::Verbose {
match node.value() {
NodeValue::Seq(_) | NodeValue::Map(_) => return Err(FmtError::SeqMapAtRoot),
_ => (),
}
}
let (max, parent) = self.line_repr_constraints(&node);
let repr = validate_requst_repr(max, parent, requested)?;
if let Some(x) = self.fmts.get_mut(idx) {
x.line = repr;
}
for d in node.descendants_depth() {
let current = self.fmts.get_mut(d.index()).unwrap();
if repr < current.line {
current.line = repr;
}
}
Ok(())
}
}
impl<'a, 'k> AsRef<Navigator<'a, 'k>> for Formatter<'a, 'k> {
fn as_ref(&self) -> &Navigator<'a, 'k> {
&self.nav
}
}
impl<'a, 'k> Deref for Formatter<'a, 'k> {
type Target = Navigator<'a, 'k>;
fn deref(&self) -> &Navigator<'a, 'k> {
&self.nav
}
}
fn max_line_repr(nv: &NodeValue) -> Repr {
use NodeValue::*;
match nv {
Primitive => Repr::Inline, Tuple(x) if x.is_empty() => Repr::Inline,
Seq(x) if x.is_empty() => Repr::Inline,
Map(x) if x.is_empty() => Repr::Inline,
Cntr(x) if x.is_empty() => Repr::Inline,
Tuple(_) => Repr::Concise, _ => Repr::Verbose, }
}
fn validate_requst_repr(
max_allowed: Repr,
parent: Option<Repr>,
requested: Repr,
) -> Result<Repr, FmtError> {
let parent = parent.unwrap_or(Repr::Verbose);
if requested > max_allowed {
Err(FmtError::mk_request_too_high(max_allowed, requested))
} else if requested > parent {
let e = match parent {
Repr::Inline => FmtError::ParentIsInline,
Repr::Concise => FmtError::ParentIsConcise,
Repr::Verbose => unreachable!("requested can't be higher than Verbose"),
};
Err(e)
} else {
Ok(requested)
}
}
#[derive(Debug, PartialEq)]
pub enum FmtError {
IdxOutOfBounds,
ParentIsInline,
ParentIsConcise,
RequestTooHigh(&'static str, &'static str),
SeqMapAtRoot,
}
impl FmtError {
fn mk_request_too_high(max: Repr, request: Repr) -> Self {
fn n(r: Repr) -> &'static str {
match r {
Repr::Inline => "inline",
Repr::Concise => "concise",
Repr::Verbose => "verbose",
}
};
FmtError::RequestTooHigh(n(max), n(request))
}
}
impl error::Error for FmtError {}
impl stdfmt::Display for FmtError {
fn fmt(&self, f: &mut stdfmt::Formatter) -> stdfmt::Result {
match self {
FmtError::IdxOutOfBounds => write!(
f,
"the requested item index is outside the range of the formatter"
),
FmtError::ParentIsInline => {
write!(f, "parent is 'inline', child must be formatted as 'inline'")
}
FmtError::ParentIsConcise => write!(
f,
"parent is 'concise', child must be formatted as 'inline' or 'concise'"
),
FmtError::RequestTooHigh(max, req) => write!(
f,
"requested format '{}' is higher than the value maximally allows '{}'",
req, max
),
FmtError::SeqMapAtRoot => write!(
f,
"requested to format root as verbose but the root is a sequence or a map"
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use FmtError::*;
use Repr::*;
#[test]
fn test_validate_request_repr() {
use validate_requst_repr as val;
assert_eq!(val(Inline, None, Inline), Ok(Inline));
assert_eq!(
val(Inline, None, Concise),
Err(FmtError::mk_request_too_high(Inline, Concise))
);
assert_eq!(val(Concise, Some(Inline), Concise), Err(ParentIsInline));
assert_eq!(val(Verbose, Some(Concise), Verbose), Err(ParentIsConcise));
assert_eq!(val(Verbose, Some(Verbose), Concise), Ok(Concise));
}
fn lines(fmts: &[Fmt]) -> Vec<Repr> {
fmts.iter().map(|x| x.line).collect()
}
#[test]
fn test_fmt_new() {
let kserd = Kserd::new_str("Hello");
assert_eq!(&lines(&Formatter::new(&kserd).fmts), &[Inline]);
let kserd = Kserd::new(Value::Tuple(vec![]));
assert_eq!(&lines(&Formatter::new(&kserd).fmts), &[Inline]);
let kserd = Kserd::new_cntr(Vec::<(String, _)>::new()).unwrap();
assert_eq!(&lines(&Formatter::new(&kserd).fmts), &[Inline]);
let kserd = Kserd::new(Value::Seq(vec![]));
assert_eq!(&lines(&Formatter::new(&kserd).fmts), &[Inline]);
let kserd = Kserd::new_map(vec![]);
assert_eq!(&lines(&Formatter::new(&kserd).fmts), &[Inline]);
let kserd = Kserd::new(Value::Tuple(vec![
Kserd::new_cntr(Vec::<(String, _)>::new()).unwrap(),
Kserd::new(Value::Seq(vec![])),
Kserd::new_map(vec![]),
]));
assert_eq!(
&lines(&Formatter::new(&kserd).fmts),
&[Concise, Inline, Inline, Inline]
);
}
#[test]
fn test_inlining() {
let kserd = Kserd::new(Value::Tuple(vec![
Kserd::new_unit(),
Kserd::new(Value::Seq(vec![
Kserd::new_num(1.01),
Kserd::new_str("Hello, world!"),
])),
Kserd::new_num(10101),
]));
let mut fmtr = Formatter::new(&kserd);
assert_eq!(
&lines(&fmtr.fmts),
&[Concise, Inline, Concise, Inline, Inline, Inline]
);
fmtr.inline(0).unwrap();
assert_eq!(
&lines(&fmtr.fmts),
&[Inline, Inline, Inline, Inline, Inline, Inline]
);
let kserd = Kserd::new_cntr(vec![
("a", Kserd::new_num(1.01)), ("b", Kserd::new(Value::Seq(vec![Kserd::new_num(1)]))), ])
.unwrap();
let mut fmtr = Formatter::new(&kserd);
assert_eq!(&lines(&fmtr.fmts), &[Verbose, Inline, Verbose, Inline]);
fmtr.inline(2).unwrap();
assert_eq!(&lines(&fmtr.fmts), &[Verbose, Inline, Inline, Inline]);
fmtr.inline(0).unwrap();
assert_eq!(&lines(&fmtr.fmts), &[Inline, Inline, Inline, Inline]);
}
#[test]
fn test_concising() {
let kserd = Kserd::new_cntr(vec![
(
"a",
Kserd::new(Value::Seq(vec![
Kserd::new_num(0), Kserd::new_num(1), ])),
),
(
"b",
Kserd::new(Value::Tuple(vec![
Kserd::new_num(1), Kserd::new_str("Hello"), ])),
),
(
"c",
Kserd::new_map(vec![
(
Kserd::new(Value::Tuple(vec![
Kserd::new_unit(), ])),
Kserd::new_unit(), ),
(
Kserd::new(Value::Seq(vec![
Kserd::new_unit(), ])),
Kserd::new_unit(), ),
]),
),
("d", Kserd::new_str("asdf")), ])
.unwrap();
let mut fmtr = Formatter::new(&kserd);
assert_eq!(
&lines(&fmtr.fmts),
&[
Verbose, Verbose, Inline, Inline, Concise, Inline, Inline, Verbose, Concise,
Inline, Inline, Concise, Inline, Inline, Inline
]
);
assert_eq!(fmtr.concise(1), Ok(()));
assert_eq!(
&lines(&fmtr.fmts),
&[
Verbose, Concise, Inline, Inline, Concise, Inline, Inline, Verbose, Concise,
Inline, Inline, Concise, Inline, Inline, Inline
]
);
assert_eq!(fmtr.concise(0), Ok(()));
assert_eq!(
&lines(&fmtr.fmts),
&[
Concise, Concise, Inline, Inline, Concise, Inline, Inline, Concise, Concise,
Inline, Inline, Concise, Inline, Inline, Inline
]
);
}
#[test]
fn test_verbosing() {
let kserd = Kserd::new_cntr(vec![
(
"a",
Kserd::new(Value::Seq(vec![
Kserd::new_num(0), Kserd::new_num(1), ])),
),
(
"b",
Kserd::new(Value::Tuple(vec![
Kserd::new_num(1), Kserd::new_str("Hello"), ])),
),
(
"c",
Kserd::new_map(vec![
(
Kserd::new(Value::Tuple(vec![
Kserd::new_unit(), ])),
Kserd::new_unit(), ),
(
Kserd::new(Value::Seq(vec![
Kserd::new_unit(), ])),
Kserd::new_unit(), ),
]),
),
("d", Kserd::new_str("asdf")), ])
.unwrap();
let mut fmtr = Formatter::new(&kserd);
assert_eq!(
&lines(&fmtr.fmts),
&[
Verbose, Verbose, Inline, Inline, Concise, Inline, Inline, Verbose, Concise,
Inline, Inline, Concise, Inline, Inline, Inline
]
);
assert_eq!(fmtr.inline(0), Ok(()));
assert_eq!(&lines(&fmtr.fmts), &[Inline; 15]);
fmtr.concise(0).unwrap();
assert_eq!(
&lines(&fmtr.fmts),
&[
Concise, Inline, Inline, Inline, Inline, Inline, Inline, Inline, Inline, Inline,
Inline, Inline, Inline, Inline, Inline
]
);
fmtr.concise(1).unwrap();
assert_eq!(
&lines(&fmtr.fmts),
&[
Concise, Concise, Inline, Inline, Inline, Inline, Inline, Inline, Inline, Inline,
Inline, Inline, Inline, Inline, Inline
]
);
fmtr.verbose(0).unwrap();
assert_eq!(
&lines(&fmtr.fmts),
&[
Verbose, Concise, Inline, Inline, Inline, Inline, Inline, Inline, Inline, Inline,
Inline, Inline, Inline, Inline, Inline
]
);
}
}