1use bigdecimal::{ToPrimitive, Zero};
2use num_bigint::BigInt;
3
4use crate::error::{DogeError, DogeResult};
5use crate::value::Value;
6
7fn normalize_index(i: i64, len: usize) -> DogeResult<usize> {
10 let len_i = len as i64;
11 let idx = if i < 0 { i + len_i } else { i };
12 if idx < 0 || idx >= len_i {
13 Err(DogeError::index_out_of_bounds(format!(
14 "index {i} is out of bounds for length {len}"
15 )))
16 } else {
17 Ok(idx as usize)
18 }
19}
20
21fn normalize_bigindex(i: &BigInt, len: usize) -> DogeResult<usize> {
25 match i.to_i64() {
26 Some(v) => normalize_index(v, len),
27 None => Err(DogeError::index_out_of_bounds(format!(
28 "index {i} is out of bounds for length {len}"
29 ))),
30 }
31}
32
33pub fn index_get(container: &Value, index: &Value) -> DogeResult {
36 match (container, index) {
37 (Value::List(items), Value::Int(i)) => {
38 let items = items.borrow();
39 let idx = normalize_bigindex(i, items.len())?;
40 Ok(items[idx].clone())
41 }
42 (Value::Str(s), Value::Int(i)) => {
43 let chars: Vec<char> = s.chars().collect();
44 let idx = normalize_bigindex(i, chars.len())?;
45 Ok(Value::str(chars[idx].to_string()))
46 }
47 (Value::Bytes(b), Value::Int(i)) => {
48 let idx = normalize_bigindex(i, b.len())?;
49 Ok(Value::int(b[idx]))
50 }
51 (Value::Dict(d), Value::Str(k)) => d
52 .borrow()
53 .get(k.as_ref())
54 .cloned()
55 .ok_or_else(|| DogeError::key_error(format!("no such key: {k:?}"))),
56 (Value::List(_) | Value::Str(_) | Value::Bytes(_), _) => {
57 Err(DogeError::type_error(format!(
58 "cannot index {} with {} (need an Int)",
59 container.describe(),
60 index.describe()
61 )))
62 }
63 (Value::Dict(_), _) => Err(DogeError::type_error(format!(
64 "cannot index a Dict with {} (keys are Str)",
65 index.describe()
66 ))),
67 (
69 Value::Int(_)
70 | Value::Float(_)
71 | Value::Decimal(_)
72 | Value::Bool(_)
73 | Value::None
74 | Value::Object(_)
75 | Value::Function(_)
76 | Value::Class(_)
77 | Value::BoundMethod(_)
78 | Value::Error(_)
79 | Value::Socket(_)
80 | Value::Pup(_)
81 | Value::Bowl(_),
82 _,
83 ) => Err(DogeError::type_error(format!(
84 "cannot index {}",
85 container.describe()
86 ))),
87 }
88}
89
90pub fn index_set(container: &Value, index: &Value, value: Value) -> DogeResult<()> {
93 match (container, index) {
94 (Value::List(items), Value::Int(i)) => {
95 let mut items = items.borrow_mut();
96 let idx = normalize_bigindex(i, items.len())?;
97 items[idx] = value;
98 Ok(())
99 }
100 (Value::Dict(d), Value::Str(k)) => {
101 d.borrow_mut().insert(k.to_string(), value);
102 Ok(())
103 }
104 (Value::Str(_), _) => Err(DogeError::type_error(
105 "cannot assign into a Str (Str values are immutable)",
106 )),
107 (Value::Bytes(_), _) => Err(DogeError::type_error(
108 "cannot assign into a Bytes (Bytes values are immutable)",
109 )),
110 (Value::List(_), _) => Err(DogeError::type_error(format!(
111 "cannot index a List with {} (need an Int)",
112 index.describe()
113 ))),
114 (Value::Dict(_), _) => Err(DogeError::type_error(format!(
115 "cannot index a Dict with {} (keys are Str)",
116 index.describe()
117 ))),
118 (
120 Value::Int(_)
121 | Value::Float(_)
122 | Value::Decimal(_)
123 | Value::Bool(_)
124 | Value::None
125 | Value::Object(_)
126 | Value::Function(_)
127 | Value::Class(_)
128 | Value::BoundMethod(_)
129 | Value::Error(_)
130 | Value::Socket(_)
131 | Value::Pup(_)
132 | Value::Bowl(_),
133 _,
134 ) => Err(DogeError::type_error(format!(
135 "cannot index into {}",
136 container.describe()
137 ))),
138 }
139}
140
141fn slice_bound(what: &str, v: &Value) -> DogeResult<Option<i64>> {
144 match v {
145 Value::None => Ok(None),
146 Value::Int(n) => Ok(Some(bigint_to_bound(n))),
148 _ => Err(DogeError::type_error(format!(
149 "a slice {what} must be an Int, not {}",
150 v.describe()
151 ))),
152 }
153}
154
155fn bigint_to_bound(n: &BigInt) -> i64 {
159 n.to_i64()
160 .unwrap_or(if n.sign() == num_bigint::Sign::Minus {
161 i64::MIN
162 } else {
163 i64::MAX
164 })
165}
166
167fn slice_step(v: &Value) -> DogeResult<i64> {
171 match v {
172 Value::None => Ok(1),
173 Value::Int(n) if n.is_zero() => Err(DogeError::value_error("a slice step cannot be zero")),
174 Value::Int(n) => Ok(bigint_to_bound(n)),
175 _ => Err(DogeError::type_error(format!(
176 "a slice step must be an Int, not {}",
177 v.describe()
178 ))),
179 }
180}
181
182fn slice_indices(start: Option<i64>, end: Option<i64>, step: i64, len: usize) -> Vec<usize> {
186 let len = len as i64;
187 let (lower, upper) = if step < 0 { (-1, len - 1) } else { (0, len) };
188 let clamp = |bound: i64| {
189 let bound = if bound < 0 { bound + len } else { bound };
190 bound.clamp(lower, upper)
191 };
192 let start = match start {
193 Some(s) => clamp(s),
194 None => {
195 if step < 0 {
196 upper
197 } else {
198 lower
199 }
200 }
201 };
202 let end = match end {
203 Some(e) => clamp(e),
204 None => {
205 if step < 0 {
206 lower
207 } else {
208 upper
209 }
210 }
211 };
212
213 let mut indices = Vec::new();
214 let mut i = start;
215 if step > 0 {
216 while i < end {
217 indices.push(i as usize);
218 i += step;
219 }
220 } else {
221 while i > end {
222 indices.push(i as usize);
223 i += step;
224 }
225 }
226 indices
227}
228
229pub fn slice_get(container: &Value, start: &Value, end: &Value, step: &Value) -> DogeResult {
233 let step = slice_step(step)?;
234 let start = slice_bound("start", start)?;
235 let end = slice_bound("end", end)?;
236 match container {
237 Value::List(items) => {
238 let items = items.borrow();
239 let picked = slice_indices(start, end, step, items.len())
240 .into_iter()
241 .map(|i| items[i].clone())
242 .collect();
243 Ok(Value::list(picked))
244 }
245 Value::Str(s) => {
246 let chars: Vec<char> = s.chars().collect();
247 let picked: String = slice_indices(start, end, step, chars.len())
248 .into_iter()
249 .map(|i| chars[i])
250 .collect();
251 Ok(Value::str(picked))
252 }
253 Value::Bytes(b) => {
254 let picked: Vec<u8> = slice_indices(start, end, step, b.len())
255 .into_iter()
256 .map(|i| b[i])
257 .collect();
258 Ok(Value::bytes(picked))
259 }
260 Value::Dict(_)
262 | Value::Int(_)
263 | Value::Float(_)
264 | Value::Decimal(_)
265 | Value::Bool(_)
266 | Value::None
267 | Value::Object(_)
268 | Value::Function(_)
269 | Value::Class(_)
270 | Value::BoundMethod(_)
271 | Value::Error(_)
272 | Value::Socket(_)
273 | Value::Pup(_)
274 | Value::Bowl(_) => Err(DogeError::type_error(format!(
275 "cannot slice {}",
276 container.describe()
277 ))),
278 }
279}
280
281pub fn iter_value(v: &Value) -> DogeResult<Vec<Value>> {
286 match v {
287 Value::List(items) => Ok(items.borrow().clone()),
288 Value::Str(s) => Ok(s.chars().map(|c| Value::str(c.to_string())).collect()),
289 Value::Bytes(b) => Ok(b.iter().map(|&x| Value::int(x)).collect()),
290 Value::Dict(entries) => Ok(entries
291 .borrow()
292 .iter()
293 .map(|(k, _)| Value::str(k))
294 .collect()),
295 Value::Int(_)
297 | Value::Float(_)
298 | Value::Decimal(_)
299 | Value::Bool(_)
300 | Value::None
301 | Value::Object(_)
302 | Value::Function(_)
303 | Value::Class(_)
304 | Value::BoundMethod(_)
305 | Value::Error(_)
306 | Value::Socket(_)
307 | Value::Pup(_)
308 | Value::Bowl(_) => Err(DogeError::type_error(format!(
309 "cannot loop over {}",
310 v.describe()
311 ))),
312 }
313}
314
315pub fn unpack_value(v: &Value, fixed: usize, rest: bool) -> DogeResult<Vec<Value>> {
323 let mut values = iter_value(v)
324 .map_err(|_| DogeError::type_error(format!("cannot unpack {}", v.describe())))?;
325 if rest {
326 if values.len() < fixed {
327 return Err(DogeError::value_error(format!(
328 "expected at least {fixed} values to unpack, but got {}",
329 values.len()
330 )));
331 }
332 let collected = values.split_off(fixed);
333 values.push(Value::list(collected));
334 } else if values.len() != fixed {
335 return Err(DogeError::value_error(format!(
336 "expected {fixed} values to unpack, but got {}",
337 values.len()
338 )));
339 }
340 Ok(values)
341}