Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name = "shvproto"
description = "Rust implementation of the SHV protocol"
license = "MIT"
repository = "https://github.com/silicon-heaven/libshvproto-rs"
version = "6.1.8"
version = "6.1.9"
edition = "2024"

[dependencies]
Expand All @@ -16,9 +16,7 @@ hex = "0.4"
clap = { version = "4.5", features = ["derive"] }
simple_logger = { version = "5.0", features = ["stderr"], optional = true }
serde = { version = "1.0.219", features = ["derive"], optional = true }
jaq-all = { git = "https://github.com/01mf02/jaq", tag = "v3.0.0", version = "0.1.0-beta", optional = true }
# FIXME: remove this after jaq support is complete
fn_name = "0.1.0"
jaq-all = { version = "0.1.0-beta", optional = true }

[dev-dependencies]
assert_cmd = "2.0"
Expand Down Expand Up @@ -100,5 +98,6 @@ missing_errors_doc = "allow"
missing_panics_doc = "allow"
must_use_candidate = "allow"
should_panic_without_expect = "allow"
struct_excessive_bools = "allow"
todo = "allow"
too_many_lines = "allow"
1 change: 0 additions & 1 deletion src/bin/cp2cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use jaq_all::{jaq_core::{Ctx, Vars, data::JustLut}, jaq_std};

#[derive(Parser, Debug)]
#[structopt(name = "cp2cp", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = "ChainPack to Cpon and back utility")]
#[expect(clippy::struct_excessive_bools, reason = "Fine for cli args")]
struct Cli {
#[arg(short, long, help = "Cpon indentation string")]
indent: Option<String>,
Expand Down
133 changes: 111 additions & 22 deletions src/jaq.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{cmp::Ordering, collections::BTreeMap, ops::{Add, Div, Mul, Neg, Rem, Sub}};
use std::{cmp::Ordering, collections::{BTreeMap, btree_map::Entry}, ops::{Add, Div, Mul, Neg, Rem, Sub}};

use jaq_all::jaq_core::{Error, ops};
use jaq_all::jaq_core::{Error, Exn, ops};

use crate::{RpcValue, Value};

Expand Down Expand Up @@ -157,23 +157,38 @@ impl jaq_all::jaq_std::ValT for RpcValue {
}

fn as_f64(&self) -> Option<f64> {
todo!("{}", fn_name::uninstantiated!());
if let Value::Double(double) = self.value {
Some(double)
} else {
None
}
}

fn is_utf8_str(&self) -> bool {
todo!("{}", fn_name::uninstantiated!());
self.is_string()
}

fn as_bytes(&self) -> Option<&[u8]> {
todo!("{}", fn_name::uninstantiated!());
if let Value::String(str) = &self.value {
Some(str.as_bytes())
} else {
None
}
}

fn as_sub_str(&self, _sub: &[u8]) -> Self {
todo!("{}", fn_name::uninstantiated!());
fn as_sub_str(&self, sub: &[u8]) -> Self {
if matches!(&self.value, Value::String(_)) {
// We do not have any fancy bytes handling, so we will just believe that the sub range
// is a substring of self, and create a string out of it.
String::from_utf8_lossy(sub).to_string().into()
} else {
// jaq-json panics here, but I don't really like that, so if self is not a String, let's just give null.
RpcValue::null()
}
}

fn from_utf8_bytes(_b: impl AsRef<[u8]> + Send + 'static) -> Self {
todo!("{}", fn_name::uninstantiated!());
fn from_utf8_bytes(b: impl AsRef<[u8]> + Send + 'static) -> Self {
String::from_utf8_lossy(b.as_ref()).to_string().into()
}
}

Expand Down Expand Up @@ -241,34 +256,108 @@ impl jaq_all::jaq_core::ValT for RpcValue {
}
}

fn range(self, _range: jaq_all::jaq_core::val::Range<&Self>) -> ValR {
todo!("{}", fn_name::uninstantiated!());
fn range(self, range: jaq_all::jaq_core::val::Range<&Self>) -> ValR {
let start = range.start.map_or(0, RpcValue::as_usize);
let end = range.end.map_or(0, RpcValue::as_usize);
match &self.value {
Value::String(str) => {
let bytes = str.as_bytes();
bytes.get(start..end).map(|bytes| String::from_utf8_lossy(bytes).to_string().into()).ok_or_else(|| Error::typ(self, ".."))
}
Value::List(lst) => {
lst
.get(start..end)
.map(|new_range| new_range.to_vec().into())
.ok_or_else(|| Error::typ(self, ".."))
}
_ => Err(Error::typ(self, "")),
}
}

fn map_values<'a, I: Iterator<Item = ValX<'a>>>(
self,
_opt: jaq_all::jaq_core::path::Opt,
_f: impl Fn(Self) -> I,
fn map_values<'a, I: Iterator<Item = ValX<'a>>>(self, opt: jaq_all::jaq_core::path::Opt, f: impl Fn(Self) -> I,
) -> ValX<'a> {
todo!("{}", fn_name::uninstantiated!());
match self.value {
Value::List(lst) => {
lst
.into_iter()
.flat_map(f)
.collect::<Result<Vec<_>,_>>()
.map(Into::into)
}
Value::Map(map) => {
map
.into_iter()
.filter_map(|(k, v)| f(v)
.next()
.map(|v| Ok((k, v?))))
.collect::<Result<BTreeMap<_,_>,_>>()
.map(Into::into)
}
v => opt.fail(RpcValue { meta: None, value: v }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))),
}
}

