///|
/// An Ion value that has no PartiQL equivalent.
pub(all) suberror LowerError {
/// A symbol whose text is unknown (a symbol ID that no table defines).
SymbolWithoutText
} derive(Debug, Eq)
///|
/// Converts an Ion timestamp: missing fields become 0, an unknown offset
/// becomes UTC, and more than 9 fractional digits are truncated.
pub fn timestamp_of_ion(ts : @ion_core.Timestamp) -> Timestamp {
let (nanos, precision) = match ts.fraction {
None => (0, 0)
Some(fraction) => {
// Int64: `-exponent` must not overflow.
let digits = -fraction.exponent.to_int64()
if digits <= 0L {
(0, 0)
} else if digits <= 9L {
let d = digits.to_int()
let scale = @bigint.BigInt::from_int(pow10(9 - d))
((fraction.coefficient * scale).to_int(), d)
} else {
// Dropping more digits than the coefficient has leaves 0; never
// build a power of ten larger than the coefficient.
let drop = digits - 9L
if drop > digit_count(fraction.coefficient).to_int64() {
(0, 9)
} else {
let scale = 10N.pow(@bigint.BigInt::from_int64(drop))
((fraction.coefficient / scale).to_int(), 9)
}
}
}
}
let offset = match ts.offset {
Some(Known(minutes)) => minutes
_ => 0
}
// An Ion timestamp is always valid, so these constructors do not raise.
let date = Date::{
year: ts.year,
month: ts.month.unwrap_or(1),
day: ts.day.unwrap_or(1),
}
let time = Time::{
hour: ts.hour.unwrap_or(0),
minute: ts.minute.unwrap_or(0),
second: ts.second.unwrap_or(0),
nanos,
precision,
offset: Some(offset),
}
{ date, time, }
}
///|
fn lower_ion(x : @ion_core.IonValue) -> Value raise LowerError {
match x.kind() {
Null(_) => Null
Bool(b) => Value::from_bool(b)
Int(n) => Value::from_bigint(n)
Float(d) => Value::from_float(d)
Decimal(d) => Value::from_decimal(d)
Timestamp(ts) => Value::from_timestamp(timestamp_of_ion(ts))
Symbol(token) =>
match token.text() {
Some(text) => Value::from_string(text)
None => raise SymbolWithoutText
}
String(s) => Value::from_string(s)
Clob(bytes) => Value::from_clob(bytes)
Blob(bytes) => Value::from_blob(bytes)
List(items) | Sexp(items) =>
Value::from_list(items.map(item => Value::from_ion(item)))
Struct(fields) => {
let pairs : Array[(String, Value)] = []
for field in fields {
match field.name().text() {
Some(name) => pairs.push((name, Value::from_ion(field.value())))
None => raise SymbolWithoutText
}
}
Value::from_tuple(Tuple(pairs))
}
}
}
///|
/// Converts a boxed Ion value to its core value, one level deep. Other
/// values are returned unchanged.
pub fn Value::lower(self : Value) -> Value raise LowerError {
match self {
Ion(x) => lower_ion(x)
other => other
}
}
///|
/// Lowers recursively, including the children of collections and tuples.
pub fn Value::lower_deep(self : Value) -> Value raise LowerError {
self.fold(
(node, children) => {
let node = node.lower()
match node {
List(_) => Value::from_list(children)
Bag(_) => Value::from_bag(Bag(children))
Tuple(t) =>
Value::from_tuple(
Tuple(t.fields().mapi((i, p) => (p.0, children[i]))),
)
Map(m) =>
Value::from_map({
key_type: m.key_type,
value_type: m.value_type,
entries: m
.entries()
.mapi((i, _) => (children[2 * i], children[2 * i + 1])),
})
Graph(g) => {
let mut at = 0
let payload = (p : Value?) => {
match p {
None => None
Some(_) => {
let v = children[at]
at += 1
Some(v)
}
}
}
let nodes = g.nodes().map(n => { ..n, payload: payload(n.payload), })
let edges = g.edges().map(e => { ..e, payload: payload(e.payload), })
Value::from_graph({ nodes, edges, })
}
other => other
}
},
prepare=lowered_or_self,
)
}