@@ -27,7 +27,7 @@ A flexible, composable string transformation CLI tool and Rust library. `string_
2727 - [ Range Specifications] ( #range-specifications )
2828 - [ Escaping] ( #escaping )
2929 - [ Debug Mode] ( #debug-mode )
30- - [ Examples] ( #examples )
30+ - [ More Examples] ( #more- examples )
3131- [ Testing] ( #testing )
3232- [ Contributing] ( #contributing )
3333- [ License] ( #license )
@@ -44,6 +44,10 @@ A flexible, composable string transformation CLI tool and Rust library. `string_
4444- ** Case conversion** : Uppercase and lowercase.
4545- ** Trim and strip** : Remove whitespace, custom characters, ansi sequences.
4646- ** Append and prepend** : Add text before or after.
47+ - ** Map** : Apply a sub-pipeline to each list item.
48+ - ** Sort, reverse, unique** : List operations for sorting, reversing, deduplication.
49+ - ** Pad** : Pad strings or list items to a given width.
50+ - ** Regex extract** : Extract regex matches or groups.
4751- ** Smart escaping** : Contextual pipe handling - no escaping needed in most cases.
4852- ** Flexible indices** : Python-like negative indices in ranges and slices with Rust-like syntax.
4953- ** Stdin support** : Read input from stdin when no input argument is provided.
@@ -57,7 +61,7 @@ Find the crate on [crates.io](https://crates.io/crates/string_pipeline):
5761
5862``` toml
5963[dependencies ]
60- string_pipeline = " 0.7 .0"
64+ string_pipeline = " 0.8 .0"
6165```
6266
6367---
@@ -113,17 +117,31 @@ string-pipeline "{replace:s/ /_/g|upper}" "foo bar baz"
113117string-pipeline " {split:,:..|trim|append:!}" " a, b,c , d , e "
114118# Output: a!,b!,c!,d!,e!
115119
116- # Using stdin for processing file content
117- cat data.txt | string-pipeline " {split:\n:..|trim|prepend:- }"
120+ # Using map to uppercase each item
121+ string-pipeline " {split:,:..|map:{upper}}" " a,b,c"
122+ # Output: A,B,C
123+
124+ # Sort and join
125+ string-pipeline " {split:,:..|sort:desc|join:-}" " b,a,c"
126+ # Output: c-b-a
127+
128+ # Pad each item to width 3 with '*'
129+ string-pipeline " {split:,:..|map:{pad:3:*:both}}" " a,bb,c"
130+ # Output: *a*,bb*,*c*
131+
132+ # Extract numbers from each item
133+ string-pipeline " {split:,:..|map:{regex_extract:\d+}}" " a1,b22,c333"
134+ # Output: 1,22,333
118135```
119136
120137### As a Library
121138
122139``` rust
123- use string_pipeline :: process ;
140+ use string_pipeline :: Template ;
124141
125142fn main () {
126- let result = process (" a,b,c" , " {split:,:..|join:\\n}" ). unwrap ();
143+ let template = Template :: parse (" {split:,:..|join:\\n}" ). unwrap ();
144+ let result = template . format (" a,b,c" ). unwrap ();
127145 assert_eq! (result , " a\ n b\ n c" );
128146}
129147```
@@ -150,33 +168,45 @@ Arguments to operations are separated by `:`.
150168 - ` replace:s/<pattern>/<replacement>/<flags> `
151169 - ` upper `
152170 - ` lower `
153- - ` trim `
171+ - ` trim[:left|right|both] `
154172 - ` strip:<chars> `
155173 - ` append:<suffix> `
156174 - ` prepend:<prefix> `
157175 - ` strip_ansi `
158176 - ` filter:<regex_pattern> `
159177 - ` filter_not:<regex_pattern> `
160178 - ` slice:<range> `
179+ - ` map:{<operation_list>} `
180+ - ` sort[:asc|desc] `
181+ - ` reverse `
182+ - ` unique `
183+ - ` pad:<width>[:<char>][:left|right|both] `
184+ - ` regex_extract:<pattern>[:<group>] `
161185
162186#### Supported Operations
163187
164- | Operation | Syntax | Description |
165- | ---------- | ----------------------------------------- | ------------------------------------------- |
166- | Split | ` split:<sep>:<range> ` | Split by separator, select by index/range |
167- | Join | ` join:<sep> ` | Join a list with separator |
168- | Substring | ` substring:<range> ` | Extract substrings |
169- | Replace | ` replace:s/<pattern>/<replacement>/<flags> ` | Regex replace (sed-like) |
170- | Uppercase | ` upper ` | Convert to uppercase |
171- | Lowercase | ` lower ` | Convert to lowercase |
172- | Trim | ` trim ` | Trim whitespace |
173- | Strip | ` strip:<chars> ` | Trim custom characters |
174- | Append | ` append:<suffix> ` | Append text |
175- | Prepend | ` prepend:<prefix> ` | Prepend text |
176- | StripAnsi | ` strip_ansi ` | Removes ansi escape sequences |
177- | Filter | ` filter:<regex_pattern> ` | Keep only items matching regex pattern |
178- | FilterNot | ` filter_not:<regex_pattern> ` | Remove items matching regex pattern |
179- | Slice | ` slice:<range> ` | Select elements from a list |
188+ | Operation | Syntax | Description |
189+ | ------------ | ------------------------------------------- | --------------------------------------------------- |
190+ | Split | ` split:<sep>:<range> ` | Split by separator, select by index/range |
191+ | Join | ` join:<sep> ` | Join a list with separator |
192+ | Substring | ` substring:<range> ` | Extract substring(s) by character index/range |
193+ | Replace | ` replace:s/<pattern>/<replacement>/<flags> ` | Regex replace (sed-like, supports flags) |
194+ | Uppercase | ` upper ` | Convert to uppercase |
195+ | Lowercase | ` lower ` | Convert to lowercase |
196+ | Trim | ` trim[:left\|right\|both] ` | Trim whitespace (or side-specific) |
197+ | Strip | ` strip:<chars> ` | Strip custom characters from both ends |
198+ | Append | ` append:<suffix> ` | Append text |
199+ | Prepend | ` prepend:<prefix> ` | Prepend text |
200+ | StripAnsi | ` strip_ansi ` | Remove ANSI escape sequences |
201+ | Filter | ` filter:<regex_pattern> ` | Keep only items matching regex pattern |
202+ | FilterNot | ` filter_not:<regex_pattern> ` | Remove items matching regex pattern |
203+ | Slice | ` slice:<range> ` | Select elements from a list by index/range |
204+ | Map | ` map:{<operation_list>} ` | Apply a sub-pipeline to each list item |
205+ | Sort | ` sort[:asc\|desc] ` | Sort list ascending/descending |
206+ | Reverse | ` reverse ` | Reverse string or list |
207+ | Unique | ` unique ` | Remove duplicate items from a list |
208+ | Pad | ` pad:<width>[:<char>][:left\|right\|both] ` | Pad string/list items to width with char/side |
209+ | RegexExtract | ` regex_extract:<pattern>[:<group>] ` | Extract first match or group from string/list items |
180210
181211#### Range Specifications
182212
@@ -190,7 +220,7 @@ Ranges use Rust-like syntax and support negative indices like Python:
190220| ` N.. ` | From N to end | ` {split:,:2..} ` → from 2nd to end |
191221| ` ..N ` | From start to N | ` {split:,:..3} ` → first 3 elements |
192222| ` ..=N ` | From start to N inclusive | ` {split:,:..=2} ` → first 3 elements |
193- | ` .. ` | All elements | ` {split:,:..) ` → all elements |
223+ | ` .. ` | All elements | ` {split:,:..} ` → all elements |
194224
195225Negative indices count from the end:
196226
@@ -221,9 +251,7 @@ The parser intelligently handles pipe characters (`|`) based on context:
221251
222252---
223253
224- ## Examples
225-
226- ### Basic
254+ ## More examples
227255
228256``` sh
229257# Get the last item
@@ -262,83 +290,72 @@ string-pipeline "{upper|append:!}" "hello"
262290string-pipeline " {prepend:\:foo}" " bar"
263291# Output: :foobar
264292
265- ```
266-
267- ### Advanced
268-
269- ``` sh
270- # Complex chaining: split, select range, join, replace, uppercase
271- string-pipeline " {split:,:0..2|join:-|replace:s/a/X/|upper}" " a,b,c"
272- # Output: X-B
273-
274- # Split, trim each item, then prepend
275- echo " a , b , c " | string-pipeline " {split:,:..|trim|prepend:item_}"
276- # Output: item_a,item_b,item_c
293+ # Map: uppercase each item
294+ string-pipeline " {split:,:..|map:{upper}}" " a,b,c"
295+ # Output: A,B,C
277296
278- # Strip custom characters
279- string-pipeline " {strip:xy}" " xyhelloxy"
280- # Output: hello
281- ```
282-
283- ### Real-World
284-
285- ``` sh
286- # Process CSV-like data
287- echo " name,age,city" | string-pipeline " {split:,:1..}"
288- # Output: age,city
289-
290- # Format file paths
291- echo " /home/user/documents/file.txt" | string-pipeline " {split:/:-1}"
292- # Output: file.txt
297+ # Sort, reverse, unique, pad, regex_extract
298+ string-pipeline " {split:,:..|sort:desc|join:-}" " b,a,c"
299+ # Output: c-b-a
293300
294- # Extract file extension
295- echo " document.pdf" | string-pipeline " {split:.:-1|upper}"
296- # Output: PDF
301+ string-pipeline " {split:,:..|reverse}" " a,b,c"
302+ # Output: c,b,a
297303
298- # Process log entries with timestamps
299- echo " 2023-01-01 ERROR Failed to connect" | string-pipeline " {split: :1..|join: |lower}"
300- # Output: error failed to connect
304+ string-pipeline " {split:,:..|unique}" " a,b,a,c"
305+ # Output: a,b,c
301306
302- # Clean colored git output
303- git log --oneline --color=always | string-pipeline " {split:\n:..|strip_ansi|join:\n} "
307+ string-pipeline " {split:,:..|map:{pad:3:*:both}} " " a,bb,c "
308+ # Output: *a*,bb*,*c*
304309
305- # Process ls colored output
306- ls --color=always | string-pipeline " {strip_ansi}"
307-
308- # Clean grep colored output
309- grep --color=always " pattern" file.txt | string-pipeline " {strip_ansi|upper}"
310-
311- # Chain with other operations
312- echo -e " \x1b[31mred\x1b[0m,\x1b[32mgreen\x1b[0m" | \
313- string-pipeline " {split:,:..|strip_ansi|upper|join: \| }"
314- # Output: RED | GREEN
315-
316- # Process log files with ANSI codes
317- cat colored.log | string-pipeline " {split:\n:-10..|strip_ansi|join:\n}"
310+ string-pipeline " {split:,:..|map:{regex_extract:\\ d+}}" " a1,b22,c333"
311+ # Output: 1,22,333
318312```
319313
320314### Debug Mode
321315
322316``` sh
323317# Print debug info for each operation
324- string-pipeline " {!split:,:..|upper|join:-} " " a,b,c "
325- # DEBUG: Initial value: Str("a,b,c ")
318+ string-pipeline " {!split:,:..|map:{trim|upper}} " " user123, admin456 ,guest789 "
319+ # DEBUG: Initial value: Str("user123, admin456 ,guest789 ")
326320# DEBUG: Applying operation 1: Split { sep: ",", range: Range(None, None, false) }
327321# DEBUG: Result: List with 3 items:
328- # DEBUG: [0]: "a "
329- # DEBUG: [1]: "b "
330- # DEBUG: [2]: "c "
322+ # DEBUG: [0]: "user123 "
323+ # DEBUG: [1]: " admin456 "
324+ # DEBUG: [2]: "guest789 "
331325# DEBUG: ---
332- # DEBUG: Applying operation 2: Upper
326+ # DEBUG: Applying operation 2: Map { operations: [Trim { direction: Both }, Upper] }
327+ # DEBUG: Map operation starting with 3 items
328+ # DEBUG: Map operations to apply: 2 steps
329+ # DEBUG: Step 1: Trim { direction: Both }
330+ # DEBUG: Step 2: Upper
331+ # DEBUG: Processing item 1 of 3: "user123"
332+ # DEBUG: Item 1/3 initial value: Str("user123")
333+ # DEBUG: Item 1/3 applying step 1: Trim { direction: Both }
334+ # DEBUG: Item 1/3 step 1 result: String("user123")
335+ # DEBUG: Item 1/3 applying step 2: Upper
336+ # DEBUG: Item 1/3 step 2 result: String("USER123")
337+ # DEBUG: Processing item 2 of 3: " admin456 "
338+ # DEBUG: Item 2/3 initial value: Str(" admin456 ")
339+ # DEBUG: Item 2/3 applying step 1: Trim { direction: Both }
340+ # DEBUG: Item 2/3 step 1 result: String("admin456")
341+ # DEBUG: Item 2/3 applying step 2: Upper
342+ # DEBUG: Item 2/3 step 2 result: String("ADMIN456")
343+ # DEBUG: Processing item 3 of 3: "guest789"
344+ # DEBUG: Item 3/3 initial value: Str("guest789")
345+ # DEBUG: Item 3/3 applying step 1: Trim { direction: Both }
346+ # DEBUG: Item 3/3 step 1 result: String("guest789")
347+ # DEBUG: Item 3/3 applying step 2: Upper
348+ # DEBUG: Item 3/3 step 2 result: String("GUEST789")
349+ # DEBUG: Map operation completed. Results:
350+ # DEBUG: Item 1: "USER123"
351+ # DEBUG: Item 2: "ADMIN456"
352+ # DEBUG: Item 3: "GUEST789"
333353# DEBUG: Result: List with 3 items:
334- # DEBUG: [0]: "A"
335- # DEBUG: [1]: "B"
336- # DEBUG: [2]: "C"
337- # DEBUG: ---
338- # DEBUG: Applying operation 3: Join { sep: "-" }
339- # DEBUG: Result: String("A-B-C")
354+ # DEBUG: [0]: "USER123"
355+ # DEBUG: [1]: "ADMIN456"
356+ # DEBUG: [2]: "GUEST789"
340357# DEBUG: ---
341- # A-B-C
358+ # USER123,ADMIN456,GUEST789
342359```
343360
344361---
0 commit comments