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 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}