11use std:: fmt:: Write as _;
22
3- pub ( crate ) fn autoformat_leading ( leading : & mut String , indent : usize , no_comments : bool ) {
3+ /// Formatting configuration for use with [`KdlDocument::autoformat_config`](`crate::KdlDocument::autoformat_config`)
4+ /// and [`KdlNode::autoformat_config`](`crate::KdlNode::autoformat_config`).
5+ #[ non_exhaustive]
6+ #[ derive( Debug ) ]
7+ pub struct FormatConfig < ' a > {
8+ /// How deeply to indent the overall node or document,
9+ /// in repetitions of [`indent`](`FormatConfig::indent`).
10+ /// Defaults to `0`.
11+ pub indent_level : usize ,
12+
13+ /// The indentation to use at each level. Defaults to four spaces.
14+ pub indent : & ' a str ,
15+
16+ /// Whether to remove comments. Defaults to `false`.
17+ pub no_comments : bool ,
18+ }
19+
20+ /// See field documentation for defaults.
21+ impl Default for FormatConfig < ' _ > {
22+ fn default ( ) -> Self {
23+ Self :: builder ( ) . build ( )
24+ }
25+ }
26+
27+ impl FormatConfig < ' _ > {
28+ /// Creates a new [`FormatConfigBuilder`] with default configuration.
29+ pub const fn builder ( ) -> FormatConfigBuilder < ' static > {
30+ FormatConfigBuilder :: new ( )
31+ }
32+ }
33+
34+ /// A [`FormatConfig`] builder.
35+ ///
36+ /// Note that setters can be repeated.
37+ #[ derive( Debug , Default ) ]
38+ pub struct FormatConfigBuilder < ' a > ( FormatConfig < ' a > ) ;
39+
40+ impl < ' a > FormatConfigBuilder < ' a > {
41+ /// Creates a new [`FormatConfig`] builder with default configuration.
42+ pub const fn new ( ) -> Self {
43+ FormatConfigBuilder ( FormatConfig {
44+ indent_level : 0 ,
45+ indent : " " ,
46+ no_comments : false ,
47+ } )
48+ }
49+
50+ /// How deeply to indent the overall node or document,
51+ /// in repetitions of [`indent`](`FormatConfig::indent`).
52+ /// Defaults to `0` iff not specified.
53+ pub const fn maybe_indent_level ( mut self , indent_level : Option < usize > ) -> Self {
54+ if let Some ( indent_level) = indent_level {
55+ self . 0 . indent_level = indent_level;
56+ }
57+ self
58+ }
59+
60+ /// How deeply to indent the overall node or document,
61+ /// in repetitions of [`indent`](`FormatConfig::indent`).
62+ /// Defaults to `0` iff not specified.
63+ pub const fn indent_level ( mut self , indent_level : usize ) -> Self {
64+ self . 0 . indent_level = indent_level;
65+ self
66+ }
67+
68+ /// The indentation to use at each level.
69+ /// Defaults to four spaces iff not specified.
70+ pub const fn maybe_indent < ' b , ' c > ( self , indent : Option < & ' b str > ) -> FormatConfigBuilder < ' c >
71+ where
72+ ' a : ' b ,
73+ ' b : ' c ,
74+ {
75+ if let Some ( indent) = indent {
76+ self . indent ( indent)
77+ } else {
78+ self
79+ }
80+ }
81+
82+ /// The indentation to use at each level.
83+ /// Defaults to four spaces if not specified.
84+ pub const fn indent < ' b > ( self , indent : & ' b str ) -> FormatConfigBuilder < ' b > {
85+ FormatConfigBuilder ( FormatConfig { indent, ..self . 0 } )
86+ }
87+
88+ /// Whether to remove comments.
89+ /// Defaults to `false` iff not specified.
90+ pub const fn maybe_no_comments ( mut self , no_comments : Option < bool > ) -> Self {
91+ if let Some ( no_comments) = no_comments {
92+ self . 0 . no_comments = no_comments;
93+ }
94+ self
95+ }
96+
97+ /// Whether to remove comments.
98+ /// Defaults to `false` iff not specified.
99+ pub const fn no_comments ( mut self , no_comments : bool ) -> Self {
100+ self . 0 . no_comments = no_comments;
101+ self
102+ }
103+
104+ /// Builds the [`FormatConfig`].
105+ pub const fn build ( self ) -> FormatConfig < ' a > {
106+ self . 0
107+ }
108+ }
109+
110+ pub ( crate ) fn autoformat_leading ( leading : & mut String , config : & FormatConfig < ' _ > ) {
4111 let mut result = String :: new ( ) ;
5- if !no_comments {
112+ if !config . no_comments {
6113 let input = leading. trim ( ) ;
7114 if !input. is_empty ( ) {
8115 for line in input. lines ( ) {
9116 let trimmed = line. trim ( ) ;
10117 if !trimmed. is_empty ( ) {
11- writeln ! ( result, "{:indent$}{}" , "" , trimmed, indent = indent) . unwrap ( ) ;
118+ for _ in 0 ..config. indent_level {
119+ result. push_str ( config. indent ) ;
120+ }
121+ writeln ! ( result, "{}" , trimmed) . unwrap ( ) ;
12122 }
13123 }
14124 }
15125 }
16- write ! ( result, "{:indent$}" , "" , indent = indent) . unwrap ( ) ;
126+ for _ in 0 ..config. indent_level {
127+ result. push_str ( config. indent ) ;
128+ }
17129 * leading = result;
18130}
19131
@@ -33,3 +145,26 @@ pub(crate) fn autoformat_trailing(decor: &mut String, no_comments: bool) {
33145 }
34146 * decor = result;
35147}
148+
149+ #[ cfg( test) ]
150+ mod test {
151+ use super :: * ;
152+
153+ #[ test]
154+ fn builder ( ) -> miette:: Result < ( ) > {
155+ let built = FormatConfig :: builder ( )
156+ . indent_level ( 12 )
157+ . indent ( " \t " )
158+ . no_comments ( true )
159+ . build ( ) ;
160+ assert ! ( matches!(
161+ built,
162+ FormatConfig {
163+ indent_level: 12 ,
164+ indent: " \t " ,
165+ no_comments: true ,
166+ }
167+ ) ) ;
168+ Ok ( ( ) )
169+ }
170+ }
0 commit comments