1use std::borrow::Borrow;
22use std::collections::HashSet;
23use std::hash::Hash;
24use std::hash::Hasher;
25use std::iter::FusedIterator;
26use std::rc::Rc;
27use thiserror::Error;
28use yash_syntax::source::Location;
29use yash_syntax::syntax::FullCompoundCommand;
30
31#[derive(Clone, Debug, Eq, PartialEq)]
33pub struct Function {
34 pub name: String,
36
37 pub body: Rc<FullCompoundCommand>,
43
44 pub origin: Location,
46
47 pub read_only_location: Option<Location>,
53}
54
55impl Function {
56 #[inline]
61 #[must_use]
62 pub fn new<N: Into<String>, C: Into<Rc<FullCompoundCommand>>>(
63 name: N,
64 body: C,
65 origin: Location,
66 ) -> Self {
67 Function {
68 name: name.into(),
69 body: body.into(),
70 origin,
71 read_only_location: None,
72 }
73 }
74
75 #[inline]
80 #[must_use]
81 pub fn make_read_only(mut self, location: Location) -> Self {
82 self.read_only_location = Some(location);
83 self
84 }
85
86 #[must_use]
88 pub const fn is_read_only(&self) -> bool {
89 self.read_only_location.is_some()
90 }
91}
92
93#[derive(Clone, Debug, Eq)]
104struct HashEntry(Rc<Function>);
105
106impl PartialEq for HashEntry {
107 fn eq(&self, other: &HashEntry) -> bool {
112 self.0.name == other.0.name
113 }
114}
115
116impl Hash for HashEntry {
117 fn hash<H: Hasher>(&self, state: &mut H) {
122 self.0.name.hash(state)
123 }
124}
125
126impl Borrow<str> for HashEntry {
127 fn borrow(&self) -> &str {
128 &self.0.name
129 }
130}
131
132#[derive(Clone, Debug, Default)]
134pub struct FunctionSet {
135 entries: HashSet<HashEntry>,
136}
137
138#[derive(Clone, Debug, Eq, Error, PartialEq)]
140#[error("cannot redefine read-only function `{}`", .existing.name)]
141#[non_exhaustive]
142pub struct DefineError {
143 pub existing: Rc<Function>,
145 pub new: Rc<Function>,
147}
148
149#[derive(Clone, Debug, Eq, Error, PartialEq)]
151#[error("cannot unset read-only function `{}`", .existing.name)]
152#[non_exhaustive]
153pub struct UnsetError {
154 pub existing: Rc<Function>,
156}
157
158#[derive(Clone, Debug)]
162pub struct Iter<'a> {
163 inner: std::collections::hash_set::Iter<'a, HashEntry>,
164}
165
166impl FunctionSet {
167 #[must_use]
169 pub fn new() -> Self {
170 FunctionSet::default()
171 }
172
173 #[must_use]
175 pub fn get(&self, name: &str) -> Option<&Rc<Function>> {
176 self.entries.get(name).map(|entry| &entry.0)
177 }
178
179 #[inline]
181 #[must_use]
182 pub fn len(&self) -> usize {
183 self.entries.len()
184 }
185
186 #[inline]
188 #[must_use]
189 pub fn is_empty(&self) -> bool {
190 self.entries.is_empty()
191 }
192
193 pub fn define<F: Into<Rc<Function>>>(
199 &mut self,
200 function: F,
201 ) -> Result<Option<Rc<Function>>, DefineError> {
202 #[allow(clippy::mutable_key_type)]
203 fn inner(
204 entries: &mut HashSet<HashEntry>,
205 new: Rc<Function>,
206 ) -> Result<Option<Rc<Function>>, DefineError> {
207 match entries.get(new.name.as_str()) {
208 Some(existing) if existing.0.is_read_only() => Err(DefineError {
209 existing: Rc::clone(&existing.0),
210 new,
211 }),
212
213 _ => Ok(entries.replace(HashEntry(new)).map(|entry| entry.0)),
214 }
215 }
216 inner(&mut self.entries, function.into())
217 }
218
219 pub fn unset(&mut self, name: &str) -> Result<Option<Rc<Function>>, UnsetError> {
224 match self.entries.get(name) {
225 Some(entry) if entry.0.is_read_only() => Err(UnsetError {
226 existing: Rc::clone(&entry.0),
227 }),
228
229 _ => Ok(self.entries.take(name).map(|entry| entry.0)),
230 }
231 }
232
233 pub fn iter(&self) -> Iter<'_> {
237 let inner = self.entries.iter();
238 Iter { inner }
239 }
240}
241
242impl<'a> Iterator for Iter<'a> {
243 type Item = &'a Rc<Function>;
244
245 fn next(&mut self) -> Option<Self::Item> {
246 self.inner.next().map(|entry| &entry.0)
247 }
248}
249
250impl ExactSizeIterator for Iter<'_> {
251 #[inline]
252 fn len(&self) -> usize {
253 self.inner.len()
254 }
255}
256
257impl FusedIterator for Iter<'_> {}
258
259impl<'a> IntoIterator for &'a FunctionSet {
260 type Item = &'a Rc<Function>;
261 type IntoIter = Iter<'a>;
262
263 fn into_iter(self) -> Self::IntoIter {
264 self.iter()
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn defining_new_function() {
274 let mut set = FunctionSet::new();
275 let function = Rc::new(Function::new(
276 "foo",
277 "{ :; }".parse::<FullCompoundCommand>().unwrap(),
278 Location::dummy("foo"),
279 ));
280
281 let result = set.define(function.clone());
282 assert_eq!(result, Ok(None));
283 assert_eq!(set.get("foo"), Some(&function));
284 }
285
286 #[test]
287 fn redefining_existing_function() {
288 let mut set = FunctionSet::new();
289 let function1 = Rc::new(Function::new(
290 "foo",
291 "{ echo 1; }".parse::<FullCompoundCommand>().unwrap(),
292 Location::dummy("foo 1"),
293 ));
294 let function2 = Rc::new(Function::new(
295 "foo",
296 "{ echo 2; }".parse::<FullCompoundCommand>().unwrap(),
297 Location::dummy("foo 2"),
298 ));
299 set.define(function1.clone()).unwrap();
300
301 let result = set.define(function2.clone());
302 assert_eq!(result, Ok(Some(function1)));
303 assert_eq!(set.get("foo"), Some(&function2));
304 }
305
306 #[test]
307 fn redefining_readonly_function() {
308 let mut set = FunctionSet::new();
309 let function1 = Rc::new(
310 Function::new(
311 "foo",
312 "{ echo 1; }".parse::<FullCompoundCommand>().unwrap(),
313 Location::dummy("foo 1"),
314 )
315 .make_read_only(Location::dummy("readonly")),
316 );
317 let function2 = Rc::new(Function::new(
318 "foo",
319 "{ echo 2; }".parse::<FullCompoundCommand>().unwrap(),
320 Location::dummy("foo 2"),
321 ));
322 set.define(function1.clone()).unwrap();
323
324 let error = set.define(function2.clone()).unwrap_err();
325 assert_eq!(error.existing, function1);
326 assert_eq!(error.new, function2);
327 assert_eq!(set.get("foo"), Some(&function1));
328 }
329
330 #[test]
331 fn unsetting_existing_function() {
332 let mut set = FunctionSet::new();
333 let function = Rc::new(Function::new(
334 "foo",
335 "{ :; }".parse::<FullCompoundCommand>().unwrap(),
336 Location::dummy("foo"),
337 ));
338 set.define(function.clone()).unwrap();
339
340 let result = set.unset("foo").unwrap();
341 assert_eq!(result, Some(function));
342 assert_eq!(set.get("foo"), None);
343 }
344
345 #[test]
346 fn unsetting_nonexisting_function() {
347 let mut set = FunctionSet::new();
348
349 let result = set.unset("foo").unwrap();
350 assert_eq!(result, None);
351 assert_eq!(set.get("foo"), None);
352 }
353
354 #[test]
355 fn unsetting_readonly_function() {
356 let mut set = FunctionSet::new();
357 let function = Rc::new(
358 Function::new(
359 "foo",
360 "{ :; }".parse::<FullCompoundCommand>().unwrap(),
361 Location::dummy("foo"),
362 )
363 .make_read_only(Location::dummy("readonly")),
364 );
365 set.define(function.clone()).unwrap();
366
367 let error = set.unset("foo").unwrap_err();
368 assert_eq!(error.existing, function);
369 }
370
371 #[test]
372 fn iteration() {
373 let mut set = FunctionSet::new();
374 let function1 = Rc::new(Function::new(
375 "foo",
376 "{ echo 1; }".parse::<FullCompoundCommand>().unwrap(),
377 Location::dummy("foo"),
378 ));
379 let function2 = Rc::new(Function::new(
380 "bar",
381 "{ echo 2; }".parse::<FullCompoundCommand>().unwrap(),
382 Location::dummy("bar"),
383 ));
384 set.define(function1.clone()).unwrap();
385 set.define(function2.clone()).unwrap();
386
387 let functions = set.iter().collect::<Vec<_>>();
388 assert!(
389 functions[..] == [&function1, &function2] || functions[..] == [&function2, &function1],
390 "{functions:?}"
391 );
392 }
393}