use std::borrow::Cow;
use serde_json::Value;
use snafu::Snafu;
use vector_config_common::{
attributes::CustomAttribute,
constants::{self, ComponentType},
schema::SchemaObject,
};
use super::query::{OneOrMany, QueryError, QueryableSchema, SchemaType, SimpleSchema};
#[derive(Debug, Snafu)]
pub enum SchemaError {
#[snafu(display("invalid component schema: {pointer}: {reason}"))]
InvalidComponentSchema {
pointer: &'static str,
reason: Cow<'static, str>,
},
}
impl SchemaError {
pub fn invalid_component_schema<S: Into<Cow<'static, str>>>(
pointer: &'static str,
reason: S,
) -> Self {
Self::InvalidComponentSchema {
pointer,
reason: reason.into(),
}
}
}
pub struct ComponentSchema<'a> {
schema: &'a SchemaObject,
component_name: String,
component_type: ComponentType,
}
impl<'a> ComponentSchema<'a> {
pub fn component_type(&self) -> ComponentType {
self.component_type
}
pub fn component_name(&self) -> &str {
&self.component_name
}
}
impl<'a> QueryableSchema for ComponentSchema<'a> {
fn schema_type(&self) -> SchemaType {
self.schema.schema_type()
}
fn description(&self) -> Option<&str> {
self.schema.description()
}
fn title(&self) -> Option<&str> {
self.schema.title()
}
fn get_attributes(&self, key: &str) -> Option<OneOrMany<CustomAttribute>> {
self.schema.get_attributes(key)
}
fn get_attribute(&self, key: &str) -> Result<Option<CustomAttribute>, QueryError> {
self.schema.get_attribute(key)
}
fn has_flag_attribute(&self, key: &str) -> Result<bool, QueryError> {
self.schema.has_flag_attribute(key)
}
}
impl<'a> TryFrom<SimpleSchema<'a>> for ComponentSchema<'a> {
type Error = SchemaError;
fn try_from(value: SimpleSchema<'a>) -> Result<Self, Self::Error> {
let component_type =
get_component_metadata_kv_str(&value, constants::DOCS_META_COMPONENT_TYPE).and_then(
|s| {
ComponentType::try_from(s.as_str()).map_err(|_| {
SchemaError::invalid_component_schema(
constants::DOCS_META_COMPONENT_TYPE,
"value was not a valid component type",
)
})
},
)?;
let component_name =
get_component_metadata_kv_str(&value, constants::DOCS_META_COMPONENT_NAME)?;
Ok(Self {
schema: value.into_inner(),
component_name,
component_type,
})
}
}
fn get_component_metadata_kv_str<'a>(
schema: &'a SimpleSchema<'a>,
key: &'static str,
) -> Result<String, SchemaError> {
schema
.get_attribute(key)
.map_err(|e| SchemaError::invalid_component_schema(key, e.to_string()))?
.ok_or_else(|| SchemaError::invalid_component_schema(key, "attribute must be present"))
.and_then(|attr| match attr {
CustomAttribute::Flag(_) => Err(SchemaError::invalid_component_schema(
key,
"expected key/value attribute, got flag instead",
)),
CustomAttribute::KeyValue { value, .. } => Ok(value),
})
.and_then(|v| match v {
Value::String(name) => Ok(name),
_ => Err(SchemaError::invalid_component_schema(
key,
format!("`{}` must be a string", key),
)),
})
}