use crate::builder::id::ShapeName;
use crate::builder::values::ObjectBuilder;
use crate::error::{Error, ErrorKind};
use crate::model::shapes::TraitValue;
use crate::model::values::{Number, Value, ValueMap};
use crate::model::ShapeID;
use crate::prelude::{
PRELUDE_NAMESPACE, TRAIT_BOX, TRAIT_DEPRECATED, TRAIT_DOCUMENTATION, TRAIT_ERROR,
TRAIT_EXTERNALDOCUMENTATION, TRAIT_IDEMPOTENT, TRAIT_LENGTH, TRAIT_NOREPLACE, TRAIT_PAGINATED,
TRAIT_PATTERN, TRAIT_PRIVATE, TRAIT_RANGE, TRAIT_READONLY, TRAIT_REFERENCES, TRAIT_REQUIRED,
TRAIT_REQUIRESLENGTH, TRAIT_SENSITIVE, TRAIT_SINCE, TRAIT_STREAMING, TRAIT_TAGS, TRAIT_TITLE,
TRAIT_TRAIT, TRAIT_UNIQUEITEMS, TRAIT_UNSTABLE,
};
use crate::syntax::SHAPE_ID_ABSOLUTE_SEPARATOR;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Clone, Debug, PartialEq)]
pub enum ErrorSource {
Client,
Server,
}
#[derive(Clone, Debug)]
pub struct TraitBuilder {
pub(crate) shape_id: ShapeName,
pub(crate) value: TraitValue,
}
pub fn boxed() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_BOX))
}
pub fn deprecated(message: Option<&str>, since: Option<&str>) -> TraitBuilder {
let mut values = ObjectBuilder::default();
if let Some(message) = message {
let _ = values.string("message", message);
}
if let Some(since) = since {
let _ = values.string("since", since);
}
TraitBuilder::with_value(&prelude_name(TRAIT_DEPRECATED), values.into())
}
pub fn documentation(value: &str) -> TraitBuilder {
TraitBuilder::with_value(&prelude_name(TRAIT_DOCUMENTATION), value.into())
}
pub fn error_source(src: ErrorSource) -> TraitBuilder {
TraitBuilder::with_value(&prelude_name(TRAIT_ERROR), src.to_string().into())
}
pub fn external_documentation(map: &[(&str, &str)]) -> TraitBuilder {
let value: ValueMap = map
.iter()
.map(|(k, v)| (k.to_string(), v.to_string().into()))
.collect();
TraitBuilder::with_value(&prelude_name(TRAIT_EXTERNALDOCUMENTATION), value.into())
}
pub fn idempotent() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_IDEMPOTENT))
}
pub fn length(min: Option<usize>, max: Option<usize>) -> TraitBuilder {
assert!(min.is_some() || max.is_some());
let mut values = ObjectBuilder::default();
if let Some(min) = min {
let _ = values.integer("min", min as i64);
}
if let Some(max) = max {
let _ = values.integer("max", max as i64);
}
TraitBuilder::with_value(&prelude_name(TRAIT_LENGTH), values.into())
}
pub fn length_max(value: usize) -> TraitBuilder {
length(None, Some(value))
}
pub fn length_min(value: usize) -> TraitBuilder {
length(Some(value), None)
}
pub fn no_replace() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_NOREPLACE))
}
pub fn paginated(
input_token: Option<&str>,
output_token: Option<&str>,
items: Option<&str>,
page_size: Option<&str>,
) -> TraitBuilder {
let mut values = ObjectBuilder::default();
if let Some(input_token) = input_token {
let _ = values.string("inputToken", input_token);
}
if let Some(output_token) = output_token {
let _ = values.string("outputToken", output_token);
}
if let Some(items) = items {
let _ = values.string("items", items);
}
if let Some(page_size) = page_size {
let _ = values.string("pageSize", page_size);
}
TraitBuilder::with_value(&prelude_name(TRAIT_PAGINATED), values.into())
}
pub fn pattern(pat: &str) -> TraitBuilder {
assert!(!pat.is_empty());
TraitBuilder::with_value(&prelude_name(TRAIT_PATTERN), pat.into())
}
pub fn private() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_PRIVATE))
}
pub fn range(min: Option<usize>, max: Option<usize>) -> TraitBuilder {
assert!(min.is_some() || max.is_some());
let mut values = ObjectBuilder::default();
if let Some(min) = min {
let _ = values.integer("min", min as i64);
}
if let Some(max) = max {
let _ = values.integer("max", max as i64);
}
TraitBuilder::with_value(&prelude_name(TRAIT_RANGE), values.into())
}
pub fn range_max(value: usize) -> TraitBuilder {
range(None, Some(value))
}
pub fn range_min(value: usize) -> TraitBuilder {
range(Some(value), None)
}
pub fn readonly() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_READONLY))
}
pub fn references(reference_list: Value) -> TraitBuilder {
match reference_list {
Value::Array(_) => {
TraitBuilder::with_value(&prelude_name(TRAIT_REFERENCES), reference_list)
}
_ => panic!(
"{}",
ErrorKind::InvalidTraitValue(TRAIT_REFERENCES.to_string())
),
}
}
pub fn required() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_REQUIRED))
}
pub fn requires_length() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_REQUIRESLENGTH))
}
pub fn sensitive() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_SENSITIVE))
}
pub fn streaming() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_STREAMING))
}
pub fn since(date: &str) -> TraitBuilder {
assert!(!date.is_empty());
TraitBuilder::with_value(&prelude_name(TRAIT_SINCE), date.into())
}
pub fn tagged(tags: &[&str]) -> TraitBuilder {
TraitBuilder::with_value(
&prelude_name(TRAIT_TAGS),
Value::Array(tags.iter().map(|s| (*s).into()).collect()),
)
}
pub fn title(title: &str) -> TraitBuilder {
TraitBuilder::with_value(&prelude_name(TRAIT_TITLE), title.into())
}
pub fn a_trait() -> TraitBuilder {
TraitBuilder::new(&prelude_name(TRAIT_TRAIT))
}
pub fn unique_items() -> TraitBuilder {
TraitBuilder::annotation(&prelude_name(TRAIT_UNIQUEITEMS))
}
pub fn unstable() -> TraitBuilder {
TraitBuilder::new(&prelude_name(TRAIT_UNSTABLE))
}
impl Display for ErrorSource {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ErrorSource::Client => "client",
ErrorSource::Server => "server",
}
)
}
}
impl FromStr for ErrorSource {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"client" => Ok(Self::Client),
"server" => Ok(Self::Server),
_ => Err(ErrorKind::InvalidTraitValue(TRAIT_ERROR.to_string()).into()),
}
}
}
impl TraitBuilder {
pub fn new(id: &str) -> Self {
Self {
shape_id: ShapeName::from_str(id).unwrap(),
value: None,
}
}
pub fn annotation(id: &str) -> Self {
Self {
shape_id: ShapeName::from_str(id).unwrap(),
value: Some(Value::Object(ValueMap::new())),
}
}
pub fn with_value(id: &str, value: Value) -> Self {
Self {
shape_id: ShapeName::from_str(id).unwrap(),
value: Some(value),
}
}
pub fn array(&mut self, value: Vec<Value>) -> &mut Self {
self.value(value.into())
}
pub fn object(&mut self, value: ValueMap) -> &mut Self {
self.value(value.into())
}
pub fn number(&mut self, value: Number) -> &mut Self {
self.value(value.into())
}
pub fn integer(&mut self, value: i64) -> &mut Self {
self.value(value.into())
}
pub fn float(&mut self, value: f64) -> &mut Self {
self.value(value.into())
}
pub fn boolean(&mut self, value: bool) -> &mut Self {
self.value(value.into())
}
pub fn reference(&mut self, value: ShapeID) -> &mut Self {
self.value(value.into())
}
pub fn string(&mut self, value: &str) -> &mut Self {
self.value(value.to_string().into())
}
pub fn value(&mut self, value: Value) -> &mut Self {
self.value = Some(value);
self
}
}
#[inline]
fn prelude_name(name: &str) -> String {
format!(
"{}{}{}",
PRELUDE_NAMESPACE, SHAPE_ID_ABSOLUTE_SEPARATOR, name
)
}