use super::*;
use std::fmt::{self, Display, Write};
struct PadAdapter<'a> {
buf: &'a mut dyn Write,
on_newline: bool,
}
impl<'a> PadAdapter<'a> {
fn new(buf: &'a mut dyn Write) -> Self {
Self { buf, on_newline: false }
}
}
impl fmt::Write for PadAdapter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
for s in s.split_inclusive('\n') {
if self.on_newline {
self.buf.write_str(FMT_PADDING)?;
}
self.on_newline = s.ends_with('\n');
self.buf.write_str(s)?;
}
Ok(())
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct IdState {
max_world_id: i32,
max_solid_id: i32,
max_side_id: i32,
max_entity_id: i32,
}
impl<S: Display + AsRef<str>> Vmf<S> {
pub fn to_string_new_ids(&self) -> String {
format!("{self:#}")
}
}
impl<S: Display + AsRef<str>> Block<S> {
pub fn fmt_new_ids(&self, f: &mut dyn Write, state: &mut IdState) -> fmt::Result {
writeln!(f, "{}", self.name)?;
let mut adapter = PadAdapter::new(f);
writeln!(adapter, "{{")?;
self.write_new_id(&mut adapter, state)?;
for prop in self.props.iter() {
if !prop.is_id() {
writeln!(adapter, "{prop}")?;
}
}
for block in self.blocks.iter() {
block.fmt_new_ids(&mut adapter, state)?;
writeln!(&mut adapter)?;
}
write!(f, "}}")?;
Ok(())
}
fn write_new_id(&self, f: &mut dyn Write, state: &mut IdState) -> fmt::Result {
let new_id = match self.name.as_ref() {
"world" => {
state.max_world_id += 1;
state.max_world_id
}
"solid" => {
state.max_solid_id += 1;
state.max_solid_id
}
"side" => {
state.max_side_id += 1;
state.max_side_id
}
"entity" => {
state.max_entity_id += 1;
state.max_entity_id
}
_ => {
writeln!(f, "{self}")?;
return Ok(());
}
};
writeln!(f, "{}", Property::<&str, i32>::new("id", new_id))?;
Ok(())
}
}
impl<S: Display + AsRef<str>> Display for Vmf<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let is_alternate = f.alternate();
let mut state = IdState::default();
let mut iter = self.inner.blocks.iter().peekable();
while let Some(block) = iter.next() {
if is_alternate {
block.fmt_new_ids(f, &mut state)?;
} else {
write!(f, "{block}")?;
}
if iter.peek().is_some() {
writeln!(f)?;
}
}
Ok(())
}
}
impl<S: Display> Display for Block<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{}", self.name)?;
let mut adapter = PadAdapter::new(f);
writeln!(adapter, "{{")?;
for prop in self.props.iter() {
writeln!(adapter, "{prop}")?;
}
for block in self.blocks.iter() {
writeln!(adapter, "{block}")?;
}
write!(f, "}}")?;
Ok(())
}
}
impl<K: Display, V: Display> Display for Property<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{}\" \"{}\"", self.key, self.value)
}
}
#[cfg(test)]
mod tests {
const INPUT_ID: &str = r#"world {}
world{ "id" "O_O two worlds incredibly rare/dumb but supported" }
solid {
"id" "not a number"
side { "id" "42" }
side { "id" "420" }
side { "id" "69" }
}
solid { "id" "infinity" }
entity {}
entity { entity {} }
"#;
#[test]
fn alternate() {
let truth_str = r#"world { "id" "1" }
world{ "id" "2" }
solid {
"id" "1"
side { "id" "1" }
side { "id" "2" }
side { "id" "3" }
}
solid { "id" "2" }
entity { "id" "1" }
entity { "id" "2" entity { "id" "3" } }
"#;
let truth = crate::parse::<&str, ()>(truth_str).unwrap();
let input = crate::parse::<&str, ()>(INPUT_ID).unwrap();
let output_str = format!("{input:#}");
let output = crate::parse::<&str, ()>(&output_str).unwrap();
eprintln!("{output_str}");
assert_eq!(truth, output);
}
}