1
1
use std:: fmt:: Write as _;
2
2
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 < ' _ > ) {
4
111
let mut result = String :: new ( ) ;
5
- if !no_comments {
112
+ if !config . no_comments {
6
113
let input = leading. trim ( ) ;
7
114
if !input. is_empty ( ) {
8
115
for line in input. lines ( ) {
9
116
let trimmed = line. trim ( ) ;
10
117
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 ( ) ;
12
122
}
13
123
}
14
124
}
15
125
}
16
- write ! ( result, "{:indent$}" , "" , indent = indent) . unwrap ( ) ;
126
+ for _ in 0 ..config. indent_level {
127
+ result. push_str ( config. indent ) ;
128
+ }
17
129
* leading = result;
18
130
}
19
131
@@ -33,3 +145,26 @@ pub(crate) fn autoformat_trailing(decor: &mut String, no_comments: bool) {
33
145
}
34
146
* decor = result;
35
147
}
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