| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #![cfg_attr(not(test), forbid(clippy::indexing_slicing))] |
| | #![cfg_attr(not(test), forbid(clippy::string_slice))] |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | fn format_line_flowed(line: &str, prefix: &str) -> String { |
| | let mut result = String::new(); |
| | let mut buffer = prefix.to_string(); |
| | let mut after_space = prefix.ends_with(' '); |
| |
|
| | for c in line.chars() { |
| | if c == ' ' { |
| | if buffer.is_empty() { |
| | |
| | buffer.push(' '); |
| | } |
| | buffer.push(c); |
| | after_space = true; |
| | } else if c == '>' { |
| | if buffer.is_empty() { |
| | |
| | buffer.push(' '); |
| | } |
| | buffer.push(c); |
| | after_space = false; |
| | } else { |
| | if after_space && buffer.len() >= 72 && !c.is_whitespace() { |
| | |
| | result += &buffer; |
| | result += "\r\n"; |
| | buffer = prefix.to_string(); |
| | } |
| | buffer.push(c); |
| | after_space = false; |
| | } |
| | } |
| | result + &buffer |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | pub fn format_flowed(text: &str) -> String { |
| | let mut result = String::new(); |
| |
|
| | for line in text.split('\n') { |
| | if !result.is_empty() { |
| | result += "\r\n"; |
| | } |
| |
|
| | let line = line.trim_end(); |
| | let quote_depth = line.chars().take_while(|&c| c == '>').count(); |
| | let (prefix, mut line) = line.split_at(quote_depth); |
| |
|
| | let mut prefix = prefix.to_string(); |
| |
|
| | if quote_depth > 0 { |
| | if let Some(s) = line.strip_prefix(' ') { |
| | line = s; |
| | prefix += " "; |
| | } |
| | } |
| |
|
| | result += &format_line_flowed(line, &prefix); |
| | } |
| |
|
| | result |
| | } |
| |
|
| | |
| | pub fn format_flowed_quote(text: &str) -> String { |
| | let mut result = String::new(); |
| |
|
| | for line in text.split('\n') { |
| | if !result.is_empty() { |
| | result += "\n"; |
| | } |
| | result += "> "; |
| | result += line; |
| | } |
| |
|
| | format_flowed(&result) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | pub fn unformat_flowed(text: &str, delsp: bool) -> String { |
| | let mut result = String::new(); |
| | let mut skip_newline = true; |
| |
|
| | for line in text.split('\n') { |
| | let line = if !result.is_empty() && skip_newline { |
| | line.trim_start_matches('>') |
| | } else { |
| | line |
| | }; |
| |
|
| | |
| | let line = line.strip_prefix(' ').unwrap_or(line); |
| |
|
| | if !skip_newline { |
| | result.push('\n'); |
| | } |
| |
|
| | if let Some(line) = line.strip_suffix(' ') { |
| | |
| | result += line; |
| | if !delsp { |
| | result.push(' '); |
| | } |
| | skip_newline = true; |
| | } else { |
| | |
| | result += line; |
| | skip_newline = false; |
| | } |
| | } |
| | result |
| | } |
| |
|
| | #[cfg(test)] |
| | mod tests { |
| | use super::*; |
| |
|
| | #[test] |
| | fn test_format_flowed() { |
| | let text = ""; |
| | assert_eq!(format_flowed(text), ""); |
| |
|
| | let text = "Foo bar baz"; |
| | assert_eq!(format_flowed(text), text); |
| |
|
| | let text = ">Foo bar"; |
| | assert_eq!(format_flowed(text), text); |
| |
|
| | let text = "> Foo bar"; |
| | assert_eq!(format_flowed(text), text); |
| |
|
| | let text = ">\n\nA"; |
| | assert_eq!(format_flowed(text), ">\r\n\r\nA"); |
| |
|
| | let text = "This is the Autocrypt Setup Message used to transfer your key between clients.\n\ |
| | \n\ |
| | To decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device."; |
| | let expected = "This is the Autocrypt Setup Message used to transfer your key between clients.\r\n\ |
| | \r\n\ |
| | To decrypt and use your key, open the message in an Autocrypt-compliant \r\n\ |
| | client and enter the setup code presented on the generating device."; |
| | assert_eq!(format_flowed(text), expected); |
| |
|
| | let text = "> A quote"; |
| | assert_eq!(format_flowed(text), "> A quote"); |
| |
|
| | let text = "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > A"; |
| | assert_eq!( |
| | format_flowed(text), |
| | "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > \r\n> A" |
| | ); |
| |
|
| | |
| | let text = "> This is the Autocrypt Setup Message used to transfer your key between clients.\n\ |
| | > \n\ |
| | > To decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device."; |
| | let expected = "> This is the Autocrypt Setup Message used to transfer your key between \r\n\ |
| | > clients.\r\n\ |
| | >\r\n\ |
| | > To decrypt and use your key, open the message in an Autocrypt-compliant \r\n\ |
| | > client and enter the setup code presented on the generating device."; |
| | assert_eq!(format_flowed(text), expected); |
| |
|
| | let text = ">> This is the Autocrypt Setup Message used to transfer your key between clients.\n\ |
| | >> \n\ |
| | >> To decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device."; |
| | let expected = ">> This is the Autocrypt Setup Message used to transfer your key between \r\n\ |
| | >> clients.\r\n\ |
| | >>\r\n\ |
| | >> To decrypt and use your key, open the message in an Autocrypt-compliant \r\n\ |
| | >> client and enter the setup code presented on the generating device."; |
| | assert_eq!(format_flowed(text), expected); |
| |
|
| | |
| | let text = " Foo bar baz"; |
| | assert_eq!(format_flowed(text), " Foo bar baz"); |
| |
|
| | let text = " Foo bar baz"; |
| | assert_eq!(format_flowed(text), " Foo bar baz"); |
| |
|
| | let text = |
| | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAA"; |
| | let expected = |
| | "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \r\nAAAAAA"; |
| | assert_eq!(format_flowed(text), expected); |
| | } |
| |
|
| | #[test] |
| | fn test_unformat_flowed() { |
| | let text = "this is a very long message that should be wrapped using format=flowed and \n\ |
| | unwrapped on the receiver"; |
| | let expected = |
| | "this is a very long message that should be wrapped using format=flowed and \ |
| | unwrapped on the receiver"; |
| | assert_eq!(unformat_flowed(text, false), expected); |
| |
|
| | let text = " Foo bar"; |
| | let expected = " Foo bar"; |
| | assert_eq!(unformat_flowed(text, false), expected); |
| |
|
| | let text = |
| | "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > \n> A"; |
| | let expected = |
| | "> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx > A"; |
| | assert_eq!(unformat_flowed(text, false), expected); |
| | } |
| |
|
| | #[test] |
| | fn test_format_flowed_quote() { |
| | let quote = "this is a quoted line"; |
| | let expected = "> this is a quoted line"; |
| | assert_eq!(format_flowed_quote(quote), expected); |
| |
|
| | let quote = "first quoted line\nsecond quoted line"; |
| | let expected = "> first quoted line\r\n> second quoted line"; |
| | assert_eq!(format_flowed_quote(quote), expected); |
| |
|
| | let quote = "> foo bar baz"; |
| | let expected = "> > foo bar baz"; |
| | assert_eq!(format_flowed_quote(quote), expected); |
| |
|
| | let quote = "this is a very long quote that should be wrapped using format=flowed and unwrapped on the receiver"; |
| | let expected = |
| | "> this is a very long quote that should be wrapped using format=flowed and \r\n\ |
| | > unwrapped on the receiver"; |
| | assert_eq!(format_flowed_quote(quote), expected); |
| | } |
| | } |
| |
|