Skip to main content

oak_pretty_print/document/
mod.rs

1use crate::{config::FormatConfig, document::printer::Printer};
2use alloc::{borrow::Cow, boxed::Box, string::String, vec::Vec};
3use core::fmt;
4
5pub mod printer;
6
7/// Document 抽象,用于描述布局逻辑
8#[derive(Clone, serde::Serialize)]
9#[serde(tag = "kind", content = "value", rename_all = "camelCase")]
10pub enum Document<'a> {
11    /// 空文档
12    Nil,
13    /// 纯文本
14    Text(Cow<'a, str>),
15    /// 连接多个文档
16    Concat(Vec<Document<'a>>),
17    /// 组合文档,作为换行计算的最小单位
18    Group(Box<Document<'a>>),
19    /// 增加缩进
20    Indent(Box<Document<'a>>),
21    /// 强制换行
22    Line,
23    /// 软换行:如果 Group 展开则为换行,否则为空
24    SoftLine,
25    /// 软换行(带空格):如果 Group 展开则为换行,否则为空格
26    SoftLineSpace,
27    /// 强制换行且会导致父级 Group 也展开
28    HardLine,
29}
30
31impl<'a> fmt::Debug for Document<'a> {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        #[cfg(debug_assertions)]
34        {
35            match self {
36                Document::Nil => write!(f, "Nil"),
37                Document::Text(s) => write!(f, "Text({:?})", s),
38                Document::Concat(docs) => f.debug_list().entries(docs).finish(),
39                Document::Group(d) => f.debug_tuple("Group").field(d).finish(),
40                Document::Indent(d) => f.debug_tuple("Indent").field(d).finish(),
41                Document::Line => write!(f, "Line"),
42                Document::SoftLine => write!(f, "SoftLine"),
43                Document::SoftLineSpace => write!(f, "SoftLineSpace"),
44                Document::HardLine => write!(f, "HardLine"),
45            }
46        }
47        #[cfg(not(debug_assertions))]
48        {
49            match self {
50                Document::Nil => write!(f, "Doc::Nil"),
51                Document::Text(_) => write!(f, "Doc::Text"),
52                Document::Concat(_) => write!(f, "Doc::Concat"),
53                Document::Group(_) => write!(f, "Doc::Group"),
54                Document::Indent(_) => write!(f, "Doc::Indent"),
55                Document::Line => write!(f, "Doc::Line"),
56                Document::SoftLine => write!(f, "Doc::SoftLine"),
57                Document::SoftLineSpace => write!(f, "Doc::SoftLineSpace"),
58                Document::HardLine => write!(f, "Doc::HardLine"),
59            }
60        }
61    }
62}
63
64impl<'a> Document<'a> {
65    /// 渲染 Document 为字符串
66    pub fn render(&self, config: FormatConfig) -> String {
67        Printer::new(config).print(self)
68    }
69
70    /// 创建文本文档
71    pub fn text<S: Into<Cow<'a, str>>>(text: S) -> Self {
72        Document::Text(text.into())
73    }
74
75    /// 连接多个文档
76    pub fn concat(docs: Vec<Document<'a>>) -> Self {
77        Document::Concat(docs)
78    }
79
80    /// 创建组合文档
81    pub fn group(doc: Document<'a>) -> Self {
82        Document::Group(Box::new(doc))
83    }
84
85    /// 创建缩进文档
86    pub fn indent(doc: Document<'a>) -> Self {
87        Document::Indent(Box::new(doc))
88    }
89
90    /// 辅助方法:将多个文档用指定分隔符连接
91    pub fn join<I>(docs: I, separator: Document<'a>) -> Self
92    where
93        I: IntoIterator<Item = Document<'a>>,
94    {
95        let mut result = Vec::new();
96        for (i, doc) in docs.into_iter().enumerate() {
97            if i > 0 {
98                result.push(separator.clone());
99            }
100            result.push(doc);
101        }
102        Document::Concat(result)
103    }
104}
105
106impl<'a> From<Cow<'a, str>> for Document<'a> {
107    fn from(s: Cow<'a, str>) -> Self {
108        Document::Text(s)
109    }
110}
111
112impl From<String> for Document<'_> {
113    fn from(s: String) -> Self {
114        Document::Text(s.into())
115    }
116}
117
118impl<'a> From<&'a str> for Document<'a> {
119    fn from(s: &'a str) -> Self {
120        Document::Text(s.into())
121    }
122}
123
124pub trait JoinDoc<'a> {
125    fn join_doc(self, separator: Document<'a>) -> Vec<Document<'a>>;
126}
127
128impl<'a, I> JoinDoc<'a> for I
129where
130    I: IntoIterator<Item = Document<'a>>,
131{
132    fn join_doc(self, separator: Document<'a>) -> Vec<Document<'a>> {
133        let mut result = Vec::new();
134        for (i, doc) in self.into_iter().enumerate() {
135            if i > 0 {
136                result.push(separator.clone());
137            }
138            result.push(doc);
139        }
140        result
141    }
142}