diff --git a/.vscode/settings.json b/.vscode/settings.json index 1d316d3..3ce711c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,6 +21,5 @@ "structs", "valtree", "valtrees" - ], - "rust-analyzer.diagnostics.disabled": ["unresolved-import"] + ] } diff --git a/src/conformance/test.rs b/src/conformance/test.rs index 947ad0c..7f50772 100644 --- a/src/conformance/test.rs +++ b/src/conformance/test.rs @@ -112,14 +112,11 @@ impl ConformanceTestSpec { .ok_or(ValidationError::ParameterNotFound(param.name.clone()))?; // validate position - let pos = match parameter.location.as_ref() { - "query" => ParamPosition::Query, - "header" => ParamPosition::Header, - "path" => ParamPosition::Path, - "cookie" => ParamPosition::Cookie, - pos_str => Err(ValidationError::InvalidParameterLocation( - pos_str.to_owned(), - ))?, + let pos = match parameter.location { + crate::spec::ParamLoc::Path => ParamPosition::Path, + crate::spec::ParamLoc::Query => ParamPosition::Query, + crate::spec::ParamLoc::Header => ParamPosition::Header, + crate::spec::ParamLoc::Cookie => ParamPosition::Cookie, }; // TODO: validate type diff --git a/src/spec/parameter.rs b/src/spec/parameter.rs index bd61cba..f1b5596 100644 --- a/src/spec/parameter.rs +++ b/src/spec/parameter.rs @@ -2,6 +2,17 @@ use serde::{Deserialize, Serialize}; use super::{FromRef, Ref, RefError, RefType, Spec}; use crate::Schema; +use serde::ser::SerializeStruct; + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)] +#[serde(rename_all = "camelCase")] +pub enum ParamLoc { + #[default] + Query, + Header, + Path, + Cookie, +} // FIXME: Verify against OpenAPI 3.0.1 /// Describes a single operation parameter. @@ -10,59 +21,43 @@ use crate::Schema; /// and [location](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#parameterIn). /// /// See . -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Default)] pub struct Parameter { - /// The name of the parameter. pub name: String, - // TODO: enumify - /// The location of the parameter. - /// Possible values are "query", "header", "path" or "cookie". #[serde(rename = "in")] - pub location: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub required: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub schema: Option, + pub location: ParamLoc, + /// A brief description of the parameter. This could contain examples + /// of use. GitHub Flavored Markdown is allowed. #[serde(skip_serializing_if = "Option::is_none")] - #[serde(rename = "uniqueItems")] - pub unique_items: Option, + pub description: Option, - /// string, number, boolean, integer, array, file ( only for formData ) #[serde(skip_serializing_if = "Option::is_none")] - #[serde(rename = "type")] - pub param_type: Option, + pub required: Option, + /// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage. + /// Default value is `false`. #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, + #[serde(default)] + pub deprecated: Option, - /// A brief description of the parameter. This could contain examples - /// of use. GitHub Flavored Markdown is allowed. #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, + style: Option, - // collectionFormat: ??? - // default: ??? - // maximum ? - // exclusiveMaximum ?? - // minimum ?? - // exclusiveMinimum ?? - // maxLength ?? - // minLength ?? - // pattern ?? - // maxItems ?? - // minItems ?? - // enum ?? - // multipleOf ?? - // allowEmptyValue ( for query / body params ) - /// Describes how the parameter value will be serialized depending on the type of the parameter - /// value. Default values (based on value of in): for `query` - `form`; for `path` - `simple`; for - /// `header` - `simple`; for cookie - `form`. #[serde(skip_serializing_if = "Option::is_none")] - style: Option, + pub schema: Option, + // #[serde(skip_serializing_if = "Option::is_none")] + // #[serde(rename = "uniqueItems")] + // pub unique_items: Option, + // + // /// string, number, boolean, integer, array, file ( only for formData ) + // #[serde(skip_serializing_if = "Option::is_none")] + // #[serde(rename = "type")] + // pub param_type: Option, + // + // #[serde(skip_serializing_if = "Option::is_none")] + // pub format: Option, } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] @@ -70,6 +65,11 @@ pub struct Parameter { enum ParameterStyle { Form, Simple, + Matrix, + Label, + SpaceDelimited, + PipeDelimited, + DeepObject, } impl FromRef for Parameter { @@ -91,3 +91,34 @@ impl FromRef for Parameter { } } } + +impl Serialize for Parameter { + fn serialize(&self, serializer: S) -> Result { + let mut s = serializer.serialize_struct("Parameter", 10)?; + + s.serialize_field("name", &self.name)?; + s.serialize_field("in", &self.location)?; + s.serialize_field("description", &self.description)?; + s.serialize_field("required", &self.required)?; + s.serialize_field("deprecated", &self.deprecated)?; + s.serialize_field("schema", &self.schema)?; + //s.serialize_field("uniqueItems", &self.unique_items)?; + //s.serialize_field("type", &self.param_type)?; + //s.serialize_field("format", &self.format)?; + + if let Some(style) = &self.style { + s.serialize_field("style", style)?; + } else { + match self.location { + ParamLoc::Query | ParamLoc::Cookie => { + s.serialize_field("style", &ParameterStyle::Form)?; + } + ParamLoc::Path | ParamLoc::Header => { + s.serialize_field("style", &ParameterStyle::Simple)?; + } + } + } + + s.end() + } +} diff --git a/src/spec/schema.rs b/src/spec/schema.rs index ea5cda0..d8e6c3d 100644 --- a/src/spec/schema.rs +++ b/src/spec/schema.rs @@ -1,4 +1,4 @@ -//! Schema specification for [OpenAPI 3.0.1](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md) +//! Schema specification for [OpenAPI 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md) use std::collections::BTreeMap; @@ -29,9 +29,31 @@ pub enum Type { String, Array, Object, + Null, } -// FIXME: Verify against OpenAPI 3.0 +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Encoding { + Base16, + Hex, + Base32, + Base32Hex, + Base64, + Base64Url, + + #[serde(rename = "quoted-printable")] + QuotedPrintable, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum SchemaOrBool { + Schema(Schema), + Bool(bool), +} + +// FIXME: Verify against OpenAPI 3.1 /// The Schema Object allows the definition of input and output data types. /// These types can be objects, but also primitives and arrays. /// This object is an extended subset of the @@ -60,9 +82,6 @@ pub struct Schema { #[serde(skip_serializing_if = "Option::is_none")] pub schema_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub nullable: Option, - // // structure // @@ -77,14 +96,17 @@ pub struct Schema { #[serde(skip_serializing_if = "BTreeMap::is_empty")] pub properties: BTreeMap>, - /// Value can be boolean or object. Inline or referenced schema MUST be of a - /// [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/HEAD/versions/3.1.0.md#schemaObject) - /// and not a standard JSON Schema. - /// - /// See . #[serde(rename = "additionalProperties")] #[serde(skip_serializing_if = "Option::is_none")] - pub additional_properties: Option>>, + pub additional_properties: Option>>, + + #[serde(rename = "contentEncoding")] + #[serde(skip_serializing_if = "Option::is_none")] + pub content_encoding: Option, + + #[serde(rename = "contentMediaType")] + #[serde(skip_serializing_if = "Option::is_none")] + pub content_media_type: Option, // // additional metadata @@ -92,8 +114,9 @@ pub struct Schema { #[serde(skip_serializing_if = "Option::is_none")] pub default: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub example: Option, + #[serde(default)] + #[serde(skip_serializing_if = "Vec::is_empty")] + pub examples: Vec, // // validation requirements @@ -118,14 +141,14 @@ pub struct Schema { #[serde(rename = "exclusiveMaximum")] #[serde(skip_serializing_if = "Option::is_none")] - pub exclusive_maximum: Option, + pub exclusive_maximum: Option, #[serde(skip_serializing_if = "Option::is_none")] pub maximum: Option, #[serde(rename = "exclusiveMinimum")] #[serde(skip_serializing_if = "Option::is_none")] - pub exclusive_minimum: Option, + pub exclusive_minimum: Option, #[serde(rename = "minLength")] #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/validation/validator.rs b/src/validation/validator.rs index 0bd1525..0fd3dce 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -39,8 +39,8 @@ impl ValidationTree { if let Some(type_) = schema.schema_type { trace!("restricting data type: {:?}", type_); - let type_val = if let Some(nullable) = schema.nullable { - DataType::new(type_).set_nullable(nullable) + let type_val = if type_ == SchemaType::Null { + DataType::new(type_).set_nullable(true) } else { DataType::new(type_) }; diff --git a/watch.sh b/watch.sh deleted file mode 100755 index 4b26ab4..0000000 --- a/watch.sh +++ /dev/null @@ -1,4 +0,0 @@ -RUST_BACKTRACE=1 \ -RUST_LOG=warn,oas=trace,conformance=trace \ -cargo watch \ --x "$@"