#printing #pretty-print #speed #focus #output #group #compactness

elegance

一个专注于速度和紧凑性的 Rust 优雅打印库

1 个不稳定版本

0.1.0 2024年7月31日

#81值格式化

Download history 119/week @ 2024-07-30

每月119次下载

Apache-2.0

16KB
241

Elegance

GitHub Actions Workflow Status docs.rs

Elegance 是一个专注于速度和紧凑性的 Rust 优雅打印库。

使用方法

创建一个打印机

let mut pp = Printer::new(String::new(), 40);

添加一些文本和空格

pp.text("Hello, world!")?;
pp.space()?; // breakable space
pp.hard_break()?; // forced line break

将结构封装在组中

pp.group(2, |pp| {
    pp.text("foo")?;
    pp.space()?;
    pp.text("bar")
})?;

完成文档

let result = pp.finish()?;
println!("{}", result);

流式输出

打印机可以写入任何 std::io::Write 实现。

use elegant::{Printer, Io};
let mut pp = Printer::new(Io(std::io::stdout()), 40);

示例

use elegance::*;

enum SExp {
    Atom(u32),
    List(Vec<SExp>),
}

impl SExp {
    pub fn print<R: Render>(&self, pp: &mut Printer<R>) -> Result<(), R::Error> {
        match self {
            SExp::Atom(x) => pp.text(format!("{}", x))?,
            SExp::List(xs) => pp.group(1, |pp| {
                pp.text("(")?;
                if let Some((first, rest)) = xs.split_first() {
                    first.print(pp)?;
                    for v in rest {
                        pp.space()?;
                        v.print(pp)?;
                    }
                }
                pp.text(")")
            })?,
        }
        Ok(())
    }
}

fn main() {
    let exp = SExp::List(vec![
        SExp::List(vec![SExp::Atom(1)]),
        SExp::List(vec![SExp::Atom(2), SExp::Atom(3)]),
        SExp::List(vec![SExp::Atom(4), SExp::Atom(5), SExp::Atom(6)]),
    ]);

    let mut printer = Printer::new(String::new(), 10);
    exp.print(&mut printer).unwrap();
    let result = printer.finish().unwrap();

    assert_eq!(result, indoc::indoc! {"
        ((1)
         (2 3)
         (4 5 6))"});
}

与其他库的差异

此包实现了一个 Oppen 风格的优雅打印库,而 pretty 包遵循 Walder 风格的方法。

在 Walder 风格的优雅打印中,文档是通过可组合的 Doc 类型和解算符构建的。以下是一个示例

impl SExp {
    /// Returns a pretty printed representation of `self`.
    pub fn to_doc(&self) -> RcDoc<()> {
        match *self {
            Atom(ref x) => RcDoc::as_string(x),
            List(ref xs) =>
                RcDoc::text("(")
                    .append(RcDoc::intersperse(xs.iter().map(|x| x.to_doc()), Doc::line()).nest(1).group())
                    .append(RcDoc::text(")"))
        }
    }
}

此方法特别适合函数式编程语言,但可能不适合 Rust。将语法树转换为 Doc 需要分配与整个文档大小成比例的额外内存。

与这个库的关键区别在于,它通过控制流而不是数据结构来表示打印文档的结构。因此,打印过程是完全流式的,并且在整个过程中占用恒定的内存。

参考

无运行时依赖