Skip to main content

oxilean_std/string/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6/// A half-open byte range `[start, end)` in a source file.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
8pub struct Span {
9    /// Inclusive start byte position.
10    pub start: BytePos,
11    /// Exclusive end byte position.
12    pub end: BytePos,
13}
14impl Span {
15    /// Create a new span.
16    pub fn new(start: BytePos, end: BytePos) -> Self {
17        Self { start, end }
18    }
19    /// Create a span from raw byte offsets.
20    pub fn from_offsets(start: u32, end: u32) -> Self {
21        Self {
22            start: BytePos(start),
23            end: BytePos(end),
24        }
25    }
26    /// Create a zero-length span at position `pos`.
27    pub fn point(pos: BytePos) -> Self {
28        Self {
29            start: pos,
30            end: pos,
31        }
32    }
33    /// Length in bytes.
34    pub fn len(self) -> u32 {
35        self.end.0.saturating_sub(self.start.0)
36    }
37    /// Returns `true` if this span is empty.
38    pub fn is_empty(self) -> bool {
39        self.start >= self.end
40    }
41    /// Merge two spans into the smallest span covering both.
42    pub fn merge(self, other: Span) -> Span {
43        Span {
44            start: BytePos(self.start.0.min(other.start.0)),
45            end: BytePos(self.end.0.max(other.end.0)),
46        }
47    }
48    /// Check whether this span contains `pos`.
49    pub fn contains(self, pos: BytePos) -> bool {
50        self.start <= pos && pos < self.end
51    }
52    /// Slice the source text covered by this span.
53    ///
54    /// Returns `None` if the span is out of bounds.
55    pub fn slice<'a>(&self, src: &'a str) -> Option<&'a str> {
56        let s = self.start.to_usize();
57        let e = self.end.to_usize();
58        src.get(s..e)
59    }
60    /// Shift both endpoints by `n` bytes.
61    pub fn shift(self, n: u32) -> Self {
62        Span {
63            start: self.start.shift(n),
64            end: self.end.shift(n),
65        }
66    }
67}
68/// Extended string encoder/decoder.
69#[allow(dead_code)]
70pub struct StringEncoderExt {
71    /// Encoding type label.
72    pub encoding: String,
73}
74impl StringEncoderExt {
75    /// Create a UTF-8 encoder.
76    pub fn utf8() -> Self {
77        Self {
78            encoding: "UTF-8".to_string(),
79        }
80    }
81    /// Encode a string to bytes (UTF-8).
82    pub fn encode(&self, s: &str) -> Vec<u8> {
83        s.as_bytes().to_vec()
84    }
85    /// Decode bytes from UTF-8.
86    pub fn decode(&self, bytes: &[u8]) -> Result<String, std::string::FromUtf8Error> {
87        String::from_utf8(bytes.to_vec())
88    }
89    /// Check round-trip property.
90    pub fn roundtrip(&self, s: &str) -> bool {
91        self.decode(&self.encode(s)).as_deref() == Ok(s)
92    }
93}
94/// Extended string monoid utilities.
95#[allow(dead_code)]
96pub struct StringMonoidExt {
97    /// Accumulated string.
98    pub buffer: String,
99}
100impl StringMonoidExt {
101    /// Create a new empty monoid.
102    pub fn new() -> Self {
103        Self {
104            buffer: String::new(),
105        }
106    }
107    /// Append another string (monoid operation).
108    pub fn mappend(&mut self, other: &str) {
109        self.buffer.push_str(other);
110    }
111    /// Check identity: buffer == ""
112    pub fn is_identity(&self) -> bool {
113        self.buffer.is_empty()
114    }
115}
116/// A (1-based) line and column position in a source file.
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
118pub struct LineCol {
119    /// 1-based line number.
120    pub line: u32,
121    /// 1-based column number (in characters, not bytes).
122    pub col: u32,
123}
124impl LineCol {
125    /// Create a new `LineCol`.
126    pub fn new(line: u32, col: u32) -> Self {
127        Self { line, col }
128    }
129}
130/// Extended substring finder using KMP or naive search.
131#[allow(dead_code)]
132pub struct SubstringFinder2 {
133    /// The haystack to search in.
134    pub text: String,
135    /// The pattern to search for.
136    pub pattern: String,
137}
138impl SubstringFinder2 {
139    /// Create a new finder.
140    pub fn new(text: impl Into<String>, pattern: impl Into<String>) -> Self {
141        Self {
142            text: text.into(),
143            pattern: pattern.into(),
144        }
145    }
146    /// Find all non-overlapping occurrences using naive search.
147    pub fn find_all(&self) -> Vec<usize> {
148        let mut positions = Vec::new();
149        let t = &self.text;
150        let p = &self.pattern;
151        if p.is_empty() {
152            return positions;
153        }
154        let mut start = 0;
155        while let Some(pos) = t[start..].find(p.as_str()) {
156            positions.push(start + pos);
157            start += pos + p.len();
158        }
159        positions
160    }
161    /// Find using KMP algorithm.
162    pub fn find_kmp(&self) -> Vec<usize> {
163        str_ext2_kmp_search(&self.text, &self.pattern)
164    }
165}
166/// Levenshtein metric (version 2) with additional operations.
167#[allow(dead_code)]
168pub struct LevenshteinMetric2 {
169    /// Maximum allowed distance (None = unlimited).
170    pub max_dist: Option<usize>,
171}
172impl LevenshteinMetric2 {
173    /// Create an unbounded metric.
174    pub fn new() -> Self {
175        Self { max_dist: None }
176    }
177    /// Create with a maximum distance threshold.
178    pub fn with_max(d: usize) -> Self {
179        Self { max_dist: Some(d) }
180    }
181    /// Compute the distance between two strings.
182    pub fn distance(&self, a: &str, b: &str) -> usize {
183        str_ext2_levenshtein(a, b)
184    }
185    /// Check if distance is within threshold.
186    pub fn within_threshold(&self, a: &str, b: &str) -> bool {
187        match self.max_dist {
188            Some(d) => self.distance(a, b) <= d,
189            None => true,
190        }
191    }
192    /// Check metric identity: d(a, a) = 0.
193    pub fn identity_law(&self, a: &str) -> bool {
194        self.distance(a, a) == 0
195    }
196    /// Check metric symmetry: d(a, b) = d(b, a).
197    pub fn symmetry_law(&self, a: &str, b: &str) -> bool {
198        self.distance(a, b) == self.distance(b, a)
199    }
200}
201/// A mutable string builder with convenient push methods.
202///
203/// Accumulates string fragments and can be finalised into a `String`.
204/// Supports indentation tracking for pretty-printing.
205#[derive(Debug, Default, Clone)]
206pub struct StringBuilder {
207    pub(super) buf: String,
208    pub(super) indent_level: usize,
209    indent_width: usize,
210}
211impl StringBuilder {
212    /// Create a new empty `StringBuilder`.
213    pub fn new() -> Self {
214        Self {
215            buf: String::new(),
216            indent_level: 0,
217            indent_width: 2,
218        }
219    }
220    /// Create a `StringBuilder` with pre-allocated capacity.
221    pub fn with_capacity(cap: usize) -> Self {
222        Self {
223            buf: String::with_capacity(cap),
224            indent_level: 0,
225            indent_width: 2,
226        }
227    }
228    /// Set the number of spaces per indent level (default: 2).
229    pub fn set_indent_width(&mut self, w: usize) -> &mut Self {
230        self.indent_width = w;
231        self
232    }
233    /// Append a string slice.
234    pub fn push_str(&mut self, s: &str) -> &mut Self {
235        self.buf.push_str(s);
236        self
237    }
238    /// Append a single character.
239    pub fn push(&mut self, c: char) -> &mut Self {
240        self.buf.push(c);
241        self
242    }
243    /// Append a newline followed by the current indentation.
244    pub fn newline(&mut self) -> &mut Self {
245        self.buf.push('\n');
246        self.buf
247            .push_str(&" ".repeat(self.indent_level * self.indent_width));
248        self
249    }
250    /// Append `s` followed by a newline (and indent for next line).
251    pub fn line(&mut self, s: &str) -> &mut Self {
252        self.buf.push_str(s);
253        self.newline();
254        self
255    }
256    /// Increase the indentation level by 1.
257    pub fn indent(&mut self) -> &mut Self {
258        self.indent_level += 1;
259        self
260    }
261    /// Decrease the indentation level by 1 (minimum 0).
262    pub fn dedent(&mut self) -> &mut Self {
263        self.indent_level = self.indent_level.saturating_sub(1);
264        self
265    }
266    /// Append `s` formatted with `format!`.
267    pub fn push_fmt(&mut self, args: std::fmt::Arguments<'_>) -> &mut Self {
268        use std::fmt::Write;
269        let _ = self.buf.write_fmt(args);
270        self
271    }
272    /// Current byte length of the buffer.
273    pub fn len(&self) -> usize {
274        self.buf.len()
275    }
276    /// Returns `true` if the buffer is empty.
277    pub fn is_empty(&self) -> bool {
278        self.buf.is_empty()
279    }
280    /// Finalise and return the accumulated string.
281    pub fn finish(self) -> String {
282        self.buf
283    }
284    /// Get a reference to the accumulated string so far.
285    pub fn as_str(&self) -> &str {
286        &self.buf
287    }
288    /// Clear the buffer, keeping capacity.
289    pub fn clear(&mut self) {
290        self.buf.clear();
291    }
292    /// Append a separator only if the buffer is non-empty.
293    pub fn sep(&mut self, s: &str) -> &mut Self {
294        if !self.buf.is_empty() {
295            self.buf.push_str(s);
296        }
297        self
298    }
299}
300/// A byte position in a source file.
301#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
302pub struct BytePos(pub u32);
303impl BytePos {
304    /// Create a `BytePos` from a `usize` offset.
305    pub fn from_usize(n: usize) -> Self {
306        Self(n as u32)
307    }
308    /// Convert to `usize`.
309    pub fn to_usize(self) -> usize {
310        self.0 as usize
311    }
312    /// Shift the position forward by `n` bytes.
313    pub fn shift(self, n: u32) -> Self {
314        Self(self.0 + n)
315    }
316}
317/// Rolling hash for Rabin-Karp pattern matching.
318#[allow(dead_code)]
319pub struct RollingHashExt {
320    /// Base for the polynomial hash.
321    pub base: u64,
322    /// Modulus for the hash.
323    pub modulus: u64,
324}
325impl RollingHashExt {
326    /// Create with standard parameters.
327    pub fn new() -> Self {
328        Self {
329            base: 31,
330            modulus: 1_000_000_007,
331        }
332    }
333    /// Compute hash of a string.
334    pub fn hash_str(&self, s: &str) -> u64 {
335        s.bytes().fold(0u64, |acc, b| {
336            (acc.wrapping_mul(self.base).wrapping_add(b as u64)) % self.modulus
337        })
338    }
339    /// Find pattern in text using Rabin-Karp.
340    pub fn find(&self, text: &str, pattern: &str) -> Vec<usize> {
341        str_ext2_rabin_karp(text, pattern, self.base, self.modulus)
342    }
343}