1use itertools::{Itertools, Position};
22use std::ffi::{OsStr, OsString};
23#[cfg(not(target_os = "windows"))]
24use std::os::unix::ffi::{OsStrExt, OsStringExt};
25
26
27pub struct StringSplicer<'a, T: Bytes> {
31 string: Vec<&'a OsStr>,
32 to: &'a [T],
33}
34
35impl<'a, T: Bytes> StringSplicer<'a, T> {
36 fn new(string: Vec<&'a OsStr>, to: &'a [T]) -> Self {
37 Self {
38 string,
39 to
40 }
41 }
42
43 pub fn assemble(self) -> OsString
46 {
47 self.assemble_with_sep_and_wrap("", "")
48 }
49
50 pub fn assemble_with_sep(self, sep: impl Bytes) -> OsString
53 {
54 self.assemble_with_sep_and_wrap(sep, "")
55 }
56
57 pub fn assemble_with_wrap(self, wrap: impl Bytes) -> OsString
60 {
61 self.assemble_with_sep_and_wrap("", wrap)
62 }
63
64 pub fn assemble_with_sep_and_wrap(self,
82 sep: impl Bytes,
83 wrap: impl Bytes) -> OsString
84 {
85 if self.string.len() == 0 {
87 return OsString::new();
88 }
89
90 if self.string.len() == 1 {
92 return self.string[0].to_os_string();
93 }
94
95 let sep = sep.as_byte_slice();
96 let wrap = wrap.as_byte_slice();
97 let part_count = self.string.len();
98 let to_count = self.to.len();
99
100 let splice_items = self.to.into_iter()
101 .enumerate()
102 .fold(OsString::new(), |mut splice_items, (i, item)| {
103 splice_items.push(wrap.bytes_as_os_str());
104 splice_items.push(item.bytes_as_os_str());
105 splice_items.push(wrap.bytes_as_os_str());
106
107 if i+1 < to_count {
109 splice_items.push(sep.bytes_as_os_str());
110 }
111 splice_items
112 });
113
114
115 self.string
116 .into_iter()
117 .take(part_count-1)
118 .fold(OsString::new(), |mut parts, part| {
119 parts.push(part);
120 parts.push(&splice_items);
121
122 parts
123 })
124 }
125}
126
127
128
129pub trait Bytes: std::fmt::Debug
133{
134 fn as_byte_slice(&self) -> &[u8];
148
149 fn bytes_as_os_str(&self) -> &OsStr {
163 let bytes = self.as_byte_slice();
164 OsStr::from_bytes(bytes)
165 }
166}
167
168
169impl Bytes for &[u8] {
170 fn as_byte_slice(&self) -> &[u8] {
171 self
172 }
173}
174
175impl Bytes for Vec<u8> {
176 fn as_byte_slice(&self) -> &[u8] {
177 self.as_slice()
178 }
179}
180
181impl Bytes for &Vec<u8> {
182 fn as_byte_slice(&self) -> &[u8] {
183 self.as_slice()
184 }
185}
186
187impl Bytes for OsStr {
188 fn as_byte_slice(&self) -> &[u8] {
189 self.as_bytes()
190 }
191}
192
193impl Bytes for &OsStr {
194 fn as_byte_slice(&self) -> &[u8] {
195 self.as_bytes()
196 }
197}
198
199impl Bytes for &&OsStr {
200 fn as_byte_slice(&self) -> &[u8] {
201 self.as_bytes()
202 }
203}
204
205impl Bytes for OsString {
206 fn as_byte_slice(&self) -> &[u8] {
207 self.as_os_str().as_byte_slice()
208 }
209}
210
211impl Bytes for &OsString {
212 fn as_byte_slice(&self) -> &[u8] {
213 self.as_os_str().as_byte_slice()
214 }
215}
216
217impl Bytes for str {
218 fn as_byte_slice(&self) -> &[u8] {
219 self.as_bytes()
220 }
221}
222
223impl Bytes for &str {
224 fn as_byte_slice(&self) -> &[u8] {
225 self.as_bytes()
226 }
227}
228
229
230
231pub trait OsStrConcat {
234 fn concat(self, sep: impl Bytes) -> OsString;
251}
252
253impl<I> OsStrConcat for I
254where
255 I: IntoIterator,
256 I::Item: Bytes + Clone,
257{
258 fn concat(self, sep: impl Bytes) -> OsString {
259 let sep = sep.as_byte_slice();
260
261 let mut string = self.into_iter()
262 .fold(Vec::new(), |mut string, part| {
263 string.extend_from_slice(part.as_byte_slice());
264 string.extend_from_slice(sep);
265
266 string
267 });
268
269 string.truncate(string.len() - sep.len());
271
272 OsString::from_vec(string.to_vec())
273 }
274}
275
276
277
278pub trait OsStringTools {
286 fn replace(self, pat: impl Bytes, to: impl Bytes) -> OsString;
291
292 fn escape_double_quote(self) -> OsString;
295
296 fn escape_single_quote(self) -> OsString;
299
300 fn trim_start(self, pat: impl Bytes) -> OsString;
305
306 fn trim_end(self, pat: impl Bytes) -> OsString;
311}
312
313impl OsStringTools for OsString {
314 #[inline]
315 fn replace(self, pat: impl Bytes, to: impl Bytes) -> OsString {
316 let pat = pat.as_byte_slice();
317 if self.contains(pat) {
318 self.as_os_str().replace(pat, to)
319 } else {
320 self
321 }
322 }
323
324 #[inline]
325 fn escape_double_quote(self) -> OsString {
326 if self.contains("\"") {
327 self.as_os_str().escape_double_quote()
328 } else {
329 self
330 }
331 }
332
333 #[inline]
334 fn escape_single_quote(self) -> OsString {
335 if self.contains("'") {
336 self.as_os_str().escape_single_quote()
337 } else {
338 self
339 }
340 }
341
342 #[inline]
343 fn trim_start(self, pat: impl Bytes) -> OsString {
344 let pat = pat.as_byte_slice();
345
346 let skip_n = *self.as_bytes()
347 .chunks(pat.len())
348 .enumerate()
349 .take_while(|(_, part)| part == &pat)
350 .map(|(i,_)| i)
351 .collect_vec()
352 .last()
353 .unwrap_or(&0);
354
355 if skip_n == 0 {
357 return self;
358 } else {
359 let mut string = self.into_vec();
360 string.rotate_left(skip_n+1);
362 string.truncate((string.len() - skip_n) - 1);
363
364 return OsString::from_vec(string);
365 }
366 }
367
368 #[inline]
369 fn trim_end(self, pat: impl Bytes) -> OsString {
370 let pat = pat.as_byte_slice();
371 let mut string = self.into_vec();
372
373 while string.ends_with(pat) {
374 string.truncate(string.len() - pat.len())
375 }
376
377 OsString::from_vec(string)
378 }
379}
380
381pub trait OsStrTools
385{
386 fn split(&self, pat: impl Bytes) -> Vec<&OsStr>;
396
397 fn split_lines(&self) -> Vec<&OsStr>;
401
402 fn replace(&self, pat: impl Bytes, to: impl Bytes) -> OsString;
407
408 fn trim_start(&self, pat: impl Bytes) -> &OsStr;
413
414 fn trim_end(&self, pat: impl Bytes) -> &OsStr;
419
420 fn contains(&self, pat: impl Bytes) -> bool;
422
423 fn position(&self, pat: impl Bytes) -> Option<usize>;
428
429 fn splice<'a, B: Bytes>(&'a self,
453 from: impl Bytes,
454 to: &'a [B]) -> StringSplicer<'a, B>;
455
456 fn quote_double(&self) -> OsString;
458
459 fn quote_single(&self) -> OsString;
461
462 fn escape_double_quote(&self) -> OsString;
465
466 fn escape_single_quote(&self) -> OsString;
469}
470
471
472
473impl OsStrTools for OsStr
474{
475 fn split(&self, pat: impl Bytes) -> Vec<&OsStr>
476 {
477 let orig_string = self.as_byte_slice();
478 let pat = pat.as_byte_slice();
479 let pat_len = pat.len();
480 let mut last_pos = 0;
481
482 let split_string = orig_string
483 .windows(pat_len)
484 .enumerate()
485 .scan(0, |prev_split_end, (i, chars)| {
486 if chars == pat {
487 let split_at = *prev_split_end;
488 *prev_split_end = i+pat_len;
489 Some(Some((split_at, i)))
490 } else {
491 Some(None)
492 }
493 })
494 .filter_map(|s| s)
495 .map(|(start, end)| {
496 let split = &orig_string[start..end];
497 (OsStr::from_bytes(split), end)
498 })
499 .with_position()
500 .batching(move |it| {
501 match it.next() {
502 Some(item) => {
503 let string = match item {
504 Position::First((string, _)) |
505 Position::Middle((string, _)) => string,
506 Position::Last((string, pos)) |
507 Position::Only((string, pos)) => {
508 last_pos = pos ;
509 string
510 }
511 };
512
513 return Some(string)
514 }
515 None => {
516 if last_pos == 0 {
518 last_pos = self.len();
519 if self.as_byte_slice() == pat.as_byte_slice() {
521 return Some(OsStr::new(""));
522 } else if self.len() == 0 {
523 return None;
524 } else {
525 return Some(self);
526 }
527 } else if last_pos+pat_len <= self.len() {
528 let last = last_pos;
530 let remaining = self.len() - last_pos;
531 last_pos = self.len();
532 let split = &orig_string[last+pat_len..last+remaining];
533 return Some(OsStr::from_bytes(split));
534 } else { return None; }
535 }
536 }
537 })
538 .collect();
539
540 split_string
541 }
542
543 fn replace(&self, pat: impl Bytes, with: impl Bytes) -> OsString
544 {
545 if self.len() == 0 {
546 return OsString::new()
547 }
548
549 let string_len = self.len();
550 let replace_len = with.as_byte_slice().len();
551 let replace_iter = std::iter::repeat(OsStr::from_bytes(with.as_byte_slice()));
552
553 let splits = self.split(pat);
554 let split_len = splits.len();
555
556 let splits = splits
557 .into_iter()
558 .interleave_shortest(replace_iter);
559
560 let mut new_string = OsString::with_capacity(string_len + replace_len);
561
562 for split in splits.take(split_len*2-1) {
563 new_string.push(split);
564 }
565
566 new_string
567 }
568
569 fn split_lines(&self) -> Vec<&OsStr> {
570 let newline = "\n";
571 self.split(newline)
572 }
573
574 fn quote_double(&self) -> OsString {
575 let string_len = self.len();
576 let quote = "\"";
577
578 let mut new_string = OsString::with_capacity(string_len + 2);
579 new_string.push(quote);
580 new_string.push(self);
581 new_string.push(quote);
582
583 new_string
584 }
585
586 fn quote_single(&self) -> OsString
587 {
588 let string = self;
589 let string_len = string.len();
590 let quote = OsStr::new("\'");
591
592 let mut new_string = OsString::with_capacity(string_len + 2);
593 new_string.push(quote);
594 new_string.push(string);
595 new_string.push(quote);
596
597 new_string
598 }
599
600 fn splice<'a, B: Bytes>(&'a self,
601 pat: impl Bytes,
602 to: &'a [B]) -> StringSplicer<'a, B> {
603 let string = self.split(pat);
604 StringSplicer::new(string, to)
605 }
606
607
608 fn trim_start(&self, pat: impl Bytes) -> &OsStr {
609 let mut string = self.as_bytes();
610 let pat = pat.as_byte_slice();
611 let pat_len = pat.len();
612
613 while string.starts_with(pat) {
614 string = &string[pat_len..];
615 }
616
617 OsStr::from_bytes(string)
618 }
619
620 fn trim_end(&self, pat: impl Bytes) -> &OsStr {
621 let mut string = self.as_bytes();
622 let pat = pat.as_byte_slice();
623 let pat_len = pat.len();
624
625 while string.ends_with(pat) {
626 string = &string[..string.len()-pat_len];
627 }
628
629 OsStr::from_bytes(string)
630 }
631
632 fn contains(&self, pat: impl Bytes) -> bool {
633 self.position(pat).is_some()
634 }
635
636 fn position(&self, pat: impl Bytes) -> Option<usize> {
637 let string = self.as_bytes();
638 let pat = pat.as_byte_slice();
639 let pat_len = pat.len();
640
641 string.windows(pat_len)
642 .position(|chars| chars == pat)
643 }
644
645 fn escape_double_quote(&self) -> OsString {
646 let quote = "\"";
647 let quote_escaped = "\\\"";
648
649 self.replace(quote, quote_escaped)
650 }
651
652 fn escape_single_quote(&self) -> OsString {
653 let single_quote = "'";
654 let single_quote_escaped = "'\\''";
655
656 self.replace(single_quote, single_quote_escaped)
657 }
658}
659
660
661
662#[cfg(target_os = "windows")]
671trait WinOsString {
672 fn from_vec(s: Vec<u8>) -> OsString;
673 fn into_vec(self) -> Vec<u8>;
674}
675
676#[cfg(target_os = "windows")]
677impl WinOsString for OsString {
678 fn from_vec(s: Vec<u8>) -> OsString {
679 use os_str_bytes::OsStringBytes;
680 OsStringBytes::from_vec(s.clone())
682 .expect("INVALID STRING: Tried to convert invalid WTF8 to OsString!")
683 }
684
685 fn into_vec(self) -> Vec<u8> {
686 use os_str_bytes::OsStringBytes;
687 OsStringBytes::into_vec(self)
688 }
689}
690
691#[cfg(target_os = "windows")]
692#[doc(cfg(windows))]
693#[doc(cfg(features = "windows"))]
694#[cfg(target_os = "windows")]
695#[cfg(features = "windows")]
696trait WinOsStr {
735 fn from_bytes(bytes: &[u8]) -> &OsStr;
736 fn as_bytes(&self) -> &[u8];
737}
738
739#[cfg(target_os = "windows")]
740impl WinOsStr for OsStr {
741 fn from_bytes(bytes: &[u8]) -> &OsStr {
742 use os_str_bytes::OsStrBytes;
743
744 let _str: std::borrow::Cow<'_, OsStr> = OsStrBytes::from_bytes(bytes)
746 .expect("INVALID STRING: Tried to convert invalid WTF8 to OsStr!");
747
748 unsafe { (bytes as *const _).cast() }
750 }
751
752 fn as_bytes<'s>(&'s self) -> &'s [u8] {
753 unsafe { (bytes as *const _).cast() }
755 }
756}
757
758
759
760#[test]
761fn testsplit() {
762 let s = OsStr::new("a,d,g");
763 assert_eq!(s.split(","), vec!["a", "d", "g"]);
764 let s = OsStr::new("abcd defe geh");
765 assert_eq!(s.split(" "), vec!["abcd", "defe", "geh"]);
766 let s = OsStr::new("abcd $s");
767 assert_eq!(s.split("$s"), vec!["abcd ", ""]);
768 let s = OsStr::new("abcd $s$s");
769 assert_eq!(s.split("$s"), vec!["abcd ", "", ""]);
770 let s = OsStr::new("");
771 assert_eq!(s.split("$s"), Vec::<&OsStr>::new());
772 let s = OsStr::new("$s");
773 assert_eq!(s.split(",,"), &["$s"]);
774
775}
776
777#[test]
778fn testreplace() {
779 let s = OsStr::new("a,d,g");
780 assert_eq!(s.replace(",", "WUT"), "aWUTdWUTg");
781 let s = OsStr::new("a,d,g");
782 assert_eq!(s.replace(".", "WUT"), "a,d,g");
783 let s = OsStr::new("a,,d,,g");
784 assert_eq!(s.replace(",,", "WUT"), "aWUTdWUTg");
785 let s = OsStr::new("");
786 assert_eq!(s.replace(",,", "WUT"), "");
787 let s = OsStr::new("$s");
788 assert_eq!(s.replace(",,", "WUT"), "$s");
789 let s = OsString::from("a,d,g");
790 assert_eq!(s.replace(",", "WUT"), "aWUTdWUTg");
791 let s = OsString::from("a,d,g");
792 assert_eq!(s.replace(".", "WUT"), "a,d,g");
793 let s = OsString::from("a,,d,,g");
794 assert_eq!(s.replace(",,", "WUT"), "aWUTdWUTg");
795 let s = OsString::from("");
796 assert_eq!(s.replace(",,", "WUT"), "");
797 let s = OsString::from("$s");
798 assert_eq!(s.replace(",,", "WUT"), "$s");
799}
800
801#[test]
802fn testtrimstart() {
803 let s = OsStr::new(",,,abcd,defe,geh,,,,");
804 assert_eq!(s.trim_start(","), "abcd,defe,geh,,,,");
805 let s = OsString::from(",,,abcd,defe,geh");
806 assert_eq!(s.trim_start(","), "abcd,defe,geh");
807}
808
809#[test]
810fn testtrimend() {
811 let s = OsStr::new(",,,abcd,defe,geh,,,,");
812 assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
813 let s = OsStr::new(",,,abcd,defe,geh,,,,");
814 assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
815 let s = OsStr::new(",,,abcd");
816 assert_eq!(s.trim_end(","), ",,,abcd");
817 let s = OsStr::new(",,,abcd,,");
818 assert_eq!(s.trim_end(","), ",,,abcd");
819 let s = OsStr::new(",,,");
820 assert_eq!(s.trim_end(","), "");
821 let s = OsStr::new(",,,");
822 assert_eq!(s.trim_end(",,"), ",");
823 let s = OsStr::new(",,,");
824 assert_eq!(s.trim_end(",,,"), "");
825 let s = OsStr::new("");
826 assert_eq!(s.trim_end(",,,"), "");
827
828 let s = OsString::from(",,,abcd,defe,geh,,,,");
829 assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
830 let s = OsString::from(",,,abcd,defe,geh,,,,");
831 assert_eq!(s.trim_end(","), ",,,abcd,defe,geh");
832 let s = OsString::from(",,,abcd");
833 assert_eq!(s.trim_end(","), ",,,abcd");
834 let s = OsString::from(",,,abcd,,");
835 assert_eq!(s.trim_end(","), ",,,abcd");
836 let s = OsString::from(",,,");
837 assert_eq!(s.trim_end(","), "");
838 let s = OsString::from(",,,");
839 assert_eq!(s.trim_end(",,"), ",");
840 let s = OsString::from(",,,");
841 assert_eq!(s.trim_end(",,,"), "");
842 let s = OsString::from("");
843 assert_eq!(s.trim_end(",,,"), "");
844}
845
846
847#[test]
848fn testescape() {
849 let s = OsStr::new("te'st");
850 assert_eq!(s.escape_single_quote(), "te'\\''st");
851 let s = OsStr::new("'te'st'");
852 assert_eq!(s.escape_single_quote(), "'\\''te'\\''st'\\''");
853 let s = OsString::from("te'st");
854 assert_eq!(s.escape_single_quote(), "te'\\''st");
855 let s = OsString::from("'te'st'");
856 assert_eq!(s.escape_single_quote(), "'\\''te'\\''st'\\''");
857}
858
859#[test]
860fn test_escape_double() {
861 let s = OsStr::new("te\"st");
862 assert_eq!(s.escape_double_quote(), "te\\\"st");
863 let s = OsStr::new("\"te\"st\"");
864 assert_eq!(s.escape_double_quote(), "\\\"te\\\"st\\\"");
865 let s = OsString::from("te\"st");
866 assert_eq!(s.escape_double_quote(), "te\\\"st");
867 let s = OsString::from("\"te\"st\"");
868 assert_eq!(s.escape_double_quote(), "\\\"te\\\"st\\\"");
869}
870
871#[test]
872fn testsplice() {
873 let s = OsStr::new("ls $s");
874 assert_eq!(s.splice("$s", &["TEST"]).assemble_with_sep(" "),
875 "ls TEST");
876 let s = OsStr::new("ls $s");
877 assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "),
878 "ls TEST TEST");
879 let s = OsStr::new("ls $s$s");
880 assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "),
881 "ls TEST TESTTEST TEST");
882 let s = OsStr::new("");
883 assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep(" "), "");
884 let s = OsStr::new("ls $s");
885 assert_eq!(s.splice("$s", &[""]).assemble_with_wrap("'"), "ls ''");
886 let s = OsStr::new("ls $s");
887 assert_eq!(s.splice("$s", &["TEST"]).assemble_with_wrap("'"),
888 "ls 'TEST'");
889 let s = OsStr::new("ls $s");
890 assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep_and_wrap(" ", "'"),
891 "ls 'TEST' 'TEST'");
892 let s = OsStr::new("ls $s$s");
893 assert_eq!(s.splice("$s", &["TEST", "TEST"]).assemble_with_sep_and_wrap(" ", "'"),
894 "ls 'TEST' 'TEST''TEST' 'TEST'");
895}
896
897#[test]
898fn testconcat() {
899 let s = &[OsStr::new("1"), OsStr::new("2"), OsStr::new("3")];
900 assert_eq!(s.concat(" "), "1 2 3");
901 assert_eq!(s.concat(""), "123");
902 assert_eq!(s.concat("' '"), "1' '2' '3");
903 let s = &[OsStr::new("")];
904 assert_eq!(s.concat(""), "");
905 assert_eq!(s.concat("' '"), "");
906 let s: &[&OsStr] = &[];
907 assert_eq!(s.concat(""), "");
908}