1#![no_std]
2
3extern crate alloc;
4
5use alloc::{
6 borrow::{Cow, ToOwned},
7 string::String,
8};
9
10mod private {
11 pub trait Sealed {}
12
13 pub trait WithInfo {
14 type Info: Default;
15 }
16}
17use private::{Sealed, WithInfo};
18
19pub trait Rewritable: AsRef<Self::Ref> + Sealed {
20 type Ref: ToOwned + WithInfo + ?Sized;
21}
22
23pub struct Rewrite<T: Rewritable> {
24 input: T,
25 output: <T::Ref as ToOwned>::Owned,
26 copied: bool,
27 info: <T::Ref as WithInfo>::Info,
28}
29
30fn is_bytes_at(a: &[u8], i: usize, b: &[u8]) -> bool {
32 let l = b.len();
33 unsafe { a.len() >= i + l && a.get_unchecked(i..(i + l)) == b }
34}
35
36fn is_char_at(s: &str, i: usize, c: char) -> bool {
38 let mut d = [0; size_of::<char>()];
39 c.encode_utf8(&mut d);
40 let l = c.len_utf8();
41 let d = unsafe { &d.get_unchecked(0..l) };
42 is_bytes_at(s.as_bytes(), i, d)
43}
44
45impl WithInfo for str {
46 type Info = usize;
47}
48
49impl<'a> Sealed for &'a str {}
50impl<'a> Rewritable for &'a str {
51 type Ref = str;
52}
53
54impl Sealed for String {}
55impl Rewritable for String {
56 type Ref = str;
57}
58
59impl<'a> Sealed for Cow<'a, str> {}
60impl<'a> Rewritable for Cow<'a, str> {
61 type Ref = str;
62}
63
64impl<'a, 'b> Sealed for &'b Cow<'a, str> {}
65impl<'a, 'b> Rewritable for &'b Cow<'a, str> {
66 type Ref = str;
67}
68
69impl<T: Rewritable> Rewrite<T>
70where
71 <T::Ref as ToOwned>::Owned: Default,
72{
73 pub fn new(input: T) -> Self {
74 Self {
75 input,
76 output: Default::default(),
77 copied: false,
78 info: Default::default(),
79 }
80 }
81}
82
83impl<T: Rewritable<Ref = str>> Rewrite<T> {
84 fn copy(&mut self) {
85 if !self.copied {
86 self.output.push_str(&self.input.as_ref()[0..self.info]);
87 self.copied = true;
88 }
89 }
90 pub fn push(&mut self, c: char) {
91 if !self.copied && is_char_at(self.input.as_ref(), self.info, c) {
92 self.info += c.len_utf8();
93 } else {
94 self.copy();
95 self.output.push(c);
96 }
97 }
98 pub fn push_str(&mut self, s: &str) {
99 if !self.copied && is_bytes_at(self.input.as_ref().as_bytes(), self.info, s.as_bytes()) {
100 self.info += s.len();
101 } else {
102 self.copy();
103 self.output.push_str(s);
104 }
105 }
106}
107
108impl<'a> From<Rewrite<&'a str>> for Cow<'a, str> {
109 fn from(this: Rewrite<&'a str>) -> Self {
110 if this.copied {
111 Cow::Owned(this.output)
112 } else {
113 Cow::Borrowed(&this.input[0..this.info])
114 }
115 }
116}
117
118impl From<Rewrite<String>> for String {
119 fn from(this: Rewrite<String>) -> Self {
120 if this.copied {
121 this.output
122 } else {
123 let mut s = this.input;
124 s.truncate(this.info);
125 s
126 }
127 }
128}
129
130impl<'a> From<Rewrite<Cow<'a, str>>> for Cow<'a, str> {
131 fn from(this: Rewrite<Cow<'a, str>>) -> Self {
132 if this.copied {
133 Cow::Owned(this.output)
134 } else {
135 match this.input {
136 Cow::Borrowed(s) => Cow::Borrowed(&s[0..this.info]),
137 Cow::Owned(mut s) => {
138 s.truncate(this.info);
139 Cow::Owned(s)
140 }
141 }
142 }
143 }
144}
145
146impl<'a, 'b> From<Rewrite<&'b Cow<'a, str>>> for Cow<'a, str> {
147 fn from(this: Rewrite<&'b Cow<'a, str>>) -> Self {
148 if this.copied {
149 Cow::Owned(this.output)
150 } else {
151 match this.input {
152 Cow::Borrowed(s) => Cow::Borrowed(&s[0..this.info]),
153 Cow::Owned(s) => {
154 let mut d = String::with_capacity(this.info);
155 d.push_str(&s[0..this.info]);
156 Cow::Owned(d)
157 }
158 }
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn str_write_same() {
169 let s = "abc";
170 let mut r = Rewrite::new(s);
171 r.push('a');
172 r.push_str("bc");
173 let d: Cow<str> = r.into();
174 assert_eq!(d, Cow::Borrowed(s))
175 }
176
177 #[test]
178 fn str_write_less() {
179 let s = "abc";
180 let mut r = Rewrite::new(s);
181 r.push('a');
182 r.push_str("b");
183 let d: Cow<str> = r.into();
184 assert_eq!(d, Cow::Borrowed(&s[..2]))
185 }
186
187 #[test]
188 fn str_write_more() {
189 let s = "abc";
190 let mut r = Rewrite::new(s);
191 r.push('a');
192 r.push_str("bcd");
193 let d: Cow<str> = r.into();
194 assert_eq!(d, Cow::<str>::Owned("abcd".to_owned()))
195 }
196
197 #[test]
198 fn str_write_different() {
199 let s = "abc";
200 let mut r = Rewrite::new(s);
201 r.push('a');
202 r.push_str("bd");
203 let d: Cow<str> = r.into();
204 assert_eq!(d, Cow::<str>::Owned("abd".to_owned()))
205 }
206}