Skip to main content

hara_native/lang/data/
cons.rs

1use crate::lang::data::{List, Tuple};
2use crate::lang::hash::JavaHash;
3use crate::lang::protocol::{
4    HashType, IColl, IConj, ICons, ICount, IDisplay, IEmpty, IEquality, IHash, IMetadata, INth,
5    IObjType, IPeekFirst, IPersistent, IPopFirst, IPushFirst, ObjType,
6};
7use std::rc::Rc;
8
9#[derive(Debug, Clone)]
10pub struct Cons<E, M = List<E>> {
11    metadata: Option<Rc<crate::lang::data::Metadata>>,
12    first: E,
13    more: M,
14}
15
16impl<E: Clone + PartialEq, M: Clone + PartialEq> PartialEq for Cons<E, M> {
17    fn eq(&self, other: &Self) -> bool {
18        self.first == other.first && self.more == other.more
19    }
20}
21impl<E: Clone, M: Clone + IntoIterator<Item = E>> Cons<E, M> {
22    pub fn new(first: E, more: M) -> Self {
23        Self {
24            metadata: None,
25            first,
26            more,
27        }
28    }
29    pub fn peek_first(&self) -> &E {
30        &self.first
31    }
32    pub fn iter(&self) -> impl Iterator<Item = E> {
33        std::iter::once(self.first.clone()).chain(self.more.clone())
34    }
35    pub fn to_list(&self) -> List<E> {
36        self.iter().collect()
37    }
38}
39impl<E: Clone, M: Clone + IntoIterator<Item = E>> IntoIterator for Cons<E, M> {
40    type Item = E;
41    type IntoIter = std::vec::IntoIter<E>;
42    fn into_iter(self) -> Self::IntoIter {
43        std::iter::once(self.first)
44            .chain(self.more)
45            .collect::<Vec<_>>()
46            .into_iter()
47    }
48}
49impl<E: Clone, M: Clone + IntoIterator<Item = E>> ICount for Cons<E, M> {
50    fn count(&self) -> usize {
51        1 + self.more.clone().into_iter().count()
52    }
53}
54impl<E: Clone> INth<E> for Cons<E> {
55    fn nth(&self, index: usize) -> Option<&E> {
56        if index == 0 {
57            Some(&self.first)
58        } else {
59            self.more.get(index - 1)
60        }
61    }
62}
63impl<E: Clone, M: Clone> IPeekFirst<E> for Cons<E, M> {
64    fn peek_first(&self) -> Option<E> {
65        Some(self.first.clone())
66    }
67}
68impl<E: Clone, M: Clone> IPushFirst<E> for Cons<E, M> {
69    type Output = Cons<E, Self>;
70    fn push_first(&self, value: E) -> Self::Output {
71        Cons {
72            metadata: self.metadata.clone(),
73            first: value,
74            more: self.clone(),
75        }
76    }
77}
78impl<E: Clone, M: Clone> IConj<E> for Cons<E, M> {
79    type Output = Cons<E, Self>;
80    fn conj(&self, value: E) -> Self::Output {
81        self.push_first(value)
82    }
83}
84impl<E: Clone, M: Clone> ICons<E> for Cons<E, M> {
85    type Output = Cons<E, Self>;
86    fn cons(&self, value: E) -> Self::Output {
87        Cons {
88            metadata: None,
89            first: value,
90            more: self.clone(),
91        }
92    }
93}
94impl<E: Clone, M: Clone> IPopFirst for Cons<E, M> {
95    type Output = M;
96    fn pop_first(&self) -> Self::Output {
97        self.more.clone()
98    }
99}
100impl<E: Clone, M: Clone> IEmpty for Cons<E, M> {
101    type Output = Tuple<E>;
102    fn empty(&self) -> Self::Output {
103        Tuple::Tup0.with_meta(self.metadata.clone())
104    }
105}
106impl<E: Clone, M: Clone> IMetadata for Cons<E, M> {
107    type Metadata = Rc<crate::lang::data::Metadata>;
108    fn meta(&self) -> Option<&Self::Metadata> {
109        self.metadata.as_ref()
110    }
111    fn with_meta(&self, metadata: Option<Self::Metadata>) -> Self {
112        Self {
113            metadata,
114            ..self.clone()
115        }
116    }
117}
118impl<E: Clone, M: Clone> IPersistent for Cons<E, M> {}
119impl<E: Clone + PartialEq, M: Clone + IntoIterator<Item = E>> IEquality for Cons<E, M> {
120    fn equality(&self, other: &Self) -> bool {
121        self.iter().eq(other.iter())
122    }
123}
124impl<E: Clone + std::fmt::Debug, M: Clone + IntoIterator<Item = E>> IDisplay for Cons<E, M> {
125    fn display(&self) -> String {
126        format!(
127            "({})",
128            self.iter()
129                .map(|v| format!("{v:?}"))
130                .collect::<Vec<_>>()
131                .join(" ")
132        )
133    }
134}
135impl<E: Clone + std::hash::Hash + JavaHash, M: Clone + IntoIterator<Item = E>> IHash
136    for Cons<E, M>
137{
138    fn hash_calc(&self, hash_type: HashType) -> u64 {
139        // Java Cons is sequential: ordered composition, "::SEQUENTIAL" seed.
140        crate::lang::hash::compose_ordered(
141            "SEQUENTIAL",
142            self.iter().map(|value| value.java_hash(hash_type)),
143        ) as u64
144    }
145}
146impl<E: Clone + std::fmt::Debug, M: Clone + IntoIterator<Item = E>> IObjType for Cons<E, M> {
147    fn obj_type(&self) -> ObjType {
148        ObjType::Sequential
149    }
150}
151impl<E, M> IColl<E> for Cons<E, M>
152where
153    E: Clone + PartialEq + std::fmt::Debug + std::hash::Hash + JavaHash,
154    M: Clone + IntoIterator<Item = E>,
155{
156    fn start_string(&self) -> &'static str {
157        "("
158    }
159    fn end_string(&self) -> &'static str {
160        ")"
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::Cons;
167    use crate::lang::data::List;
168    use crate::lang::protocol::{
169        IConj, ICons, ICount, IEmpty, IMetadata, INth, IPopFirst, IPushFirst,
170    };
171    #[test]
172    fn linked_navigation() {
173        let c = Cons::new(1, vec![2, 3].into_iter().collect::<List<_>>());
174        assert_eq!(c.count(), 3);
175        assert_eq!(c.nth(2), Some(&3));
176        assert_eq!(c.pop_first(), List::from(vec![2, 3]));
177        assert!(Cons::new(1, List::new()).pop_first().is_empty());
178
179        let documented = c.with_meta(Some(crate::lang::data::Metadata::document("doc")));
180        assert_eq!(
181            documented.push_first(0).meta().map(|m| m.doc().unwrap()),
182            Some("doc")
183        );
184        let pushed = documented.push_first(0);
185        assert_eq!(pushed.peek_first(), &0);
186        assert_eq!(pushed.pop_first(), documented);
187        let conjoined = documented.conj(0);
188        assert_eq!(conjoined.pop_first(), documented);
189        assert_eq!(conjoined.meta().map(|m| m.doc().unwrap()), Some("doc"));
190        let consed = documented.cons(0);
191        assert_eq!(consed.meta(), None);
192        assert_eq!(consed.pop_first(), documented);
193        assert!(documented.empty().is_empty());
194        assert_eq!(
195            documented.empty().meta().map(|m| m.doc().unwrap()),
196            Some("doc")
197        );
198    }
199}