1use crate::error::{DogeError, DogeResult};
2use crate::value::Value;
3
4fn normalize_index(i: i64, len: usize) -> DogeResult<usize> {
7 let len_i = len as i64;
8 let idx = if i < 0 { i + len_i } else { i };
9 if idx < 0 || idx >= len_i {
10 Err(DogeError::index_out_of_bounds(format!(
11 "index {i} is out of bounds for length {len}"
12 )))
13 } else {
14 Ok(idx as usize)
15 }
16}
17
18pub fn index_get(container: &Value, index: &Value) -> DogeResult {
21 match (container, index) {
22 (Value::List(items), Value::Int(i)) => {
23 let items = items.borrow();
24 let idx = normalize_index(*i, items.len())?;
25 Ok(items[idx].clone())
26 }
27 (Value::Str(s), Value::Int(i)) => {
28 let chars: Vec<char> = s.chars().collect();
29 let idx = normalize_index(*i, chars.len())?;
30 Ok(Value::str(chars[idx].to_string()))
31 }
32 (Value::Dict(d), Value::Str(k)) => d
33 .borrow()
34 .get(k.as_ref())
35 .cloned()
36 .ok_or_else(|| DogeError::key_error(format!("no such key: {k:?}"))),
37 (Value::List(_) | Value::Str(_), _) => Err(DogeError::type_error(format!(
38 "cannot index {} with {} (need an Int)",
39 container.describe(),
40 index.describe()
41 ))),
42 (Value::Dict(_), _) => Err(DogeError::type_error(format!(
43 "cannot index a Dict with {} (keys are Str)",
44 index.describe()
45 ))),
46 (
49 Value::Int(_)
50 | Value::Float(_)
51 | Value::Bool(_)
52 | Value::None
53 | Value::Object(_)
54 | Value::Function(_)
55 | Value::Class(_)
56 | Value::BoundMethod(_)
57 | Value::Error(_)
58 | Value::Socket(_)
59 | Value::Pup(_)
60 | Value::Bowl(_),
61 _,
62 ) => Err(DogeError::type_error(format!(
63 "cannot index {}",
64 container.describe()
65 ))),
66 }
67}
68
69pub fn index_set(container: &Value, index: &Value, value: Value) -> DogeResult<()> {
72 match (container, index) {
73 (Value::List(items), Value::Int(i)) => {
74 let mut items = items.borrow_mut();
75 let idx = normalize_index(*i, items.len())?;
76 items[idx] = value;
77 Ok(())
78 }
79 (Value::Dict(d), Value::Str(k)) => {
80 d.borrow_mut().insert(k.to_string(), value);
81 Ok(())
82 }
83 (Value::Str(_), _) => Err(DogeError::type_error(
84 "cannot assign into a Str (Str values are immutable)",
85 )),
86 (Value::List(_), _) => Err(DogeError::type_error(format!(
87 "cannot index a List with {} (need an Int)",
88 index.describe()
89 ))),
90 (Value::Dict(_), _) => Err(DogeError::type_error(format!(
91 "cannot index a Dict with {} (keys are Str)",
92 index.describe()
93 ))),
94 (
97 Value::Int(_)
98 | Value::Float(_)
99 | Value::Bool(_)
100 | Value::None
101 | Value::Object(_)
102 | Value::Function(_)
103 | Value::Class(_)
104 | Value::BoundMethod(_)
105 | Value::Error(_)
106 | Value::Socket(_)
107 | Value::Pup(_)
108 | Value::Bowl(_),
109 _,
110 ) => Err(DogeError::type_error(format!(
111 "cannot index into {}",
112 container.describe()
113 ))),
114 }
115}
116
117fn slice_bound(what: &str, v: &Value) -> DogeResult<Option<i64>> {
120 match v {
121 Value::None => Ok(None),
122 Value::Int(n) => Ok(Some(*n)),
123 _ => Err(DogeError::type_error(format!(
124 "a slice {what} must be an Int, not {}",
125 v.describe()
126 ))),
127 }
128}
129
130fn slice_step(v: &Value) -> DogeResult<i64> {
133 match v {
134 Value::None => Ok(1),
135 Value::Int(0) => Err(DogeError::value_error("a slice step cannot be zero")),
136 Value::Int(n) => Ok(*n),
137 _ => Err(DogeError::type_error(format!(
138 "a slice step must be an Int, not {}",
139 v.describe()
140 ))),
141 }
142}
143
144fn slice_indices(start: Option<i64>, end: Option<i64>, step: i64, len: usize) -> Vec<usize> {
148 let len = len as i64;
149 let (lower, upper) = if step < 0 { (-1, len - 1) } else { (0, len) };
150 let clamp = |bound: i64| {
151 let bound = if bound < 0 { bound + len } else { bound };
152 bound.clamp(lower, upper)
153 };
154 let start = match start {
155 Some(s) => clamp(s),
156 None => {
157 if step < 0 {
158 upper
159 } else {
160 lower
161 }
162 }
163 };
164 let end = match end {
165 Some(e) => clamp(e),
166 None => {
167 if step < 0 {
168 lower
169 } else {
170 upper
171 }
172 }
173 };
174
175 let mut indices = Vec::new();
176 let mut i = start;
177 if step > 0 {
178 while i < end {
179 indices.push(i as usize);
180 i += step;
181 }
182 } else {
183 while i > end {
184 indices.push(i as usize);
185 i += step;
186 }
187 }
188 indices
189}
190
191pub fn slice_get(container: &Value, start: &Value, end: &Value, step: &Value) -> DogeResult {
195 let step = slice_step(step)?;
196 let start = slice_bound("start", start)?;
197 let end = slice_bound("end", end)?;
198 match container {
199 Value::List(items) => {
200 let items = items.borrow();
201 let picked = slice_indices(start, end, step, items.len())
202 .into_iter()
203 .map(|i| items[i].clone())
204 .collect();
205 Ok(Value::list(picked))
206 }
207 Value::Str(s) => {
208 let chars: Vec<char> = s.chars().collect();
209 let picked: String = slice_indices(start, end, step, chars.len())
210 .into_iter()
211 .map(|i| chars[i])
212 .collect();
213 Ok(Value::str(picked))
214 }
215 Value::Dict(_)
218 | Value::Int(_)
219 | Value::Float(_)
220 | Value::Bool(_)
221 | Value::None
222 | Value::Object(_)
223 | Value::Function(_)
224 | Value::Class(_)
225 | Value::BoundMethod(_)
226 | Value::Error(_)
227 | Value::Socket(_)
228 | Value::Pup(_)
229 | Value::Bowl(_) => Err(DogeError::type_error(format!(
230 "cannot slice {}",
231 container.describe()
232 ))),
233 }
234}
235
236pub fn iter_value(v: &Value) -> DogeResult<Vec<Value>> {
241 match v {
242 Value::List(items) => Ok(items.borrow().clone()),
243 Value::Str(s) => Ok(s.chars().map(|c| Value::str(c.to_string())).collect()),
244 Value::Dict(entries) => Ok(entries
245 .borrow()
246 .iter()
247 .map(|(k, _)| Value::str(k))
248 .collect()),
249 Value::Int(_)
252 | Value::Float(_)
253 | Value::Bool(_)
254 | Value::None
255 | Value::Object(_)
256 | Value::Function(_)
257 | Value::Class(_)
258 | Value::BoundMethod(_)
259 | Value::Error(_)
260 | Value::Socket(_)
261 | Value::Pup(_)
262 | Value::Bowl(_) => Err(DogeError::type_error(format!(
263 "cannot loop over {}",
264 v.describe()
265 ))),
266 }
267}
268
269pub fn unpack_value(v: &Value, fixed: usize, rest: bool) -> DogeResult<Vec<Value>> {
277 let mut values = iter_value(v)
278 .map_err(|_| DogeError::type_error(format!("cannot unpack {}", v.describe())))?;
279 if rest {
280 if values.len() < fixed {
281 return Err(DogeError::value_error(format!(
282 "expected at least {fixed} values to unpack, but got {}",
283 values.len()
284 )));
285 }
286 let collected = values.split_off(fixed);
287 values.push(Value::list(collected));
288 } else if values.len() != fixed {
289 return Err(DogeError::value_error(format!(
290 "expected {fixed} values to unpack, but got {}",
291 values.len()
292 )));
293 }
294 Ok(values)
295}