use std::io;
pub use crate::style::KeywordStyle;
use crate::{Atom, Number, Value};
#[derive(Clone, Debug)]
pub struct Options {
keyword_style: KeywordStyle,
nil_style: NilStyle,
bool_style: BoolStyle,
}
impl Options {
pub fn elisp() -> Self {
Options {
keyword_style: KeywordStyle::ColonPrefix,
nil_style: NilStyle::Symbol,
bool_style: BoolStyle::Symbol,
}
}
pub fn with_keyword_style(mut self, style: KeywordStyle) -> Self {
self.keyword_style = style;
self
}
pub fn with_nil_style(mut self, style: NilStyle) -> Self {
self.nil_style = style;
self
}
pub fn with_bool_style(mut self, style: BoolStyle) -> Self {
self.bool_style = style;
self
}
}
impl Default for Options {
fn default() -> Self {
Options {
keyword_style: KeywordStyle::Octothorpe,
nil_style: NilStyle::Token,
bool_style: BoolStyle::Token,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum NilStyle {
Symbol,
Token,
EmptyList,
False,
}
#[derive(Debug, Clone, Copy)]
pub enum BoolStyle {
Token,
Symbol,
}
pub enum CharEscape {
Quote,
ReverseSolidus,
Solidus,
Backspace,
FormFeed,
LineFeed,
CarriageReturn,
Tab,
AsciiControl(u8),
}
impl CharEscape {
#[inline]
fn from_escape_table(escape: u8, byte: u8) -> CharEscape {
match escape {
self::BB => CharEscape::Backspace,
self::TT => CharEscape::Tab,
self::NN => CharEscape::LineFeed,
self::FF => CharEscape::FormFeed,
self::RR => CharEscape::CarriageReturn,
self::QU => CharEscape::Quote,
self::BS => CharEscape::ReverseSolidus,
self::UU => CharEscape::AsciiControl(byte),
_ => unreachable!(),
}
}
}
pub trait Formatter {
#[inline]
fn write_nil<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"#nil")
}
#[inline]
fn write_bool<W: ?Sized>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(if value { b"#t" } else { b"#f" })
}
#[inline]
fn write_number<W: ?Sized>(&mut self, writer: &mut W, value: &Number) -> io::Result<()>
where
W: io::Write,
{
value.write(writer)
}
#[inline]
fn begin_string<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"\"")
}
#[inline]
fn end_string<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"\"")
}
#[inline]
fn write_string_fragment<W: ?Sized>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(fragment.as_bytes())
}
#[inline]
fn write_char_escape<W: ?Sized>(
&mut self,
writer: &mut W,
char_escape: CharEscape,
) -> io::Result<()>
where
W: io::Write,
{
use self::CharEscape::*;
let s = match char_escape {
Quote => b"\\\"",
ReverseSolidus => b"\\\\",
Solidus => b"\\/",
Backspace => b"\\b",
FormFeed => b"\\f",
LineFeed => b"\\n",
CarriageReturn => b"\\r",
Tab => b"\\t",
AsciiControl(byte) => {
static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
let bytes = &[
b'\\',
b'u',
b'0',
b'0',
HEX_DIGITS[(byte >> 4) as usize],
HEX_DIGITS[(byte & 0xF) as usize],
];
return writer.write_all(bytes);
}
};
writer.write_all(s)
}
#[inline]
fn write_symbol<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(name.as_bytes())
}
#[inline]
fn write_keyword<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"#:")?;
writer.write_all(name.as_bytes())
}
#[inline]
fn begin_list<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b"(")
}
#[inline]
fn begin_list_element<W: ?Sized>(&mut self, writer: &mut W, first: bool) -> io::Result<()>
where
W: io::Write,
{
if first {
Ok(())
} else {
writer.write_all(b" ")
}
}
#[inline]
fn end_list_element<W: ?Sized>(&mut self, _writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
Ok(())
}
#[inline]
fn end_list<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b")")
}
#[inline]
fn write_dot<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
writer.write_all(b".")
}
}
#[derive(Clone, Debug)]
pub struct DefaultFormatter;
impl Formatter for DefaultFormatter {}
#[derive(Clone, Debug)]
pub struct CustomizedFormatter {
options: Options,
}
impl Formatter for CustomizedFormatter {
fn write_nil<W: ?Sized>(&mut self, writer: &mut W) -> io::Result<()>
where
W: io::Write,
{
match self.options.nil_style {
NilStyle::EmptyList => writer.write_all(b"()"),
NilStyle::Symbol => writer.write_all(b"nil"),
NilStyle::Token => writer.write_all(b"#nil"),
NilStyle::False => self.write_bool(writer, false),
}
}
fn write_bool<W: ?Sized>(&mut self, writer: &mut W, value: bool) -> io::Result<()>
where
W: io::Write,
{
match self.options.bool_style {
BoolStyle::Symbol => writer.write_all(if value { b"t" } else { b"nil" }),
BoolStyle::Token => writer.write_all(if value { b"#t" } else { b"#f" }),
}
}
fn write_keyword<W: ?Sized>(&mut self, writer: &mut W, name: &str) -> io::Result<()>
where
W: io::Write,
{
match self.options.keyword_style {
KeywordStyle::ColonPostfix => {
writer.write_all(name.as_bytes())?;
writer.write_all(b":")
}
KeywordStyle::ColonPrefix => {
writer.write_all(b":")?;
writer.write_all(name.as_bytes())
}
KeywordStyle::Octothorpe => {
writer.write_all(b"#:")?;
writer.write_all(name.as_bytes())
}
}
}
}
#[derive(Debug)]
pub struct Printer<W, F = DefaultFormatter> {
writer: W,
formatter: F,
}
impl<W> Printer<W, CustomizedFormatter>
where
W: io::Write,
{
pub fn with_options(writer: W, options: Options) -> Self {
Printer {
writer,
formatter: CustomizedFormatter { options },
}
}
}
impl<W, F> Printer<W, F>
where
W: io::Write,
F: Formatter,
{
#[inline]
pub fn with_formatter(writer: W, formatter: F) -> Self {
Printer { writer, formatter }
}
#[inline]
pub fn into_inner(self) -> W {
self.writer
}
fn print_atom(&mut self, atom: &Atom) -> io::Result<()> {
match atom {
Atom::Nil => self.formatter.write_nil(&mut self.writer),
Atom::Bool(b) => self.formatter.write_bool(&mut self.writer, *b),
Atom::Number(n) => self.formatter.write_number(&mut self.writer, &n),
Atom::Symbol(name) => self.formatter.write_symbol(&mut self.writer, &name),
Atom::Keyword(name) => self.formatter.write_keyword(&mut self.writer, &name),
Atom::String(s) => format_escaped_str(&mut self.writer, &mut self.formatter, &s),
}
}
pub fn print(&mut self, value: &Value) -> io::Result<()> {
match value {
Value::Atom(atom) => self.print_atom(atom)?,
Value::List(elements) => {
self.formatter.begin_list(&mut self.writer)?;
for (i, element) in elements.iter().enumerate() {
self.formatter
.begin_list_element(&mut self.writer, i == 0)?;
self.print(element)?;
self.formatter.end_list_element(&mut self.writer)?;
}
self.formatter.end_list(&mut self.writer)?;
}
Value::ImproperList(elements, tail) => {
if !elements.is_empty() {
self.formatter.begin_list(&mut self.writer)?;
for (i, element) in elements.iter().enumerate() {
self.formatter
.begin_list_element(&mut self.writer, i == 0)?;
self.print(element)?;
self.formatter.end_list_element(&mut self.writer)?;
}
self.formatter.begin_list_element(&mut self.writer, false)?;
self.formatter.write_dot(&mut self.writer)?;
self.formatter.end_list_element(&mut self.writer)?;
self.formatter.begin_list_element(&mut self.writer, false)?;
self.print_atom(tail)?;
self.formatter.end_list_element(&mut self.writer)?;
self.formatter.end_list(&mut self.writer)?;
} else {
self.print_atom(tail)?;
}
}
}
Ok(())
}
}
impl<W> Printer<W>
where
W: io::Write,
{
#[inline]
pub fn new(writer: W) -> Self {
Printer::with_formatter(writer, DefaultFormatter)
}
}
impl<W, F> io::Write for Printer<W, F>
where
W: io::Write,
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.write(buf)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
fn format_escaped_str<W: ?Sized, F: ?Sized>(
writer: &mut W,
formatter: &mut F,
value: &str,
) -> io::Result<()>
where
W: io::Write,
F: Formatter,
{
formatter.begin_string(writer)?;
format_escaped_str_contents(writer, formatter, value)?;
formatter.end_string(writer)?;
Ok(())
}
fn format_escaped_str_contents<W: ?Sized, F: ?Sized>(
writer: &mut W,
formatter: &mut F,
value: &str,
) -> io::Result<()>
where
W: io::Write,
F: Formatter,
{
let bytes = value.as_bytes();
let mut start = 0;
for (i, &byte) in bytes.iter().enumerate() {
let escape = ESCAPE[byte as usize];
if escape == 0 {
continue;
}
if start < i {
formatter.write_string_fragment(writer, &value[start..i])?;
}
let char_escape = CharEscape::from_escape_table(escape, byte);
formatter.write_char_escape(writer, char_escape)?;
start = i + 1;
}
if start != bytes.len() {
formatter.write_string_fragment(writer, &value[start..])?;
}
Ok(())
}
const BB: u8 = b'b';
const TT: u8 = b't';
const NN: u8 = b'n';
const FF: u8 = b'f';
const RR: u8 = b'r';
const QU: u8 = b'"';
const BS: u8 = b'\\';
const UU: u8 = b'u';
const __: u8 = 0;
static ESCAPE: [u8; 256] = [
UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU,
UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU,
__, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
__, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __,
];
#[inline]
pub fn to_writer<W: io::Write>(writer: W, value: &Value) -> io::Result<()> {
let mut printer = Printer::new(writer);
printer.print(value)?;
Ok(())
}
#[inline]
pub fn to_writer_custom<W: io::Write>(
writer: W,
value: &Value,
options: Options,
) -> io::Result<()> {
let mut printer = Printer::with_options(writer, options);
printer.print(value)?;
Ok(())
}
#[inline]
pub fn to_vec(value: &Value) -> io::Result<Vec<u8>> {
let mut writer = Vec::with_capacity(128);
to_writer(&mut writer, value)?;
Ok(writer)
}
#[inline]
pub fn to_vec_custom(value: &Value, options: Options) -> io::Result<Vec<u8>> {
let mut writer = Vec::with_capacity(128);
to_writer_custom(&mut writer, value, options)?;
Ok(writer)
}
#[inline]
pub fn to_string(value: &Value) -> io::Result<String> {
let vec = to_vec(value)?;
let string = unsafe {
String::from_utf8_unchecked(vec)
};
Ok(string)
}
#[inline]
pub fn to_string_custom(value: &Value, options: Options) -> io::Result<String> {
let vec = to_vec_custom(value, options)?;
let string = unsafe {
String::from_utf8_unchecked(vec)
};
Ok(string)
}