use crate::error::Error;
use boon::Compiler;
use relative_path::{Component, RelativePath};
use serde_json::Value;
pub fn new() -> Compiler {
let schema_v1 = include_str!(concat!(env!("OUT_DIR"), "/pgxn-meta-v1.schemas.json"));
let schema_v2 = include_str!(concat!(env!("OUT_DIR"), "/pgxn-meta-v2.schemas.json"));
let mut compiler = spec_compiler();
for str in [schema_v1, schema_v2] {
for line in str.lines() {
let schema: Value = serde_json::from_str(line).unwrap();
let id = &schema["$id"]
.as_str()
.ok_or(Error::UnknownSchemaId)
.unwrap();
compiler.add_resource(id, schema.to_owned()).unwrap();
}
}
compiler
}
pub fn spec_compiler() -> Compiler {
let mut compiler = Compiler::new();
compiler.enable_format_assertions();
compiler.register_format(boon::Format {
name: "path",
func: is_path,
});
compiler.register_format(boon::Format {
name: "glob",
func: is_glob,
});
compiler.register_format(boon::Format {
name: "license",
func: is_license,
});
compiler
}
fn is_path(v: &Value) -> Result<(), Box<dyn std::error::Error>> {
let Value::String(s) = v else { return Ok(()) };
let path = RelativePath::new(s.strip_prefix("./").unwrap_or(s));
for c in path.components() {
match c {
Component::ParentDir => Err("references parent directory")?,
Component::CurDir => Err("references current directory")?,
_ => (),
}
}
Ok(())
}
fn is_glob(v: &Value) -> Result<(), Box<dyn std::error::Error>> {
let Value::String(s) = v else { return Ok(()) };
let path = wax::Glob::new(s.strip_prefix("./").unwrap_or(s))?;
if path.has_semantic_literals() {
Err("references parent or current directory")?
}
Ok(())
}
fn is_license(v: &Value) -> Result<(), Box<dyn std::error::Error>> {
let Value::String(s) = v else { return Ok(()) };
_ = spdx::Expression::parse(s).map_err(crate::error::Error::License)?;
Ok(())
}
#[cfg(test)]
mod tests;