implement schema registry
This commit is contained in:
parent
00c908df85
commit
dc454ef096
|
@ -0,0 +1,91 @@
|
|||
use crate::api::schema::*;
|
||||
|
||||
use failure::*;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Registry {
|
||||
formats: HashMap<&'static str, Arc<ApiStringFormat>>,
|
||||
options: HashMap<&'static str, Arc<Schema>>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
|
||||
pub fn new() -> Self {
|
||||
|
||||
let mut me = Self {
|
||||
formats: HashMap::new(),
|
||||
options: HashMap::new(),
|
||||
};
|
||||
|
||||
me.initialize_formats();
|
||||
|
||||
me.initialize_options();
|
||||
|
||||
me
|
||||
}
|
||||
|
||||
pub fn register_format(&mut self, name: &'static str, format: ApiStringFormat) {
|
||||
|
||||
if let Some(format) = self.formats.get(name) {
|
||||
panic!("standard format '{}' already registered.", name); // fixme: really panic?
|
||||
}
|
||||
|
||||
self.formats.insert(name, Arc::new(format));
|
||||
}
|
||||
|
||||
pub fn lookup_format(&self, name: &str) -> Option<Arc<ApiStringFormat>> {
|
||||
|
||||
if let Some(format) = self.formats.get(name) {
|
||||
return Some(format.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn register_option(&mut self, name: &'static str, schema: Schema) {
|
||||
|
||||
if let Some(schema) = self.options.get(name) {
|
||||
panic!("standard option '{}' already registered.", name); // fixme: really panic?
|
||||
}
|
||||
|
||||
self.options.insert(name, Arc::new(schema));
|
||||
}
|
||||
|
||||
pub fn lookup_option(&self, name: &str) -> Option<Arc<Schema>> {
|
||||
|
||||
if let Some(schema) = self.options.get(name) {
|
||||
return Some(schema.clone());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn initialize_formats(&mut self) {
|
||||
|
||||
self.register_format("pve-node", ApiStringFormat::VerifyFn(verify_pve_node));
|
||||
|
||||
}
|
||||
|
||||
fn initialize_options(&mut self) {
|
||||
|
||||
self.register_option(
|
||||
"pve-vmid",
|
||||
Integer!{
|
||||
description => "The (unique) ID of the VM.",
|
||||
minimum => Some(1),
|
||||
optional => false
|
||||
});
|
||||
|
||||
self.register_option(
|
||||
"pve-node",
|
||||
ApiString!{
|
||||
description => "The cluster node name.",
|
||||
format => self.lookup_format("pve-node")
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn verify_pve_node(value: &str) -> Result<(), Error> {
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -61,7 +61,7 @@ pub struct StringSchema {
|
|||
pub default: Option<&'static str>,
|
||||
pub min_length: Option<usize>,
|
||||
pub max_length: Option<usize>,
|
||||
pub format: ApiStringFormat,
|
||||
pub format: Option<Arc<ApiStringFormat>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -123,15 +123,33 @@ pub const DEFAULTSTRING: StringSchema = StringSchema {
|
|||
default: None,
|
||||
min_length: None,
|
||||
max_length: None,
|
||||
format: ApiStringFormat::None,
|
||||
format: None,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ApiStringFormat {
|
||||
None,
|
||||
Enum(Vec<String>),
|
||||
Pattern(Box<Regex>),
|
||||
Complex(Arc<Schema>),
|
||||
VerifyFn(fn(&str) -> Result<(), Error>),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ApiStringFormat {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ApiStringFormat::VerifyFn(fnptr) => {
|
||||
write!(f, "VerifyFn({:p}", fnptr)
|
||||
}
|
||||
ApiStringFormat::Enum(strvec) => {
|
||||
write!(f, "Enum({:?}", strvec)
|
||||
}
|
||||
ApiStringFormat::Pattern(regex) => {
|
||||
write!(f, "Pattern({:?}", regex)
|
||||
}
|
||||
ApiStringFormat::Complex(schema) => {
|
||||
write!(f, "Complex({:?}", schema)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
@ -219,8 +237,8 @@ fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Error>
|
|||
}
|
||||
}
|
||||
|
||||
match string_schema.format {
|
||||
ApiStringFormat::None => { /* do nothing */ }
|
||||
if let Some(ref format) = string_schema.format {
|
||||
match format.as_ref() {
|
||||
ApiStringFormat::Pattern(ref regex) => {
|
||||
if !regex.is_match(&res) {
|
||||
bail!("value does not match the regex pattern");
|
||||
|
@ -234,6 +252,10 @@ fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Error>
|
|||
ApiStringFormat::Complex(ref _subschema) => {
|
||||
bail!("implement me!");
|
||||
}
|
||||
ApiStringFormat::VerifyFn(verify_fn) => {
|
||||
verify_fn(&res)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Value::String(res)
|
||||
|
@ -390,7 +412,7 @@ fn test_query_string() {
|
|||
|
||||
let schema = parameter!{name => Arc::new(ApiString!{
|
||||
optional => false,
|
||||
format => ApiStringFormat::Pattern(Box::new(Regex::new("test").unwrap()))
|
||||
format => Some(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("test").unwrap()))))
|
||||
})};
|
||||
|
||||
let res = parse_query_string("name=abcd", &schema, true);
|
||||
|
@ -401,7 +423,7 @@ fn test_query_string() {
|
|||
|
||||
let schema = parameter!{name => Arc::new(ApiString!{
|
||||
optional => false,
|
||||
format => ApiStringFormat::Pattern(Box::new(Regex::new("^test$").unwrap()))
|
||||
format => Some(Arc::new(ApiStringFormat::Pattern(Box::new(Regex::new("^test$").unwrap()))))
|
||||
})};
|
||||
|
||||
let res = parse_query_string("name=ateststring", &schema, true);
|
||||
|
@ -414,7 +436,7 @@ fn test_query_string() {
|
|||
|
||||
let schema = parameter!{name => Arc::new(ApiString!{
|
||||
optional => false,
|
||||
format => ApiStringFormat::Enum(vec!["ev1".into(), "ev2".into()])
|
||||
format => Some(Arc::new(ApiStringFormat::Enum(vec!["ev1".into(), "ev2".into()])))
|
||||
})};
|
||||
|
||||
let res = parse_query_string("name=noenum", &schema, true);
|
||||
|
|
|
@ -5,6 +5,7 @@ pub mod api {
|
|||
|
||||
#[macro_use]
|
||||
pub mod schema;
|
||||
pub mod registry;
|
||||
#[macro_use]
|
||||
pub mod router;
|
||||
pub mod config;
|
||||
|
|
|
@ -20,8 +20,10 @@ use hyper;
|
|||
fn main() {
|
||||
println!("Proxmox REST Server example.");
|
||||
|
||||
let prop = Arc::new(ApiString!{ optional => true });
|
||||
let schema = parameter!{
|
||||
name => Arc::new(ApiString!{ optional => true })
|
||||
name1 => prop.clone(),
|
||||
name2 => prop.clone()
|
||||
};
|
||||
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
|
|
Loading…
Reference in New Issue