bms_rs/bms/command/
mixin.rs

1//! Mixin types for structures.
2//!
3//! - `SourcePosMixin` is a generic wrapper that attaches position information (index span) to a value.
4//! - `SourcePosMixinExt` is a trait that provides extension methods for `SourcePosMixin`, providing more convenient methods to create `SourcePosMixin` instances.
5
6use std::ops::Range;
7
8/// A generic wrapper that attaches position information (index span) to a value.
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct SourceRangeMixin<T> {
12    /// Wrapped content value
13    content: T,
14    /// Range of indices in the source string (0-based, inclusive start, exclusive end)
15    range: Range<usize>,
16}
17
18impl<T> SourceRangeMixin<T> {
19    /// Creates a new `SourceRangeMixin` with a range
20    pub const fn new(content: T, range: Range<usize>) -> Self {
21        Self { content, range }
22    }
23
24    /// Creates a new `SourceRangeMixin` with start and end indices
25    pub const fn new_with_start_end(content: T, start: usize, end: usize) -> Self {
26        Self::new(content, start..end)
27    }
28
29    /// Returns the wrapped content.
30    pub const fn content(&self) -> &T {
31        &self.content
32    }
33
34    /// Returns the wrapped content as a mutable reference.
35    pub const fn content_mut(&mut self) -> &mut T {
36        &mut self.content
37    }
38
39    /// Leans the content out of the wrapper.
40    pub fn into_content(self) -> T {
41        self.content
42    }
43
44    /// Returns the start index of the source span.
45    pub const fn start(&self) -> usize {
46        self.range.start
47    }
48
49    /// Returns the end index of the source span.
50    pub const fn end(&self) -> usize {
51        self.range.end
52    }
53
54    /// Returns the source range.
55    pub const fn range(&self) -> &Range<usize> {
56        &self.range
57    }
58
59    /// Returns the source span as a tuple of (start, end).
60    pub const fn as_span(&self) -> (usize, usize) {
61        (self.range.start, self.range.end)
62    }
63
64    /// Returns the length of the source span.
65    pub const fn len(&self) -> usize {
66        self.range.end.saturating_sub(self.range.start)
67    }
68
69    /// Returns true if the source span's length is 0.
70    pub const fn is_empty(&self) -> bool {
71        self.len() == 0
72    }
73}
74
75impl<'a, T> SourceRangeMixin<T> {
76    /// Returns the inner reference version of the wrapper.
77    pub fn inner_ref(&'a self) -> SourceRangeMixin<&'a T> {
78        let content = &self.content;
79        SourceRangeMixin::new(content, self.range.clone())
80    }
81}
82
83impl<T> SourceRangeMixin<T> {
84    /// Maps the content of the wrapper.
85    pub fn map<U, F>(self, f: F) -> SourceRangeMixin<U>
86    where
87        F: FnOnce(T) -> U,
88    {
89        SourceRangeMixin::new(f(self.content), self.range)
90    }
91}
92
93impl<T: std::fmt::Display> std::fmt::Display for SourceRangeMixin<T> {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        write!(
96            f,
97            "{} at indices [{}, {})",
98            self.content, self.range.start, self.range.end
99        )
100    }
101}
102
103impl<T> From<(T, Range<usize>)> for SourceRangeMixin<T> {
104    fn from(value: (T, Range<usize>)) -> Self {
105        Self::new(value.0, value.1)
106    }
107}
108
109impl<T> From<(T, usize, usize)> for SourceRangeMixin<T> {
110    fn from(value: (T, usize, usize)) -> Self {
111        Self::new_with_start_end(value.0, value.1, value.2)
112    }
113}
114
115impl<T> From<SourceRangeMixin<T>> for (T, Range<usize>) {
116    fn from(value: SourceRangeMixin<T>) -> Self {
117        (value.content, value.range)
118    }
119}
120
121impl<T> From<SourceRangeMixin<T>> for (T, usize, usize) {
122    fn from(value: SourceRangeMixin<T>) -> Self {
123        (value.content, value.range.start, value.range.end)
124    }
125}
126
127// Convenience implementation for creating empty SourcePosMixin with just a span
128impl From<Range<usize>> for SourceRangeMixin<()> {
129    fn from(value: Range<usize>) -> Self {
130        Self::new((), value)
131    }
132}
133
134impl From<(usize, usize)> for SourceRangeMixin<()> {
135    fn from(value: (usize, usize)) -> Self {
136        Self::new_with_start_end((), value.0, value.1)
137    }
138}
139
140impl<T: std::error::Error + 'static> std::error::Error for SourceRangeMixin<T> {
141    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
142        Some(&self.content)
143    }
144}
145
146/// Extension methods for [`SourceRangeMixin`].
147pub trait SourceRangeMixinExt {
148    /// Creates a new `SourceRangeMixin` with the same span as a wrapper.
149    fn into_wrapper<W>(self, wrapper: &SourceRangeMixin<W>) -> SourceRangeMixin<Self>
150    where
151        Self: Sized,
152    {
153        SourceRangeMixin::new(self, wrapper.range.clone())
154    }
155
156    /// Creates a new `SourceRangeMixin` with a given range.
157    fn into_wrapper_range(self, range: Range<usize>) -> SourceRangeMixin<Self>
158    where
159        Self: Sized,
160    {
161        SourceRangeMixin::new(self, range)
162    }
163
164    /// Creates a new `SourceRangeMixin` with a given (start, end) span.
165    fn into_wrapper_span(self, span: (usize, usize)) -> SourceRangeMixin<Self>
166    where
167        Self: Sized,
168    {
169        SourceRangeMixin::new_with_start_end(self, span.0, span.1)
170    }
171}
172
173impl<T> SourceRangeMixinExt for T {}