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 (
70 Value::Int(_)
71 | Value::Float(_)
72 | Value::Decimal(_)
73 | Value::Bool(_)
74 | Value::None
75 | Value::Object(_)
76 | Value::Function(_)
77 | Value::Class(_)
78 | Value::BoundMethod(_)
79 | Value::Error(_)
80 | Value::Socket(_)
81 | Value::Pup(_)
82 | Value::Bowl(_),
83 _,
84 ) => Err(DogeError::type_error(format!(
85 "cannot index {}",
86 container.describe()
87 ))),
88 }
89}
90
91pub fn index_set(container: &Value, index: &Value, value: Value) -> DogeResult<()> {
94 match (container, index) {
95 (Value::List(items), Value::Int(i)) => {
96 let mut items = items.borrow_mut();
97 let idx = normalize_bigindex(i, items.len())?;
98 items[idx] = value;
99 Ok(())
100 }
101 (Value::Dict(d), Value::Str(k)) => {
102 d.borrow_mut().insert(k.to_string(), value);
103 Ok(())
104 }
105 (Value::Str(_), _) => Err(DogeError::type_error(
106 "cannot assign into a Str (Str values are immutable)",
107 )),
108 (Value::Bytes(_), _) => Err(DogeError::type_error(
109 "cannot assign into a Bytes (Bytes values are immutable)",
110 )),
111 (Value::List(_), _) => Err(DogeError::type_error(format!(
112 "cannot index a List with {} (need an Int)",
113 index.describe()
114 ))),
115 (Value::Dict(_), _) => Err(DogeError::type_error(format!(
116 "cannot index a Dict with {} (keys are Str)",
117 index.describe()
118 ))),
119 (
122 Value::Int(_)
123 | Value::Float(_)
124 | Value::Decimal(_)
125 | Value::Bool(_)
126 | Value::None
127 | Value::Object(_)
128 | Value::Function(_)
129 | Value::Class(_)
130 | Value::BoundMethod(_)
131 | Value::Error(_)
132 | Value::Socket(_)
133 | Value::Pup(_)
134 | Value::Bowl(_),
135 _,
136 ) => Err(DogeError::type_error(format!(
137 "cannot index into {}",
138 container.describe()
139 ))),
140 }
141}
142
143fn slice_bound(what: &str, v: &Value) -> DogeResult<Option<i64>> {
146 match v {
147 Value::None => Ok(None),
148 Value::Int(n) => Ok(Some(bigint_to_bound(n))),
151 _ => Err(DogeError::type_error(format!(
152 "a slice {what} must be an Int, not {}",
153 v.describe()
154 ))),
155 }
156}
157
158fn bigint_to_bound(n: &BigInt) -> i64 {
162 n.to_i64()
163 .unwrap_or(if n.sign() == num_bigint::Sign::Minus {
164 i64::MIN
165 } else {
166 i64::MAX
167 })
168}
169
170fn slice_step(v: &Value) -> DogeResult<i64> {
174 match v {
175 Value::None => Ok(1),
176 Value::Int(n) if n.is_zero() => Err(DogeError::value_error("a slice step cannot be zero")),
177 Value::Int(n) => Ok(bigint_to_bound(n)),
178 _ => Err(DogeError::type_error(format!(
179 "a slice step must be an Int, not {}",
180 v.describe()
181 ))),
182 }
183}
184
185fn slice_indices(start: Option<i64>, end: Option<i64>, step: i64, len: usize) -> Vec<usize> {
189 let len = len as i64;
190 let (lower, upper) = if step < 0 { (-1, len - 1) } else { (0, len) };
191 let clamp = |bound: i64| {
192 let bound = if bound < 0 { bound + len } else { bound };
193 bound.clamp(lower, upper)
194 };
195 let start = match start {
196 Some(s) => clamp(s),
197 None => {
198 if step < 0 {
199 upper
200 } else {
201 lower
202 }
203 }
204 };
205 let end = match end {
206 Some(e) => clamp(e),
207 None => {
208 if step < 0 {
209 lower
210 } else {
211 upper
212 }
213 }
214 };
215
216 let mut indices = Vec::new();
217 let mut i = start;
218 if step > 0 {
219 while i < end {
220 indices.push(i as usize);
221 i += step;
222 }
223 } else {
224 while i > end {
225 indices.push(i as usize);
226 i += step;
227 }
228 }
229 indices
230}
231
232pub fn slice_get(container: &Value, start: &Value, end: &Value, step: &Value) -> DogeResult {
236 let step = slice_step(step)?;
237 let start = slice_bound("start", start)?;
238 let end = slice_bound("end", end)?;
239 match container {
240 Value::List(items) => {
241 let items = items.borrow();
242 let picked = slice_indices(start, end, step, items.len())
243 .into_iter()
244 .map(|i| items[i].clone())
245 .collect();
246 Ok(Value::list(picked))
247 }
248 Value::Str(s) => {
249 let chars: Vec<char> = s.chars().collect();
250 let picked: String = slice_indices(start, end, step, chars.len())
251 .into_iter()
252 .map(|i| chars[i])
253 .collect();
254 Ok(Value::str(picked))
255 }
256 Value::Bytes(b) => {
257 let picked: Vec<u8> = slice_indices(start, end, step, b.len())
258 .into_iter()
259 .map(|i| b[i])
260 .collect();
261 Ok(Value::bytes(picked))
262 }
263 Value::Dict(_)
266 | Value::Int(_)
267 | Value::Float(_)
268 | Value::Decimal(_)
269 | Value::Bool(_)
270 | Value::None
271 | Value::Object(_)
272 | Value::Function(_)
273 | Value::Class(_)
274 | Value::BoundMethod(_)
275 | Value::Error(_)
276 | Value::Socket(_)
277 | Value::Pup(_)
278 | Value::Bowl(_) => Err(DogeError::type_error(format!(
279 "cannot slice {}",
280 container.describe()
281 ))),
282 }
283}
284
285pub fn iter_value(v: &Value) -> DogeResult<Vec<Value>> {
290 match v {
291 Value::List(items) => Ok(items.borrow().clone()),
292 Value::Str(s) => Ok(s.chars().map(|c| Value::str(c.to_string())).collect()),
293 Value::Bytes(b) => Ok(b.iter().map(|&x| Value::int(x)).collect()),
294 Value::Dict(entries) => Ok(entries
295 .borrow()
296 .iter()
297 .map(|(k, _)| Value::str(k))
298 .collect()),
299 Value::Int(_)
302 | Value::Float(_)
303 | Value::Decimal(_)
304 | Value::Bool(_)
305 | Value::None
306 | Value::Object(_)
307 | Value::Function(_)
308 | Value::Class(_)
309 | Value::BoundMethod(_)
310 | Value::Error(_)
311 | Value::Socket(_)
312 | Value::Pup(_)
313 | Value::Bowl(_) => Err(DogeError::type_error(format!(
314 "cannot loop over {}",
315 v.describe()
316 ))),
317 }
318}
319
320pub fn unpack_value(v: &Value, fixed: usize, rest: bool) -> DogeResult<Vec<Value>> {
328 let mut values = iter_value(v)
329 .map_err(|_| DogeError::type_error(format!("cannot unpack {}", v.describe())))?;
330 if rest {
331 if values.len() < fixed {
332 return Err(DogeError::value_error(format!(
333 "expected at least {fixed} values to unpack, but got {}",
334 values.len()
335 )));
336 }
337 let collected = values.split_off(fixed);
338 values.push(Value::list(collected));
339 } else if values.len() != fixed {
340 return Err(DogeError::value_error(format!(
341 "expected {fixed} values to unpack, but got {}",
342 values.len()
343 )));
344 }
345 Ok(values)
346}