|
1 | | -use std::{cmp::Ordering, collections::BTreeMap, ops::{Add, Div, Mul, Neg, Rem, Sub}}; |
| 1 | +use std::{cmp::Ordering, collections::{BTreeMap, btree_map::Entry}, ops::{Add, Div, Mul, Neg, Rem, Sub}}; |
2 | 2 |
|
3 | | -use jaq_all::jaq_core::{Error, ops}; |
| 3 | +use jaq_all::jaq_core::{Error, Exn, ops}; |
4 | 4 |
|
5 | 5 | use crate::{RpcValue, Value}; |
6 | 6 |
|
@@ -157,23 +157,38 @@ impl jaq_all::jaq_std::ValT for RpcValue { |
157 | 157 | } |
158 | 158 |
|
159 | 159 | fn as_f64(&self) -> Option<f64> { |
160 | | - todo!("{}", fn_name::uninstantiated!()); |
| 160 | + if let Value::Double(double) = self.value { |
| 161 | + Some(double) |
| 162 | + } else { |
| 163 | + None |
| 164 | + } |
161 | 165 | } |
162 | 166 |
|
163 | 167 | fn is_utf8_str(&self) -> bool { |
164 | | - todo!("{}", fn_name::uninstantiated!()); |
| 168 | + self.is_string() |
165 | 169 | } |
166 | 170 |
|
167 | 171 | fn as_bytes(&self) -> Option<&[u8]> { |
168 | | - todo!("{}", fn_name::uninstantiated!()); |
| 172 | + if let Value::String(str) = &self.value { |
| 173 | + Some(str.as_bytes()) |
| 174 | + } else { |
| 175 | + None |
| 176 | + } |
169 | 177 | } |
170 | 178 |
|
171 | | - fn as_sub_str(&self, _sub: &[u8]) -> Self { |
172 | | - todo!("{}", fn_name::uninstantiated!()); |
| 179 | + fn as_sub_str(&self, sub: &[u8]) -> Self { |
| 180 | + if matches!(&self.value, Value::String(_)) { |
| 181 | + // We do not have any fancy bytes handling, so we will just believe that the sub range |
| 182 | + // is a substring of self, and create a string out of it. |
| 183 | + String::from_utf8_lossy(sub).to_string().into() |
| 184 | + } else { |
| 185 | + // jaq-json panics here, but I don't really like that, so if self is not a String, let's just give null. |
| 186 | + RpcValue::null() |
| 187 | + } |
173 | 188 | } |
174 | 189 |
|
175 | | - fn from_utf8_bytes(_b: impl AsRef<[u8]> + Send + 'static) -> Self { |
176 | | - todo!("{}", fn_name::uninstantiated!()); |
| 190 | + fn from_utf8_bytes(b: impl AsRef<[u8]> + Send + 'static) -> Self { |
| 191 | + String::from_utf8_lossy(b.as_ref()).to_string().into() |
177 | 192 | } |
178 | 193 | } |
179 | 194 |
|
@@ -241,34 +256,108 @@ impl jaq_all::jaq_core::ValT for RpcValue { |
241 | 256 | } |
242 | 257 | } |
243 | 258 |
|
244 | | - fn range(self, _range: jaq_all::jaq_core::val::Range<&Self>) -> ValR { |
245 | | - todo!("{}", fn_name::uninstantiated!()); |
| 259 | + fn range(self, range: jaq_all::jaq_core::val::Range<&Self>) -> ValR { |
| 260 | + let start = range.start.map_or(0, RpcValue::as_usize); |
| 261 | + let end = range.end.map_or(0, RpcValue::as_usize); |
| 262 | + match &self.value { |
| 263 | + Value::String(str) => { |
| 264 | + let bytes = str.as_bytes(); |
| 265 | + bytes.get(start..end).map(|bytes| String::from_utf8_lossy(bytes).to_string().into()).ok_or_else(|| Error::typ(self, "..")) |
| 266 | + } |
| 267 | + Value::List(lst) => { |
| 268 | + lst |
| 269 | + .get(start..end) |
| 270 | + .map(|new_range| new_range.to_vec().into()) |
| 271 | + .ok_or_else(|| Error::typ(self, "..")) |
| 272 | + } |
| 273 | + _ => Err(Error::typ(self, "")), |
| 274 | + } |
246 | 275 | } |
247 | 276 |
|
248 | | - fn map_values<'a, I: Iterator<Item = ValX<'a>>>( |
249 | | - self, |
250 | | - _opt: jaq_all::jaq_core::path::Opt, |
251 | | - _f: impl Fn(Self) -> I, |
| 277 | + fn map_values<'a, I: Iterator<Item = ValX<'a>>>(self, opt: jaq_all::jaq_core::path::Opt, f: impl Fn(Self) -> I, |
252 | 278 | ) -> ValX<'a> { |
253 | | - todo!("{}", fn_name::uninstantiated!()); |
| 279 | + match self.value { |
| 280 | + Value::List(lst) => { |
| 281 | + lst |
| 282 | + .into_iter() |
| 283 | + .flat_map(f) |
| 284 | + .collect::<Result<Vec<_>,_>>() |
| 285 | + .map(Into::into) |
| 286 | + } |
| 287 | + Value::Map(map) => { |
| 288 | + map |
| 289 | + .into_iter() |
| 290 | + .filter_map(|(k, v)| f(v) |
| 291 | + .next() |
| 292 | + .map(|v| Ok((k, v?)))) |
| 293 | + .collect::<Result<BTreeMap<_,_>,_>>() |
| 294 | + .map(Into::into) |
| 295 | + } |
| 296 | + v => opt.fail(RpcValue { meta: None, value: v }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))), |
| 297 | + } |
254 | 298 | } |
255 | 299 |
|
256 | 300 | fn map_index<'a, I: Iterator<Item = ValX<'a>>>( |
257 | 301 | self, |
258 | | - _index: &Self, |
259 | | - _opt: jaq_all::jaq_core::path::Opt, |
260 | | - _f: impl Fn(Self) -> I, |
| 302 | + index: &Self, |
| 303 | + opt: jaq_all::jaq_core::path::Opt, |
| 304 | + f: impl Fn(Self) -> I, |
261 | 305 | ) -> ValX<'a> { |
262 | | - todo!("{}", fn_name::uninstantiated!()); |
| 306 | + if let (Value::String(..) | Value::List(..), Value::Map(o)) = (&self.value, &index.value) { |
| 307 | + let range = o.get("start")..o.get("end"); |
| 308 | + return self.map_range(range, opt, f); |
| 309 | + } |
| 310 | + match self.value { |
| 311 | + Value::Map(map) => { |
| 312 | + let mut map = *map; |
| 313 | + let Value::String(index) = &index.value else { |
| 314 | + return opt.fail(RpcValue { meta: None, value: index.value.clone() }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))) |
| 315 | + }; |
| 316 | + match map.entry(index.to_string()) { |
| 317 | + Entry::Occupied(mut e) => { |
| 318 | + let v = e.get_mut(); |
| 319 | + match f(v.clone()).next().transpose()? { |
| 320 | + Some(y) => e.insert(y), |
| 321 | + None => e.remove(), |
| 322 | + }; |
| 323 | + }, |
| 324 | + Entry::Vacant(e) => { |
| 325 | + if let Some(y) = f(RpcValue::null()).next().transpose()? { |
| 326 | + e.insert(y); |
| 327 | + } |
| 328 | + }, |
| 329 | + } |
| 330 | + Ok(map.into()) |
| 331 | + }, |
| 332 | + #[expect(clippy::cast_possible_truncation, reason = "For now, we hope that usizes are 64-bit")] |
| 333 | + Value::List(lst) => { |
| 334 | + let mut lst = *lst; |
| 335 | + let Value::UInt(index) = &index.value else { |
| 336 | + return opt.fail(RpcValue { meta: None, value: index.value.clone() }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))) |
| 337 | + }; |
| 338 | + let Some(x) = lst.get(*index as usize) else { |
| 339 | + return opt.fail(lst.into(), |oof| Exn::from(Error::typ(oof, ""))); |
| 340 | + }; |
| 341 | + |
| 342 | + if let Some(y) = f(x.clone()).next().transpose()? { |
| 343 | + lst.insert(*index as usize, y); |
| 344 | + } else { |
| 345 | + lst.remove(*index as usize); |
| 346 | + } |
| 347 | + |
| 348 | + Ok(lst.into()) |
| 349 | + }, |
| 350 | + v => opt.fail(RpcValue { meta: None, value: v }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))), |
| 351 | + } |
263 | 352 | } |
264 | 353 |
|
265 | 354 | fn map_range<'a, I: Iterator<Item = ValX<'a>>>( |
266 | 355 | self, |
267 | 356 | _range: jaq_all::jaq_core::val::Range<&Self>, |
268 | | - _opt: jaq_all::jaq_core::path::Opt, |
| 357 | + opt: jaq_all::jaq_core::path::Opt, |
269 | 358 | _f: impl Fn(Self) -> I, |
270 | 359 | ) -> ValX<'a> { |
271 | | - todo!("{}", fn_name::uninstantiated!()); |
| 360 | + opt.fail(RpcValue { meta: None, value: self.value }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))) |
272 | 361 | } |
273 | 362 |
|
274 | 363 | fn as_bool(&self) -> bool { |
|
0 commit comments