1mod case;
16mod encoding;
17mod sanitize;
18mod shell;
19mod whitespace;
20
21#[cfg(test)]
22mod tests;
23
24use std::fmt;
25
26pub use sanitize::{paste_contains_control_chars, sanitize_paste_content};
28
29use case::{camel_case, kebab_case, pascal_case, screaming_snake_case, snake_case, title_case};
30use encoding::{
31 base64_decode, base64_encode, hex_decode, hex_encode, json_escape, json_unescape, url_decode,
32 url_encode,
33};
34use shell::{shell_backslash_escape, shell_double_quote, shell_single_quote};
35use whitespace::{
36 add_newlines, collapse_spaces, normalize_line_endings, paste_as_single_line,
37 remove_empty_lines, remove_newlines, trim_lines,
38};
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum PasteTransform {
46 ShellSingleQuotes,
48 ShellDoubleQuotes,
49 ShellBackslash,
50
51 CaseUppercase,
53 CaseLowercase,
54 CaseTitleCase,
55 CaseCamelCase,
56 CasePascalCase,
57 CaseSnakeCase,
58 CaseScreamingSnake,
59 CaseKebabCase,
60
61 NewlineSingleLine,
63 NewlineAddNewlines,
64 NewlineRemoveNewlines,
65
66 WhitespaceTrim,
68 WhitespaceTrimLines,
69 WhitespaceCollapseSpaces,
70 WhitespaceTabsToSpaces,
71 WhitespaceSpacesToTabs,
72 WhitespaceRemoveEmptyLines,
73 WhitespaceNormalizeLineEndings,
74
75 EncodeBase64,
77 DecodeBase64,
78 EncodeUrl,
79 DecodeUrl,
80 EncodeHex,
81 DecodeHex,
82 EncodeJsonEscape,
83 DecodeJsonUnescape,
84}
85
86impl PasteTransform {
87 pub fn display_name(&self) -> &'static str {
89 match self {
90 Self::ShellSingleQuotes => "Shell: Single Quotes",
92 Self::ShellDoubleQuotes => "Shell: Double Quotes",
93 Self::ShellBackslash => "Shell: Backslash Escape",
94
95 Self::CaseUppercase => "Case: UPPERCASE",
97 Self::CaseLowercase => "Case: lowercase",
98 Self::CaseTitleCase => "Case: Title Case",
99 Self::CaseCamelCase => "Case: camelCase",
100 Self::CasePascalCase => "Case: PascalCase",
101 Self::CaseSnakeCase => "Case: snake_case",
102 Self::CaseScreamingSnake => "Case: SCREAMING_SNAKE",
103 Self::CaseKebabCase => "Case: kebab-case",
104
105 Self::NewlineSingleLine => "Newline: Paste as Single Line",
107 Self::NewlineAddNewlines => "Newline: Add Newlines",
108 Self::NewlineRemoveNewlines => "Newline: Remove Newlines",
109
110 Self::WhitespaceTrim => "Whitespace: Trim",
112 Self::WhitespaceTrimLines => "Whitespace: Trim Lines",
113 Self::WhitespaceCollapseSpaces => "Whitespace: Collapse Spaces",
114 Self::WhitespaceTabsToSpaces => "Whitespace: Tabs to Spaces",
115 Self::WhitespaceSpacesToTabs => "Whitespace: Spaces to Tabs",
116 Self::WhitespaceRemoveEmptyLines => "Whitespace: Remove Empty Lines",
117 Self::WhitespaceNormalizeLineEndings => "Whitespace: Normalize Line Endings",
118
119 Self::EncodeBase64 => "Encode: Base64",
121 Self::DecodeBase64 => "Decode: Base64",
122 Self::EncodeUrl => "Encode: URL",
123 Self::DecodeUrl => "Decode: URL",
124 Self::EncodeHex => "Encode: Hex",
125 Self::DecodeHex => "Decode: Hex",
126 Self::EncodeJsonEscape => "Encode: JSON Escape",
127 Self::DecodeJsonUnescape => "Decode: JSON Unescape",
128 }
129 }
130
131 pub fn description(&self) -> &'static str {
133 match self {
134 Self::ShellSingleQuotes => "Wrap in single quotes, escape internal quotes",
135 Self::ShellDoubleQuotes => "Wrap in double quotes, escape special chars",
136 Self::ShellBackslash => "Escape special characters with backslash",
137
138 Self::CaseUppercase => "Convert all characters to uppercase",
139 Self::CaseLowercase => "Convert all characters to lowercase",
140 Self::CaseTitleCase => "Capitalize first letter of each word",
141 Self::CaseCamelCase => "Convert to camelCase (firstWordLower)",
142 Self::CasePascalCase => "Convert to PascalCase (AllWordsCapitalized)",
143 Self::CaseSnakeCase => "Convert to snake_case (lowercase_with_underscores)",
144 Self::CaseScreamingSnake => "Convert to SCREAMING_SNAKE_CASE",
145 Self::CaseKebabCase => "Convert to kebab-case (lowercase-with-hyphens)",
146
147 Self::NewlineSingleLine => "Strip all newlines, join into a single line",
148 Self::NewlineAddNewlines => "Ensure text ends with a newline after each line",
149 Self::NewlineRemoveNewlines => "Remove all newline characters",
150
151 Self::WhitespaceTrim => "Remove leading and trailing whitespace",
152 Self::WhitespaceTrimLines => "Trim whitespace from each line",
153 Self::WhitespaceCollapseSpaces => "Replace multiple spaces with single space",
154 Self::WhitespaceTabsToSpaces => "Convert tabs to 4 spaces",
155 Self::WhitespaceSpacesToTabs => "Convert 4 spaces to tabs",
156 Self::WhitespaceRemoveEmptyLines => "Remove blank lines",
157 Self::WhitespaceNormalizeLineEndings => "Convert line endings to LF (\\n)",
158
159 Self::EncodeBase64 => "Encode text as Base64",
160 Self::DecodeBase64 => "Decode Base64 to text",
161 Self::EncodeUrl => "URL/percent-encode special characters",
162 Self::DecodeUrl => "Decode URL/percent-encoded text",
163 Self::EncodeHex => "Encode text as hexadecimal",
164 Self::DecodeHex => "Decode hexadecimal to text",
165 Self::EncodeJsonEscape => "Escape text for JSON string",
166 Self::DecodeJsonUnescape => "Unescape JSON string escapes",
167 }
168 }
169
170 pub fn all() -> &'static [PasteTransform] {
172 &[
173 Self::ShellSingleQuotes,
175 Self::ShellDoubleQuotes,
176 Self::ShellBackslash,
177 Self::CaseUppercase,
179 Self::CaseLowercase,
180 Self::CaseTitleCase,
181 Self::CaseCamelCase,
182 Self::CasePascalCase,
183 Self::CaseSnakeCase,
184 Self::CaseScreamingSnake,
185 Self::CaseKebabCase,
186 Self::NewlineSingleLine,
188 Self::NewlineAddNewlines,
189 Self::NewlineRemoveNewlines,
190 Self::WhitespaceTrim,
192 Self::WhitespaceTrimLines,
193 Self::WhitespaceCollapseSpaces,
194 Self::WhitespaceTabsToSpaces,
195 Self::WhitespaceSpacesToTabs,
196 Self::WhitespaceRemoveEmptyLines,
197 Self::WhitespaceNormalizeLineEndings,
198 Self::EncodeBase64,
200 Self::DecodeBase64,
201 Self::EncodeUrl,
202 Self::DecodeUrl,
203 Self::EncodeHex,
204 Self::DecodeHex,
205 Self::EncodeJsonEscape,
206 Self::DecodeJsonUnescape,
207 ]
208 }
209
210 pub fn matches_query(&self, query: &str) -> bool {
212 if query.is_empty() {
213 return true;
214 }
215 let name = self.display_name().to_lowercase();
216 let query = query.to_lowercase();
217 name.contains(&query)
219 }
220}
221
222impl fmt::Display for PasteTransform {
223 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
224 write!(f, "{}", self.display_name())
225 }
226}
227
228pub fn transform(input: &str, transform: PasteTransform) -> Result<String, String> {
233 match transform {
234 PasteTransform::ShellSingleQuotes => Ok(shell_single_quote(input)),
236 PasteTransform::ShellDoubleQuotes => Ok(shell_double_quote(input)),
237 PasteTransform::ShellBackslash => Ok(shell_backslash_escape(input)),
238
239 PasteTransform::CaseUppercase => Ok(input.to_uppercase()),
241 PasteTransform::CaseLowercase => Ok(input.to_lowercase()),
242 PasteTransform::CaseTitleCase => Ok(title_case(input)),
243 PasteTransform::CaseCamelCase => Ok(camel_case(input)),
244 PasteTransform::CasePascalCase => Ok(pascal_case(input)),
245 PasteTransform::CaseSnakeCase => Ok(snake_case(input)),
246 PasteTransform::CaseScreamingSnake => Ok(screaming_snake_case(input)),
247 PasteTransform::CaseKebabCase => Ok(kebab_case(input)),
248
249 PasteTransform::NewlineSingleLine => Ok(paste_as_single_line(input)),
251 PasteTransform::NewlineAddNewlines => Ok(add_newlines(input)),
252 PasteTransform::NewlineRemoveNewlines => Ok(remove_newlines(input)),
253
254 PasteTransform::WhitespaceTrim => Ok(input.trim().to_string()),
256 PasteTransform::WhitespaceTrimLines => Ok(trim_lines(input)),
257 PasteTransform::WhitespaceCollapseSpaces => Ok(collapse_spaces(input)),
258 PasteTransform::WhitespaceTabsToSpaces => Ok(input.replace('\t', " ")),
259 PasteTransform::WhitespaceSpacesToTabs => Ok(input.replace(" ", "\t")),
260 PasteTransform::WhitespaceRemoveEmptyLines => Ok(remove_empty_lines(input)),
261 PasteTransform::WhitespaceNormalizeLineEndings => Ok(normalize_line_endings(input)),
262
263 PasteTransform::EncodeBase64 => Ok(base64_encode(input)),
265 PasteTransform::DecodeBase64 => base64_decode(input),
266 PasteTransform::EncodeUrl => Ok(url_encode(input)),
267 PasteTransform::DecodeUrl => url_decode(input),
268 PasteTransform::EncodeHex => Ok(hex_encode(input)),
269 PasteTransform::DecodeHex => hex_decode(input),
270 PasteTransform::EncodeJsonEscape => Ok(json_escape(input)),
271 PasteTransform::DecodeJsonUnescape => json_unescape(input),
272 }
273}