Skip to content

Commit 8241130

Browse files
author
Shaurya Dwivedi
committed
Basic ion types with jaq
1 parent 1d7be61 commit 8241130

2 files changed

Lines changed: 110 additions & 6 deletions

File tree

src/bin/ion/commands/jq.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::commands::jq::ion_math::DecimalMath;
2+
mod ion_functions;
23
use crate::commands::{CommandIo, IonCliCommand, WithIonCliArgument};
34
use crate::input::CommandInput;
45
use crate::output::{CommandOutput, CommandOutputWriter};
@@ -70,18 +71,16 @@ fn compile_jq_filter(jq_expr: &str) -> Filter<Native<JaqElement>> {
7071
path: (), // For error reporting, but not currently used by this program
7172
};
7273

73-
// If we wanted to define our own Ion-centric stdlib methods, we'd do something like:
74-
// Loader::new(jaq_std::defs().chain(jaq_ion::defs()))
75-
let loader = Loader::new(jaq_std::defs());
74+
// Load standard jq definitions plus Ion-specific definitions
75+
let loader = Loader::new(jaq_std::defs().chain(ion_functions::ion_defs()));
7676
let arena = Arena::default();
7777

7878
// parse the filter
7979
let modules = loader.load(&arena, program).unwrap();
8080

81-
// compile the filter
81+
// compile the filter with standard and Ion-specific functions
8282
jaq_core::Compiler::default()
83-
// Similar to `defs()` above, this would be our opportunity to extend the built-in filters
84-
.with_funs(jaq_std::funs::<JaqElement>())
83+
.with_funs(jaq_std::funs::<JaqElement>().chain(ion_functions::ion_funs()))
8584
.compile(modules)
8685
.unwrap()
8786
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use crate::commands::jq::JaqElement;
2+
use ion_rs::{Element, IonType};
3+
use jaq_core::{box_iter::box_once, Native, RunPtr, ValT};
4+
use jaq_std::Filter;
5+
6+
/// Ion-specific jq function definitions (filters implemented as definitions)
7+
pub fn ion_defs() -> impl Iterator<Item = jaq_core::load::parse::Def<&'static str>> {
8+
const ION_DEFS: &str = r#"
9+
# Ion type predicates
10+
def istimestamp: ion_type == "timestamp";
11+
def issexp: ion_type == "sexp";
12+
def issymbol: ion_type == "symbol";
13+
def isdecimal: ion_type == "decimal";
14+
15+
# Ion type selectors
16+
def timestamps: select(istimestamp);
17+
def sexps: select(issexp);
18+
def symbols: select(issymbol);
19+
def decimals: select(isdecimal);
20+
21+
# Ion conversion helpers
22+
def to_symbol: if type == "string" then symbol else error("to_symbol requires string input") end;
23+
def to_sexp: if type == "array" then sexp else error("to_sexp requires array input") end;
24+
def to_timestamp: if type == "string" then timestamp else error("to_timestamp requires string input") end;
25+
"#;
26+
27+
jaq_core::load::parse(ION_DEFS, |p| p.defs())
28+
.unwrap_or_default()
29+
.into_iter()
30+
}
31+
32+
/// Ion-specific native jq functions
33+
pub fn ion_funs() -> impl Iterator<Item = Filter<Native<JaqElement>>> {
34+
[timestamp_fn(), sexp_fn(), symbol_fn(), ion_type_fn()].into_iter()
35+
}
36+
37+
/// Creates a timestamp from a string
38+
fn timestamp_fn() -> Filter<Native<JaqElement>> {
39+
let run: RunPtr<JaqElement> = |_, (_, v)| match v.as_str() {
40+
Some(s) => match Element::read_one(s.as_bytes()) {
41+
Ok(element) if element.ion_type() == IonType::Timestamp => {
42+
box_once(Ok(JaqElement::from(element)))
43+
}
44+
_ => box_once(Err(jaq_core::Error::str("invalid timestamp format").into())),
45+
},
46+
None => box_once(Err(jaq_core::Error::str(
47+
"timestamp filter requires a string input",
48+
)
49+
.into())),
50+
};
51+
52+
("timestamp", Box::new([]), Native::new(run))
53+
}
54+
55+
/// Creates an S-expression from an array
56+
fn sexp_fn() -> Filter<Native<JaqElement>> {
57+
let run: RunPtr<JaqElement> = |_, (_, v)| match v.values().collect::<Result<Vec<_>, _>>() {
58+
Ok(items) => {
59+
let elements: Vec<Element> = items.into_iter().map(|je| je.into_inner()).collect();
60+
box_once(Ok(JaqElement::from(Element::from(
61+
ion_rs::SExp::from_iter(elements),
62+
))))
63+
}
64+
Err(e) => box_once(Err(e.into())),
65+
};
66+
67+
("sexp", Box::new([]), Native::new(run))
68+
}
69+
70+
/// Creates a symbol from a string
71+
fn symbol_fn() -> Filter<Native<JaqElement>> {
72+
let run: RunPtr<JaqElement> = |_, (_, v)| match v.as_str() {
73+
Some(s) => box_once(Ok(JaqElement::from(Element::from(ion_rs::Symbol::from(s))))),
74+
None => box_once(Err(jaq_core::Error::str(
75+
"symbol filter requires a string input",
76+
)
77+
.into())),
78+
};
79+
80+
("symbol", Box::new([]), Native::new(run))
81+
}
82+
83+
/// Returns the Ion type name as a string
84+
fn ion_type_fn() -> Filter<Native<JaqElement>> {
85+
let run: RunPtr<JaqElement> = |_, (_, v)| {
86+
let type_name = match v.ion_type() {
87+
IonType::Null => "null",
88+
IonType::Bool => "boolean",
89+
IonType::Int => "integer",
90+
IonType::Float => "float",
91+
IonType::Decimal => "decimal",
92+
IonType::Timestamp => "timestamp",
93+
IonType::Symbol => "symbol",
94+
IonType::String => "string",
95+
IonType::Clob => "clob",
96+
IonType::Blob => "blob",
97+
IonType::List => "list",
98+
IonType::SExp => "sexp",
99+
IonType::Struct => "struct",
100+
};
101+
box_once(Ok(JaqElement::from(Element::from(type_name))))
102+
};
103+
104+
("ion_type", Box::new([]), Native::new(run))
105+
}

0 commit comments

Comments
 (0)