///|
/// A JSON catalog manifest must map each endpoint name to one URI Template
/// string. Key order is sorted for reproducible `TemplateCatalog::names`.
pub(all) enum CatalogJsonError {
RootMustBeObject
TemplateMustBeString(String)
Compilation(CatalogError)
}
///|
pub fn CatalogJsonError::code(self : CatalogJsonError) -> String {
match self {
RootMustBeObject => "root_must_be_object"
TemplateMustBeString(_) => "template_must_be_string"
Compilation(error) => "compile_" + error.code()
}
}
///|
pub fn CatalogJsonError::path(self : CatalogJsonError) -> String {
match self {
RootMustBeObject => ""
TemplateMustBeString(path) => path
Compilation(_) => ""
}
}
///|
/// Build a named catalog from an already-decoded JSON object. JSON parsers
/// may have discarded duplicate object keys before this method is called;
/// callers that reject duplicates must enforce that while reading JSON text.
pub fn compile_catalog_json(
input : Json,
max_templates~ : Int32 = 1024,
max_template_length~ : Int32 = 65536,
) -> Result[TemplateCatalog, CatalogJsonError] {
let fields = match input {
Object(fields) => fields
_ => return Err(RootMustBeObject)
}
let names : Array[String] = []
for name, _ in fields {
names.push(name)
}
names.sort_by(fn(a, b) { a.lexical_compare(b) })
let entries : Array[(String, String)] = []
for name in names {
match fields.get(name) {
Some(String(source)) => entries.push((name, source))
Some(_) => return Err(TemplateMustBeString(pointer_child("", name)))
None => ()
}
}
match compile_catalog(entries, max_templates~, max_template_length~) {
Ok(value) => Ok(value)
Err(error) => Err(Compilation(error))
}
}
///|
pub(all) enum CatalogJsonRenderError {
BindingConversion(JsonBridgeError)
CatalogExpansion(CatalogExpandError)
}
///|
pub fn CatalogJsonRenderError::code(self : CatalogJsonRenderError) -> String {
match self {
BindingConversion(error) => "json_" + error.code()
CatalogExpansion(error) => "catalog_" + error.code()
}
}
///|
/// Render a named URI using a conservative JSON binding object. This method
/// shares the same string/null/array/object conversion as `expand_json`.
pub fn TemplateCatalog::expand_json(
self : TemplateCatalog,
name : String,
values : Json,
max_output~ : Int32 = 1048576,
) -> Result[String, CatalogJsonRenderError] {
if !self.contains(name) {
return Err(CatalogExpansion(UnknownTemplate(name)))
}
let bindings = match bindings_from_json(values) {
Ok(value) => value
Err(error) => return Err(BindingConversion(error))
}
match self.expand(name, bindings, max_output~) {
Ok(value) => Ok(value)
Err(error) => Err(CatalogExpansion(error))
}
}