24
24
fn format_line_flowed ( line : & str , prefix : & str ) -> String {
25
25
let mut result = String :: new ( ) ;
26
26
let mut buffer = prefix. to_string ( ) ;
27
- let mut after_space = false ;
27
+ let mut after_space = prefix . ends_with ( ' ' ) ;
28
28
29
29
for c in line. chars ( ) {
30
30
if c == ' ' {
@@ -55,7 +55,7 @@ fn format_line_flowed(line: &str, prefix: &str) -> String {
55
55
result + & buffer
56
56
}
57
57
58
- /// Returns text formatted according to RFC 3767 (format=flowed).
58
+ /// Returns text formatted according to RFC 3676 (format=flowed).
59
59
///
60
60
/// This function accepts text separated by LF, but returns text
61
61
/// separated by CRLF.
@@ -70,23 +70,20 @@ pub fn format_flowed(text: &str) -> String {
70
70
result += "\r \n " ;
71
71
}
72
72
73
- let line_no_prefix = line
74
- . strip_prefix ( '>' )
75
- . map ( |line| line. strip_prefix ( ' ' ) . unwrap_or ( line) ) ;
76
- let is_quote = line_no_prefix. is_some ( ) ;
77
- let line = line_no_prefix. unwrap_or ( line) . trim_end ( ) ;
78
- let prefix = if is_quote { "> " } else { "" } ;
73
+ let line = line. trim_end ( ) ;
74
+ let quote_depth = line. chars ( ) . take_while ( |& c| c == '>' ) . count ( ) ;
75
+ let ( prefix, mut line) = line. split_at ( quote_depth) ;
79
76
80
- if prefix. len ( ) + line. len ( ) > 78 {
81
- result += & format_line_flowed ( line, prefix) ;
82
- } else {
83
- result += prefix;
84
- if prefix. is_empty ( ) && ( line. starts_with ( '>' ) || line. starts_with ( ' ' ) ) {
85
- // Space stuffing, see RFC 3676
86
- result. push ( ' ' ) ;
77
+ let mut prefix = prefix. to_string ( ) ;
78
+
79
+ if quote_depth > 0 {
80
+ if let Some ( s) = line. strip_prefix ( ' ' ) {
81
+ line = s;
82
+ prefix += " " ;
87
83
}
88
- result += line;
89
84
}
85
+
86
+ result += & format_line_flowed ( line, & prefix) ;
90
87
}
91
88
92
89
result
@@ -111,16 +108,19 @@ pub fn format_flowed_quote(text: &str) -> String {
111
108
///
112
109
/// Lines must be separated by single LF.
113
110
///
114
- /// Quote processing is not supported, it is assumed that they are
115
- /// deleted during simplification.
116
- ///
117
111
/// Signature separator line is not processed here, it is assumed to
118
112
/// be stripped beforehand.
119
113
pub fn unformat_flowed ( text : & str , delsp : bool ) -> String {
120
114
let mut result = String :: new ( ) ;
121
115
let mut skip_newline = true ;
122
116
123
117
for line in text. split ( '\n' ) {
118
+ let line = if !result. is_empty ( ) && skip_newline {
119
+ line. trim_start_matches ( '>' )
120
+ } else {
121
+ line
122
+ } ;
123
+
124
124
// Revert space-stuffing
125
125
let line = line. strip_prefix ( ' ' ) . unwrap_or ( line) ;
126
126
@@ -150,8 +150,20 @@ mod tests {
150
150
151
151
#[ test]
152
152
fn test_format_flowed ( ) {
153
+ let text = "" ;
154
+ assert_eq ! ( format_flowed( text) , "" ) ;
155
+
153
156
let text = "Foo bar baz" ;
154
- assert_eq ! ( format_flowed( text) , "Foo bar baz" ) ;
157
+ assert_eq ! ( format_flowed( text) , text) ;
158
+
159
+ let text = ">Foo bar" ;
160
+ assert_eq ! ( format_flowed( text) , text) ;
161
+
162
+ let text = "> Foo bar" ;
163
+ assert_eq ! ( format_flowed( text) , text) ;
164
+
165
+ let text = ">\n \n A" ;
166
+ assert_eq ! ( format_flowed( text) , ">\r \n \r \n A" ) ;
155
167
156
168
let text = "This is the Autocrypt Setup Message used to transfer your key between clients.\n \
157
169
\n \
@@ -165,17 +177,33 @@ mod tests {
165
177
let text = "> A quote" ;
166
178
assert_eq ! ( format_flowed( text) , "> A quote" ) ;
167
179
180
+ let text = "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > A" ;
181
+ assert_eq ! (
182
+ format_flowed( text) ,
183
+ "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > \r \n > A"
184
+ ) ;
185
+
168
186
// Test space stuffing of wrapped lines
169
187
let text = "> This is the Autocrypt Setup Message used to transfer your key between clients.\n \
170
188
> \n \
171
189
> To decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device.";
172
190
let expected = "> This is the Autocrypt Setup Message used to transfer your key between \r \n \
173
191
> clients.\r \n \
174
- > \r \n \
192
+ >\r \n \
175
193
> To decrypt and use your key, open the message in an Autocrypt-compliant \r \n \
176
194
> client and enter the setup code presented on the generating device.";
177
195
assert_eq ! ( format_flowed( text) , expected) ;
178
196
197
+ let text = ">> This is the Autocrypt Setup Message used to transfer your key between clients.\n \
198
+ >> \n \
199
+ >> To decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device.";
200
+ let expected = ">> This is the Autocrypt Setup Message used to transfer your key between \r \n \
201
+ >> clients.\r \n \
202
+ >>\r \n \
203
+ >> To decrypt and use your key, open the message in an Autocrypt-compliant \r \n \
204
+ >> client and enter the setup code presented on the generating device.";
205
+ assert_eq ! ( format_flowed( text) , expected) ;
206
+
179
207
// Test space stuffing of spaces.
180
208
let text = " Foo bar baz" ;
181
209
assert_eq ! ( format_flowed( text) , " Foo bar baz" ) ;
@@ -202,6 +230,12 @@ mod tests {
202
230
let text = " Foo bar" ;
203
231
let expected = " Foo bar" ;
204
232
assert_eq ! ( unformat_flowed( text, false ) , expected) ;
233
+
234
+ let text =
235
+ "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > \n > A" ;
236
+ let expected =
237
+ "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > A" ;
238
+ assert_eq ! ( unformat_flowed( text, false ) , expected) ;
205
239
}
206
240
207
241
#[ test]
0 commit comments