Skip to main content

rust_lstar/
word.rs

1use crate::letter::Letter;
2use smallvec::SmallVec;
3use std::fmt;
4use std::hash::{Hash, Hasher};
5
6/// Represents a word - a sequence of letters
7#[derive(Clone, Debug)]
8pub struct Word {
9    letters: SmallVec<[Letter; 8]>,
10}
11
12impl Word {
13    /// Create a new empty word
14    pub fn new() -> Self {
15        Word {
16            letters: SmallVec::new(),
17        }
18    }
19
20    /// Create a word from a vector of letters
21    pub fn from_letters(letters: Vec<Letter>) -> Self {
22        Word { letters: SmallVec::from_vec(letters) }
23    }
24
25    /// Get the letters in this word
26    pub fn letters(&self) -> &[Letter] {
27        &self.letters
28    }
29
30    /// Get the length of this word
31    pub fn len(&self) -> usize {
32        self.letters.len()
33    }
34
35    /// Check if the word is empty
36    pub fn is_empty(&self) -> bool {
37        self.letters.is_empty()
38    }
39
40    /// Get the last letter
41    pub fn last_letter(&self) -> Option<&Letter> {
42        self.letters.last()
43    }
44
45    /// Append a letter to this word
46    pub fn append_letter(mut self, letter: Letter) -> Self {
47        self.letters.push(letter);
48        self
49    }
50
51    /// Concatenate two words
52    pub fn concatenate(&self, other: &Word) -> Word {
53        let mut letters = SmallVec::with_capacity(self.letters.len() + other.letters.len());
54        letters.extend(self.letters.iter().cloned());
55        letters.extend(other.letters.iter().cloned());
56        Word { letters }
57    }
58
59    /// Concatenate with a single letter (optimized)
60    pub fn concatenate_letter(&self, letter: &Letter) -> Word {
61        let mut letters = SmallVec::with_capacity(self.letters.len() + 1);
62        letters.extend(self.letters.iter().cloned());
63        letters.push(letter.clone());
64        Word { letters }
65    }
66
67    /// Get a prefix of the word up to (but not including) the given length
68    pub fn prefix(&self, len: usize) -> Word {
69        let prefix_len = std::cmp::min(len, self.letters.len());
70        Word {
71            letters: self.letters[..prefix_len].iter().cloned().collect(),
72        }
73    }
74}
75
76impl Default for Word {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82impl PartialEq for Word {
83    fn eq(&self, other: &Self) -> bool {
84        self.letters == other.letters
85    }
86}
87
88impl Eq for Word {}
89
90impl Hash for Word {
91    fn hash<H: Hasher>(&self, state: &mut H) {
92        self.letters.hash(state);
93    }
94}
95
96impl fmt::Display for Word {
97    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98        write!(
99            f,
100            "[{}]",
101            self.letters
102                .iter()
103                .map(|l| l.to_string())
104                .collect::<Vec<_>>()
105                .join(", ")
106        )
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn test_word_creation() {
116        let word = Word::new();
117        assert!(word.is_empty());
118        assert_eq!(word.len(), 0);
119    }
120
121    #[test]
122    fn test_word_from_letters() {
123        let letters = vec![Letter::new("a"), Letter::new("b")];
124        let word = Word::from_letters(letters);
125        assert_eq!(word.len(), 2);
126    }
127
128    #[test]
129    fn test_word_append() {
130        let word = Word::new();
131        let word = word.append_letter(Letter::new("a"));
132        assert_eq!(word.len(), 1);
133    }
134
135    #[test]
136    fn test_word_concatenate() {
137        let w1 = Word::from_letters(vec![Letter::new("a")]);
138        let w2 = Word::from_letters(vec![Letter::new("b")]);
139        let w3 = w1.concatenate(&w2);
140        assert_eq!(w3.len(), 2);
141    }
142
143    #[test]
144    fn test_word_prefix() {
145        let word = Word::from_letters(vec![Letter::new("a"), Letter::new("b"), Letter::new("c")]);
146        let prefix = word.prefix(2);
147        assert_eq!(prefix.len(), 2);
148    }
149}