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 default: Option<&'static str>,
|
||||||
pub min_length: Option<usize>,
|
pub min_length: Option<usize>,
|
||||||
pub max_length: Option<usize>,
|
pub max_length: Option<usize>,
|
||||||
pub format: ApiStringFormat,
|
pub format: Option<Arc<ApiStringFormat>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -123,15 +123,33 @@ pub const DEFAULTSTRING: StringSchema = StringSchema {
|
||||||
default: None,
|
default: None,
|
||||||
min_length: None,
|
min_length: None,
|
||||||
max_length: None,
|
max_length: None,
|
||||||
format: ApiStringFormat::None,
|
format: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ApiStringFormat {
|
pub enum ApiStringFormat {
|
||||||
None,
|
|
||||||
Enum(Vec<String>),
|
Enum(Vec<String>),
|
||||||
Pattern(Box<Regex>),
|
Pattern(Box<Regex>),
|
||||||
Complex(Arc<Schema>),
|
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]
|
#[macro_export]
|
||||||
|
@ -219,20 +237,24 @@ fn parse_simple_value(value_str: &str, schema: &Schema) -> Result<Value, Error>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match string_schema.format {
|
if let Some(ref format) = string_schema.format {
|
||||||
ApiStringFormat::None => { /* do nothing */ }
|
match format.as_ref() {
|
||||||
ApiStringFormat::Pattern(ref regex) => {
|
ApiStringFormat::Pattern(ref regex) => {
|
||||||
if !regex.is_match(&res) {
|
if !regex.is_match(&res) {
|
||||||
bail!("value does not match the regex pattern");
|
bail!("value does not match the regex pattern");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
ApiStringFormat::Enum(ref stringvec) => {
|
||||||
ApiStringFormat::Enum(ref stringvec) => {
|
if stringvec.iter().find(|&e| *e == res) == None {
|
||||||
if stringvec.iter().find(|&e| *e == res) == None {
|
bail!("value is not defined in the enumeration.");
|
||||||
bail!("value is not defined in the enumeration.");
|
}
|
||||||
|
}
|
||||||
|
ApiStringFormat::Complex(ref _subschema) => {
|
||||||
|
bail!("implement me!");
|
||||||
|
}
|
||||||
|
ApiStringFormat::VerifyFn(verify_fn) => {
|
||||||
|
verify_fn(&res)?;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ApiStringFormat::Complex(ref _subschema) => {
|
|
||||||
bail!("implement me!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +412,7 @@ fn test_query_string() {
|
||||||
|
|
||||||
let schema = parameter!{name => Arc::new(ApiString!{
|
let schema = parameter!{name => Arc::new(ApiString!{
|
||||||
optional => false,
|
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);
|
let res = parse_query_string("name=abcd", &schema, true);
|
||||||
|
@ -401,7 +423,7 @@ fn test_query_string() {
|
||||||
|
|
||||||
let schema = parameter!{name => Arc::new(ApiString!{
|
let schema = parameter!{name => Arc::new(ApiString!{
|
||||||
optional => false,
|
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);
|
let res = parse_query_string("name=ateststring", &schema, true);
|
||||||
|
@ -414,7 +436,7 @@ fn test_query_string() {
|
||||||
|
|
||||||
let schema = parameter!{name => Arc::new(ApiString!{
|
let schema = parameter!{name => Arc::new(ApiString!{
|
||||||
optional => false,
|
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);
|
let res = parse_query_string("name=noenum", &schema, true);
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub mod api {
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod registry;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod router;
|
pub mod router;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
|
|
@ -20,8 +20,10 @@ use hyper;
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Proxmox REST Server example.");
|
println!("Proxmox REST Server example.");
|
||||||
|
|
||||||
|
let prop = Arc::new(ApiString!{ optional => true });
|
||||||
let schema = parameter!{
|
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();
|
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||||
|
|
Loading…
Reference in New Issue