fn map_index<'a, I: Iterator<Item = ValX<'a>>>(
self,
_index: &Self,
_opt: jaq_all::jaq_core::path::Opt,
_f: impl Fn(Self) -> I,
index: &Self,
opt: jaq_all::jaq_core::path::Opt,
f: impl Fn(Self) -> I,
) -> ValX<'a> {
todo!("{}", fn_name::uninstantiated!());
if let (Value::String(..) | Value::List(..), Value::Map(o)) = (&self.value, &index.value) {
let range = o.get("start")..o.get("end");
return self.map_range(range, opt, f);
}
match self.value {
Value::Map(map) => {
let mut map = *map;
let Value::String(index) = &index.value else {
return opt.fail(RpcValue { meta: None, value: index.value.clone() }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, "")))
};
match map.entry(index.to_string()) {
Entry::Occupied(mut e) => {
let v = e.get_mut();
match f(v.clone()).next().transpose()? {
Some(y) => e.insert(y),
None => e.remove(),
};
},
Entry::Vacant(e) => {
if let Some(y) = f(RpcValue::null()).next().transpose()? {
e.insert(y);
}
},
}
Ok(map.into())
},
#[expect(clippy::cast_possible_truncation, reason = "For now, we hope that usizes are 64-bit")]
Value::List(lst) => {
let mut lst = *lst;
let Value::UInt(index) = &index.value else {
return opt.fail(RpcValue { meta: None, value: index.value.clone() }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, "")))
};
let Some(x) = lst.get(*index as usize) else {
return opt.fail(lst.into(), |oof| Exn::from(Error::typ(oof, "")));
};

if let Some(y) = f(x.clone()).next().transpose()? {
lst.insert(*index as usize, y);
} else {
lst.remove(*index as usize);
}

Ok(lst.into())
},
v => opt.fail(RpcValue { meta: None, value: v }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, ""))),
}
}

fn map_range<'a, I: Iterator<Item = ValX<'a>>>(
self,
_range: jaq_all::jaq_core::val::Range<&Self>,
_opt: jaq_all::jaq_core::path::Opt,
opt: jaq_all::jaq_core::path::Opt,
_f: impl Fn(Self) -> I,
) -> ValX<'a> {
todo!("{}", fn_name::uninstantiated!());
opt.fail(RpcValue { meta: None, value: self.value }, |v| jaq_all::jaq_core::Exn::from(Error::typ(v, "")))
}

fn as_bool(&self) -> bool {
Expand Down
Loading