Skip to main content

morphix/impls/
string.rs

1use std::collections::TryReserveError;
2use std::fmt::{Debug, Display};
3use std::marker::PhantomData;
4use std::ops::{AddAssign, Bound, Deref, DerefMut, Range, RangeBounds};
5use std::string::Drain;
6
7use crate::helper::macros::{default_impl_ref_observe, untracked_methods};
8use crate::helper::{AsDerefMut, AsNormalized, Pointer, Succ, Unsigned, Zero};
9use crate::observe::{DefaultSpec, Observer, SerializeObserver};
10use crate::{Adapter, MutationKind, Mutations, Observe};
11
12/// Observer implementation for [`String`].
13pub struct StringObserver<'ob, S: ?Sized, D = Zero> {
14    ptr: Pointer<S>,
15    mutation: Option<TruncateAppend>,
16    phantom: PhantomData<&'ob mut D>,
17}
18
19struct TruncateAppend {
20    pub append_index: usize, // byte index
21    pub truncate_len: usize, // char count
22}
23
24impl<'ob, S: ?Sized, D> StringObserver<'ob, S, D> {
25    #[inline]
26    fn __mark_replace(&mut self) {
27        self.mutation = None;
28    }
29}
30
31impl<'ob, S: ?Sized, D> Deref for StringObserver<'ob, S, D> {
32    type Target = Pointer<S>;
33
34    #[inline]
35    fn deref(&self) -> &Self::Target {
36        &self.ptr
37    }
38}
39
40impl<'ob, S: ?Sized, D> DerefMut for StringObserver<'ob, S, D> {
41    #[inline]
42    fn deref_mut(&mut self) -> &mut Self::Target {
43        self.__mark_replace();
44        &mut self.ptr
45    }
46}
47
48impl<'ob, S: ?Sized, D> AsNormalized for StringObserver<'ob, S, D> {
49    type OuterDepth = Succ<Zero>;
50}
51
52impl<'ob, S: ?Sized, D> Observer<'ob> for StringObserver<'ob, S, D>
53where
54    D: Unsigned,
55    S: AsDerefMut<D, Target = String> + 'ob,
56{
57    type InnerDepth = D;
58    type Head = S;
59
60    #[inline]
61    fn uninit() -> Self {
62        Self {
63            ptr: Pointer::uninit(),
64            mutation: None,
65            phantom: PhantomData,
66        }
67    }
68
69    #[inline]
70    fn observe(value: &mut Self::Head) -> Self {
71        Self {
72            ptr: Pointer::new(value),
73            mutation: Some(TruncateAppend {
74                append_index: value.as_deref().len(),
75                truncate_len: 0,
76            }),
77            phantom: PhantomData,
78        }
79    }
80
81    #[inline]
82    unsafe fn refresh(this: &mut Self, value: &mut Self::Head) {
83        Pointer::set(this, value);
84    }
85}
86
87impl<'ob, S: ?Sized, D> SerializeObserver<'ob> for StringObserver<'ob, S, D>
88where
89    D: Unsigned,
90    S: AsDerefMut<D, Target = String> + 'ob,
91{
92    unsafe fn flush_unchecked<A: Adapter>(this: &mut Self) -> Result<Mutations<A::Value>, A::Error> {
93        let len = this.as_deref().len();
94        let Some(truncate_append) = this.mutation.replace(TruncateAppend {
95            append_index: len,
96            truncate_len: 0,
97        }) else {
98            return Ok(MutationKind::Replace(A::serialize_value(this.as_deref())?).into());
99        };
100        let TruncateAppend {
101            append_index,
102            truncate_len,
103        } = truncate_append;
104        let mut mutations = Mutations::new();
105        #[cfg(feature = "truncate")]
106        if truncate_len > 0 {
107            mutations.extend(MutationKind::Truncate(truncate_len));
108        }
109        #[cfg(feature = "append")]
110        if len > append_index {
111            mutations.extend(MutationKind::Append(A::serialize_value(
112                &this.as_deref()[append_index..],
113            )?));
114        }
115        Ok(mutations)
116    }
117}
118
119impl<'ob, S: ?Sized, D> StringObserver<'ob, S, D>
120where
121    D: Unsigned,
122    S: AsDerefMut<D, Target = String>,
123{
124    untracked_methods! { String =>
125        pub fn reserve(&mut self, additional: usize);
126        pub fn reserve_exact(&mut self, additional: usize);
127        pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError>;
128        pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError>;
129        pub fn shrink_to_fit(&mut self);
130        pub fn shrink_to(&mut self, min_capacity: usize);
131    }
132}
133
134#[cfg(feature = "append")]
135impl<'ob, S: ?Sized, D> StringObserver<'ob, S, D>
136where
137    D: Unsigned,
138    S: AsDerefMut<D, Target = String> + 'ob,
139{
140    #[inline]
141    fn __append_index(&mut self) -> usize {
142        match &mut self.mutation {
143            Some(m) => m.append_index,
144            None => 0,
145        }
146    }
147
148    untracked_methods! { String =>
149        pub fn push(&mut self, c: char);
150        pub fn push_str(&mut self, s: &str);
151        pub fn extend_from_within<R>(&mut self, src: R)
152        where { R: RangeBounds<usize> };
153    }
154
155    /// See [`String::insert`].
156    #[inline]
157    pub fn insert(&mut self, idx: usize, ch: char) {
158        if idx >= self.__append_index() {
159            Observer::as_inner(self).insert(idx, ch)
160        } else {
161            Observer::track_inner(self).insert(idx, ch)
162        }
163    }
164
165    /// See [`String::insert_str`].
166    #[inline]
167    pub fn insert_str(&mut self, idx: usize, string: &str) {
168        if idx >= self.__append_index() {
169            Observer::as_inner(self).insert_str(idx, string)
170        } else {
171            Observer::track_inner(self).insert_str(idx, string)
172        }
173    }
174}
175
176#[cfg(any(feature = "truncate", feature = "append"))]
177impl<'ob, S: ?Sized, D> StringObserver<'ob, S, D>
178where
179    D: Unsigned,
180    S: AsDerefMut<D, Target = String> + 'ob,
181{
182    #[inline]
183    fn __mark_truncate(&mut self, range: Range<usize>) {
184        let count = self.as_deref()[range.clone()].chars().count();
185        let mutation = self.mutation.as_mut().unwrap();
186        mutation.truncate_len += count;
187        mutation.append_index = range.start;
188    }
189
190    /// See [`String::clear`].
191    #[inline]
192    pub fn clear(&mut self) {
193        if self.__append_index() == 0 {
194            Observer::as_inner(self).clear()
195        } else {
196            Observer::track_inner(self).clear()
197        }
198    }
199
200    /// See [`String::remove`].
201    pub fn remove(&mut self, idx: usize) -> char {
202        let char = Observer::as_inner(self).remove(idx);
203        let append_index = self.__append_index();
204        if idx >= append_index {
205            // no-op
206        } else if cfg!(feature = "truncate") && idx + char.len_utf8() == append_index {
207            let mutation = self.mutation.as_mut().unwrap();
208            mutation.truncate_len += 1;
209            mutation.append_index = idx;
210        } else {
211            self.__mark_replace();
212        }
213        char
214    }
215
216    /// See [`String::pop`].
217    pub fn pop(&mut self) -> Option<char> {
218        let char = Observer::as_inner(self).pop()?;
219        let append_index = self.__append_index();
220        let len = self.as_deref().len();
221        if len >= append_index {
222            // no-op
223        } else if cfg!(feature = "truncate") && len + char.len_utf8() == append_index {
224            let mutation = self.mutation.as_mut().unwrap();
225            mutation.truncate_len += 1;
226            mutation.append_index = len;
227        } else {
228            self.__mark_replace();
229        }
230        Some(char)
231    }
232
233    /// See [`String::truncate`].
234    pub fn truncate(&mut self, len: usize) {
235        let append_index = self.__append_index();
236        if len >= append_index {
237            return Observer::as_inner(self).truncate(len);
238        }
239        if cfg!(not(feature = "truncate")) || len == 0 {
240            return Observer::track_inner(self).truncate(len);
241        }
242        self.__mark_truncate(len..append_index);
243        Observer::as_inner(self).truncate(len)
244    }
245
246    /// See [`String::split_off`].
247    pub fn split_off(&mut self, at: usize) -> String {
248        let append_index = self.__append_index();
249        if at >= append_index {
250            return Observer::as_inner(self).split_off(at);
251        }
252        if cfg!(not(feature = "truncate")) || at == 0 {
253            return Observer::track_inner(self).split_off(at);
254        }
255        self.__mark_truncate(at..append_index);
256        Observer::as_inner(self).split_off(at)
257    }
258
259    /// See [`String::drain`].
260    pub fn drain<R>(&mut self, range: R) -> Drain<'_>
261    where
262        R: RangeBounds<usize>,
263    {
264        let append_index = self.__append_index();
265        let start_index = match range.start_bound() {
266            Bound::Included(&n) => n,
267            Bound::Excluded(&n) => n + 1,
268            Bound::Unbounded => 0,
269        };
270        if start_index >= append_index {
271            return Observer::as_inner(self).drain(range);
272        }
273        if cfg!(not(feature = "truncate")) || start_index == 0 {
274            return Observer::track_inner(self).drain(range);
275        }
276        let end_index = match range.end_bound() {
277            Bound::Included(&n) => n + 1,
278            Bound::Excluded(&n) => n,
279            Bound::Unbounded => self.as_deref().len(),
280        };
281        if end_index < append_index {
282            return Observer::track_inner(self).drain(range);
283        }
284        self.__mark_truncate(start_index..append_index);
285        Observer::track_inner(self).drain(range)
286    }
287
288    /// See [`String::replace_range`].
289    pub fn replace_range<R>(&mut self, range: R, replace_with: &str)
290    where
291        R: RangeBounds<usize>,
292    {
293        let append_index = self.__append_index();
294        let start_index = match range.start_bound() {
295            Bound::Included(&n) => n,
296            Bound::Excluded(&n) => n + 1,
297            Bound::Unbounded => 0,
298        };
299        if start_index >= append_index {
300            return Observer::as_inner(self).replace_range(range, replace_with);
301        }
302        if cfg!(not(feature = "truncate")) || start_index == 0 {
303            return Observer::track_inner(self).replace_range(range, replace_with);
304        }
305        let end_index = match range.end_bound() {
306            Bound::Included(&n) => n + 1,
307            Bound::Excluded(&n) => n,
308            Bound::Unbounded => self.as_deref().len(),
309        };
310        if end_index < append_index {
311            return Observer::track_inner(self).replace_range(range, replace_with);
312        }
313        self.__mark_truncate(start_index..append_index);
314        Observer::as_inner(self).replace_range(range, replace_with);
315    }
316}
317
318impl<'ob, S: ?Sized, D> AddAssign<&str> for StringObserver<'ob, S, D>
319where
320    D: Unsigned,
321    S: AsDerefMut<D, Target = String> + 'ob,
322{
323    #[inline]
324    fn add_assign(&mut self, rhs: &str) {
325        #[cfg(feature = "append")]
326        self.push_str(rhs);
327        #[cfg(not(feature = "append"))]
328        Observer::track_inner(self).add_assign(rhs);
329    }
330}
331
332#[cfg(feature = "append")]
333impl<'ob, S: ?Sized, D, U> Extend<U> for StringObserver<'ob, S, D>
334where
335    D: Unsigned,
336    S: AsDerefMut<D, Target = String>,
337    String: Extend<U>,
338{
339    #[inline]
340    fn extend<I: IntoIterator<Item = U>>(&mut self, other: I) {
341        Observer::as_inner(self).extend(other);
342    }
343}
344
345impl<'ob, S: ?Sized, D> Debug for StringObserver<'ob, S, D>
346where
347    D: Unsigned,
348    S: AsDerefMut<D, Target = String>,
349{
350    #[inline]
351    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352        f.debug_tuple("StringObserver").field(self.as_deref()).finish()
353    }
354}
355
356impl<'ob, S: ?Sized, D> Display for StringObserver<'ob, S, D>
357where
358    D: Unsigned,
359    S: AsDerefMut<D, Target = String>,
360{
361    #[inline]
362    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363        Display::fmt(self.as_deref(), f)
364    }
365}
366
367impl<'ob, S, D, U: ?Sized> PartialEq<U> for StringObserver<'ob, S, D>
368where
369    D: Unsigned,
370    S: AsDerefMut<D, Target = String>,
371    String: PartialEq<U>,
372{
373    #[inline]
374    fn eq(&self, other: &U) -> bool {
375        self.as_deref().eq(other)
376    }
377}
378
379impl<'ob, S, D, U: ?Sized> PartialOrd<U> for StringObserver<'ob, S, D>
380where
381    D: Unsigned,
382    S: AsDerefMut<D, Target = String>,
383    String: PartialOrd<U>,
384{
385    #[inline]
386    fn partial_cmp(&self, other: &U) -> Option<std::cmp::Ordering> {
387        self.as_deref().partial_cmp(other)
388    }
389}
390
391impl Observe for String {
392    type Observer<'ob, S, D>
393        = StringObserver<'ob, S, D>
394    where
395        Self: 'ob,
396        D: Unsigned,
397        S: AsDerefMut<D, Target = Self> + ?Sized + 'ob;
398
399    type Spec = DefaultSpec;
400}
401
402default_impl_ref_observe! {
403    impl RefObserve for String;
404}
405
406#[cfg(test)]
407mod tests {
408    use serde_json::json;
409
410    use super::*;
411    use crate::Mutation;
412    use crate::adapter::Json;
413    use crate::observe::{ObserveExt, SerializeObserverExt};
414
415    #[test]
416    fn no_mutation_returns_none() {
417        let mut s = String::from("hello");
418        let mut ob = s.__observe();
419        let Json(mutation) = ob.flush().unwrap();
420        assert!(mutation.is_none());
421    }
422
423    #[test]
424    fn replace_on_deref_mut() {
425        let mut s = String::from("hello");
426        let mut ob = s.__observe();
427        ob.clear();
428        ob.push_str("world"); // append after replace should have no effect
429        let Json(mutation) = ob.flush().unwrap();
430        assert_eq!(
431            mutation,
432            Some(Mutation {
433                path: vec![].into(),
434                kind: MutationKind::Replace(json!("world"))
435            })
436        );
437    }
438
439    #[test]
440    fn append_with_push() {
441        let mut s = String::from("a");
442        let mut ob = s.__observe();
443        ob.push('b');
444        ob.push('c');
445        let Json(mutation) = ob.flush().unwrap();
446        assert_eq!(
447            mutation,
448            Some(Mutation {
449                path: vec![].into(),
450                kind: MutationKind::Append(json!("bc"))
451            })
452        );
453    }
454
455    #[test]
456    fn append_with_push_str() {
457        let mut s = String::from("foo");
458        let mut ob = s.__observe();
459        ob.push_str("bar");
460        let Json(mutation) = ob.flush().unwrap();
461        assert_eq!(
462            mutation,
463            Some(Mutation {
464                path: vec![].into(),
465                kind: MutationKind::Append(json!("bar"))
466            })
467        );
468    }
469
470    #[test]
471    fn append_with_add_assign() {
472        let mut s = String::from("foo");
473        let mut ob = s.__observe();
474        ob += "bar";
475        let Json(mutation) = ob.flush().unwrap();
476        assert_eq!(mutation.unwrap().kind, MutationKind::Append(json!("bar")));
477    }
478
479    #[test]
480    fn append_empty_string() {
481        let mut s = String::from("foo");
482        let mut ob = s.__observe();
483        ob.push_str("");
484        ob += "";
485        let Json(mutation) = ob.flush().unwrap();
486        assert!(mutation.is_none());
487    }
488
489    #[test]
490    fn replace_after_append() {
491        let mut s = String::from("abc");
492        let mut ob = s.__observe();
493        ob.push_str("def");
494        **ob = String::from("xyz");
495        let Json(mutation) = ob.flush().unwrap();
496        assert_eq!(mutation.unwrap().kind, MutationKind::Replace(json!("xyz")));
497    }
498
499    #[test]
500    fn truncate() {
501        let mut s = String::from("你好,世界!");
502        let mut ob = s.__observe();
503        ob.truncate("你好".len());
504        let Json(mutation) = ob.flush().unwrap();
505        assert_eq!(mutation.unwrap().kind, MutationKind::Truncate(4));
506    }
507
508    #[test]
509    fn pop_as_truncate() {
510        let mut s = String::from("你好,世界!");
511        let mut ob = s.__observe();
512        ob.pop();
513        ob.pop();
514        let Json(mutation) = ob.flush().unwrap();
515        assert_eq!(mutation.unwrap().kind, MutationKind::Truncate(2));
516    }
517
518    #[test]
519    fn pop_after_append() {
520        let mut s = String::from("你好!");
521        let mut ob = s.__observe();
522        ob.push_str("世界!");
523        ob.pop();
524        let Json(mutation) = ob.flush().unwrap();
525        assert_eq!(mutation.unwrap().kind, MutationKind::Append(json!("世界")));
526    }
527
528    #[test]
529    fn append_after_pop() {
530        let mut s = String::from("你好,世界!");
531        let mut ob = s.__observe();
532        ob.pop();
533        ob.push('~');
534        let Json(mutation) = ob.flush().unwrap();
535        assert_eq!(
536            mutation.unwrap().kind,
537            MutationKind::Batch(vec![
538                Mutation {
539                    path: Default::default(),
540                    kind: MutationKind::Truncate(1),
541                },
542                Mutation {
543                    path: Default::default(),
544                    kind: MutationKind::Append(json!("~")),
545                },
546            ])
547        );
548    }
549
550    #[test]
551    fn remove_before_append_index() {
552        let mut s = String::from("你好,世界!");
553        let mut ob = s.__observe();
554        assert_eq!(ob.remove("你好".len()), ',');
555        let Json(mutation) = ob.flush().unwrap();
556        assert_eq!(mutation.unwrap().kind, MutationKind::Replace(json!("你好世界!")));
557    }
558
559    #[test]
560    fn remove_at_append_index() {
561        let mut s = String::from("你好,世界!");
562        let mut ob = s.__observe();
563        assert_eq!(ob.remove("你好,世界".len()), '!');
564        assert_eq!(ob.remove("你好,世".len()), '界');
565        let Json(mutation) = ob.flush().unwrap();
566        assert_eq!(mutation.unwrap().kind, MutationKind::Truncate(2));
567    }
568}