///|
/// Attribute maps are encoded as JSON objects so arbitrary Quill attributes
/// and application-defined embeds can round-trip without a fixed schema.
fn as_attribute_map(attributes : Json) -> Map[String, Json] {
match attributes {
Object(map) => map
_ => {}
}
}
///|
pub fn attributes_compose(left : Json, right : Json, keep_null : Bool) -> Json {
let a = as_attribute_map(left)
let b = as_attribute_map(right)
let out : Map[String, Json] = Map([])
for key, value in b {
if keep_null || value != Json::null() {
out.set(key, value)
}
}
for key, value in a {
if b.get(key) is None {
out.set(key, value)
}
}
Json::object(out)
}
///|
pub fn attributes_diff(left : Json, right : Json) -> Json {
if left == right { return empty_attributes() }
let a = as_attribute_map(left)
let b = as_attribute_map(right)
let out : Map[String, Json] = Map([])
for key, value in a {
match b.get(key) {
Some(next) => if next != value { out.set(key, next) }
None => out.set(key, Json::null())
}
}
for key, value in b {
if a.get(key) is None {
out.set(key, value)
}
}
Json::object(out)
}
///|
pub fn attributes_invert(change : Json, base : Json) -> Json {
let a = as_attribute_map(change)
let b = as_attribute_map(base)
let out : Map[String, Json] = Map([])
for key, old in b {
match a.get(key) {
Some(next) => if next != old { out.set(key, old) }
None => ()
}
}
for key, next in a {
if b.get(key) is None && next != Json::null() {
out.set(key, Json::null())
}
}
Json::object(out)
}
///|
pub fn attributes_transform(left : Json, right : Json, priority : Bool) -> Json {
if !priority {
return right
}
let a = as_attribute_map(left)
let b = as_attribute_map(right)
let out : Map[String, Json] = Map([])
for key, value in b {
if a.get(key) is None {
out.set(key, value)
}
}
Json::object(out)
}