// SPDX-FileCopyrightText: 2024 Topola contributors // // SPDX-License-Identifier: MIT use super::ListToken; use std::io; pub trait WriteSes { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error>; } impl WriteSes for char { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } impl WriteSes for String { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { let string = if self.is_empty() { "\"\"".to_string() } else if self.contains(|i: char| i == ' ' || i == '(' || i == ')' || i == '\n') { format!("\"{}\"", self) } else { self.to_string() }; writer.write_leaf(string) } } impl WriteSes for bool { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf( match self { true => "on", false => "off", } .to_string(), ) } } impl WriteSes for i32 { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } impl WriteSes for u32 { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } impl WriteSes for usize { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } impl WriteSes for f32 { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } impl WriteSes for f64 { fn write_dsn(&self, writer: &mut ListWriter) -> Result<(), io::Error> { writer.write_leaf(self.to_string()) } } pub struct ListWriter { writable: W, indent_level: usize, multiline_level: usize, pub line_len: usize, } impl ListWriter { pub fn new(writable: W) -> Self { Self { writable, indent_level: 0, multiline_level: 0, line_len: 0, } } } impl ListWriter { pub fn write_token(&mut self, token: ListToken) -> Result<(), io::Error> { let len = token.len(); match token { ListToken::Start { name } => { write!( self.writable, "\n{}({}", " ".repeat(self.indent_level), name )?; self.multiline_level = self.indent_level; self.line_len = 2 * self.indent_level + len; self.indent_level += 1; Ok(()) } ListToken::Leaf { value } => { self.line_len += 1 + len; write!(self.writable, " {}", value) } ListToken::End => { if self.indent_level <= self.multiline_level { self.indent_level -= 1; self.line_len = 2 * self.indent_level + len; write!(self.writable, "\n{})", " ".repeat(self.indent_level)) } else { self.indent_level -= 1; self.line_len += len; write!(self.writable, ")") } } } } pub fn write_leaf(&mut self, value: String) -> Result<(), io::Error> { self.write_token(ListToken::Leaf { value }) } pub fn write_value>(&mut self, value: &T) -> Result<(), io::Error> { value.write_dsn(self) } pub fn write_named>(&mut self, name: &str, value: &T) -> Result<(), io::Error> { self.write_token(ListToken::Start { name: name.to_string(), })?; self.write_value(value)?; self.write_token(ListToken::End)?; Ok(()) } pub fn write_optional>( &mut self, name: &str, optional: &Option, ) -> Result<(), io::Error> { if let Some(value) = optional { self.write_named(name, value)?; } Ok(()) } pub fn write_array>(&mut self, array: &[T]) -> Result<(), io::Error> { for elem in array { self.write_value(elem)?; } Ok(()) } pub fn write_named_array>( &mut self, name: &str, array: &[T], ) -> Result<(), io::Error> { for elem in array { self.write_named(name, elem)?; } Ok(()) } }