more general dereferencing
This commit is contained in:
parent
2b1e6a8aae
commit
311e17e3ba
|
@ -46,7 +46,12 @@ fn run_rustfmt_on(path: &OsStr) {
|
|||
}
|
||||
|
||||
fn schema_ref_type_name(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Result<String> {
|
||||
let (name, schema) = deref_definition(spec, &schema)?;
|
||||
let name = if let MaybeRef::Ref { _ref } = schema {
|
||||
_ref.rsplit_once("/").map(|(_, b)| b)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let schema = schema.deref(spec)?;
|
||||
schema_type_name(spec, name, schema)
|
||||
}
|
||||
|
||||
|
@ -105,29 +110,8 @@ fn schema_type_name(
|
|||
}
|
||||
}
|
||||
|
||||
fn deref_definition<'a>(
|
||||
spec: &'a OpenApiV2,
|
||||
r: &'a MaybeRef<Schema>,
|
||||
) -> eyre::Result<(Option<&'a str>, &'a Schema)> {
|
||||
let r = match r {
|
||||
MaybeRef::Value { value } => return Ok((None, value)),
|
||||
MaybeRef::Ref { _ref } => _ref,
|
||||
};
|
||||
let name = r
|
||||
.strip_prefix("#/definitions/")
|
||||
.ok_or_else(|| eyre::eyre!("invalid definition reference"))?;
|
||||
let global_definitions = spec
|
||||
.definitions
|
||||
.as_ref()
|
||||
.ok_or_else(|| eyre::eyre!("no global definitions"))?;
|
||||
let definition = global_definitions
|
||||
.get(name)
|
||||
.ok_or_else(|| eyre::eyre!("referenced definition does not exist"))?;
|
||||
Ok((Some(name), definition))
|
||||
}
|
||||
|
||||
fn schema_is_string(spec: &OpenApiV2, schema: &MaybeRef<Schema>) -> eyre::Result<bool> {
|
||||
let (_, schema) = deref_definition(spec, schema)?;
|
||||
let schema = schema.deref(spec)?;
|
||||
let is_str = match schema._type {
|
||||
Some(SchemaType::One(Primitive::String)) => true,
|
||||
_ => false,
|
||||
|
|
|
@ -142,10 +142,7 @@ fn fn_args_from_op(spec: &OpenApiV2, op: &Operation) -> eyre::Result<String> {
|
|||
// let mut has_headers = false;
|
||||
if let Some(params) = &op.parameters {
|
||||
for param in params {
|
||||
let full_param = match ¶m {
|
||||
MaybeRef::Value { value } => value,
|
||||
MaybeRef::Ref { _ref } => eyre::bail!("todo: add deref parameters"),
|
||||
};
|
||||
let full_param = param.deref(spec)?;
|
||||
match &full_param._in {
|
||||
ParameterIn::Path { param } => {
|
||||
let type_name = param_type(¶m, false)?;
|
||||
|
@ -270,7 +267,7 @@ fn response_ref_type_name(
|
|||
spec: &OpenApiV2,
|
||||
schema: &MaybeRef<Response>,
|
||||
) -> eyre::Result<ResponseType> {
|
||||
let (_, response) = deref_response(spec, schema)?;
|
||||
let response = schema.deref(spec)?;
|
||||
let mut ty = ResponseType::default();
|
||||
if response.headers.is_some() {
|
||||
ty.headers = Some("reqwest::header::HeaderMap".into());
|
||||
|
@ -281,27 +278,6 @@ fn response_ref_type_name(
|
|||
Ok(ty)
|
||||
}
|
||||
|
||||
fn deref_response<'a>(
|
||||
spec: &'a OpenApiV2,
|
||||
r: &'a MaybeRef<Response>,
|
||||
) -> eyre::Result<(Option<&'a str>, &'a Response)> {
|
||||
let r = match r {
|
||||
MaybeRef::Value { value } => return Ok((None, value)),
|
||||
MaybeRef::Ref { _ref } => _ref,
|
||||
};
|
||||
let name = r
|
||||
.strip_prefix("#/responses/")
|
||||
.ok_or_else(|| eyre::eyre!("invalid response reference"))?;
|
||||
let global_responses = spec
|
||||
.responses
|
||||
.as_ref()
|
||||
.ok_or_else(|| eyre::eyre!("no global responses"))?;
|
||||
let response = global_responses
|
||||
.get(name)
|
||||
.ok_or_else(|| eyre::eyre!("referenced response does not exist"))?;
|
||||
Ok((Some(name), response))
|
||||
}
|
||||
|
||||
fn create_method_body(
|
||||
spec: &OpenApiV2,
|
||||
method: &str,
|
||||
|
@ -431,7 +407,7 @@ fn create_method_response(spec: &OpenApiV2, op: &Operation) -> eyre::Result<Stri
|
|||
out.push_str("let response = self.execute(request).await?;\n");
|
||||
out.push_str("match response.status().as_u16() {\n");
|
||||
for (code, res) in &op.responses.http_codes {
|
||||
let (_, res) = deref_response(spec, res)?;
|
||||
let res = res.deref(spec)?;
|
||||
if !code.starts_with("2") {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,132 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||
use eyre::WrapErr;
|
||||
use url::Url;
|
||||
|
||||
trait JsonDeref: std::any::Any {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any>;
|
||||
}
|
||||
|
||||
impl JsonDeref for bool {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for u64 {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for f64 {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for String {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for Url {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonDeref> JsonDeref for Option<T> {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
match self {
|
||||
Some(x) => x.deref_any(path),
|
||||
None => Err(eyre::eyre!("not found")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonDeref> JsonDeref for Box<T> {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
T::deref_any(&**self, path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonDeref> JsonDeref for Vec<T> {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
let idx = head.parse::<usize>().wrap_err("not found")?;
|
||||
let value = self.get(idx).ok_or_else(|| eyre::eyre!("not found"))?;
|
||||
value.deref_any(tail)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: JsonDeref> JsonDeref for BTreeMap<String, T> {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
let value = self.get(head).ok_or_else(|| eyre::eyre!("not found"))?;
|
||||
value.deref_any(tail)
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for serde_json::Value {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
match self {
|
||||
serde_json::Value::Null => eyre::bail!("not found"),
|
||||
serde_json::Value::Bool(b) => b.deref_any(path),
|
||||
serde_json::Value::Number(x) => x.deref_any(path),
|
||||
serde_json::Value::String(s) => s.deref_any(path),
|
||||
serde_json::Value::Array(list) => list.deref_any(path),
|
||||
serde_json::Value::Object(map) => map.deref_any(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for serde_json::Map<String, serde_json::Value> {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
let value = self.get(head).ok_or_else(|| eyre::eyre!("not found"))?;
|
||||
value.deref_any(tail)
|
||||
}
|
||||
}
|
||||
|
||||
impl JsonDeref for serde_json::Number {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
eyre::bail!("not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct OpenApiV2 {
|
||||
|
@ -23,6 +149,36 @@ pub struct OpenApiV2 {
|
|||
pub external_docs: Option<ExternalDocs>,
|
||||
}
|
||||
|
||||
impl JsonDeref for OpenApiV2 {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
let path = path
|
||||
.strip_prefix("#/")
|
||||
.ok_or_else(|| eyre::eyre!("invalid ref prefix"))?;
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"swagger" => self.swagger.deref_any(tail),
|
||||
"info" => self.info.deref_any(tail),
|
||||
"host" => self.host.deref_any(tail),
|
||||
"base_path" => self.base_path.deref_any(tail),
|
||||
"schemes" => self.schemes.deref_any(tail),
|
||||
"consumes" => self.consumes.deref_any(tail),
|
||||
"produces" => self.produces.deref_any(tail),
|
||||
"paths" => self.paths.deref_any(tail),
|
||||
"definitions" => self.definitions.deref_any(tail),
|
||||
"parameters" => self.parameters.deref_any(tail),
|
||||
"responses" => self.responses.deref_any(tail),
|
||||
"security_definitions" => self.security_definitions.deref_any(tail),
|
||||
"security" => self.security.deref_any(tail),
|
||||
"tags" => self.tags.deref_any(tail),
|
||||
"external_docs" => self.external_docs.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenApiV2 {
|
||||
pub fn validate(&self) -> eyre::Result<()> {
|
||||
eyre::ensure!(self.swagger == "2.0", "swagger version must be 2.0");
|
||||
|
@ -77,6 +233,13 @@ impl OpenApiV2 {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn deref<T: std::any::Any>(&self, path: &str) -> eyre::Result<&T> {
|
||||
self.deref_any(path).and_then(|a| {
|
||||
a.downcast_ref::<T>()
|
||||
.ok_or_else(|| eyre::eyre!("incorrect type found at reference"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
|
@ -90,6 +253,24 @@ pub struct SpecInfo {
|
|||
pub version: String,
|
||||
}
|
||||
|
||||
impl JsonDeref for SpecInfo {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"title" => self.title.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
"terms_of_service" => self.terms_of_service.deref_any(tail),
|
||||
"contact" => self.contact.deref_any(tail),
|
||||
"license" => self.license.deref_any(tail),
|
||||
"version" => self.version.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct Contact {
|
||||
|
@ -98,6 +279,21 @@ pub struct Contact {
|
|||
pub email: Option<String>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Contact {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"name" => self.name.deref_any(tail),
|
||||
"url" => self.url.deref_any(tail),
|
||||
"email" => self.email.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct License {
|
||||
|
@ -105,6 +301,20 @@ pub struct License {
|
|||
pub url: Option<Url>,
|
||||
}
|
||||
|
||||
impl JsonDeref for License {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"name" => self.name.deref_any(tail),
|
||||
"url" => self.url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct PathItem {
|
||||
|
@ -120,6 +330,27 @@ pub struct PathItem {
|
|||
pub parameters: Option<Vec<MaybeRef<Parameter>>>,
|
||||
}
|
||||
|
||||
impl JsonDeref for PathItem {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"$ref" => self._ref.deref_any(tail),
|
||||
"get" => self.get.deref_any(tail),
|
||||
"put" => self.put.deref_any(tail),
|
||||
"post" => self.post.deref_any(tail),
|
||||
"delete" => self.delete.deref_any(tail),
|
||||
"options" => self.options.deref_any(tail),
|
||||
"head" => self.head.deref_any(tail),
|
||||
"patch" => self.patch.deref_any(tail),
|
||||
"parameters" => self.parameters.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PathItem {
|
||||
fn validate<'a>(&'a self, ids: &mut BTreeSet<&'a str>) -> eyre::Result<()> {
|
||||
if let Some(op) = &self.get {
|
||||
|
@ -171,6 +402,30 @@ pub struct Operation {
|
|||
pub security: Option<Vec<BTreeMap<String, Vec<String>>>>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Operation {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"tags" => self.tags.deref_any(tail),
|
||||
"summary" => self.summary.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
"external_docs" => self.external_docs.deref_any(tail),
|
||||
"operation_id" => self.operation_id.deref_any(tail),
|
||||
"consumes" => self.consumes.deref_any(tail),
|
||||
"produces" => self.produces.deref_any(tail),
|
||||
"parameters" => self.parameters.deref_any(tail),
|
||||
"responses" => self.responses.deref_any(tail),
|
||||
"schemes" => self.schemes.deref_any(tail),
|
||||
"deprecated" => self.deprecated.deref_any(tail),
|
||||
"security" => self.security.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
fn validate<'a>(&'a self, ids: &mut BTreeSet<&'a str>) -> eyre::Result<()> {
|
||||
if let Some(operation_id) = self.operation_id.as_deref() {
|
||||
|
@ -204,6 +459,20 @@ pub struct ExternalDocs {
|
|||
pub url: Url,
|
||||
}
|
||||
|
||||
impl JsonDeref for ExternalDocs {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"description" => self.description.deref_any(tail),
|
||||
"url" => self.url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct Parameter {
|
||||
|
@ -213,6 +482,33 @@ pub struct Parameter {
|
|||
pub _in: ParameterIn,
|
||||
}
|
||||
|
||||
impl JsonDeref for Parameter {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"name" => self.name.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
"in" => {
|
||||
if tail.is_empty() {
|
||||
Ok(match &self._in {
|
||||
ParameterIn::Body { schema: _ } => &"body" as _,
|
||||
ParameterIn::Path { param: _ } => &"path" as _,
|
||||
ParameterIn::Query { param: _ } => &"query" as _,
|
||||
ParameterIn::Header { param: _ } => &"header" as _,
|
||||
ParameterIn::FormData { param: _ } => &"formData" as _,
|
||||
})
|
||||
} else {
|
||||
eyre::bail!("not found")
|
||||
}
|
||||
}
|
||||
_ => self._in.deref_any(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parameter {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
self._in.validate()
|
||||
|
@ -244,6 +540,22 @@ pub enum ParameterIn {
|
|||
},
|
||||
}
|
||||
|
||||
impl JsonDeref for ParameterIn {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match self {
|
||||
ParameterIn::Body { schema } => match head {
|
||||
"schema" => schema.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
ParameterIn::Path { param }
|
||||
| ParameterIn::Query { param }
|
||||
| ParameterIn::Header { param }
|
||||
| ParameterIn::FormData { param } => param.deref_any(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterIn {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
match self {
|
||||
|
@ -292,6 +604,37 @@ pub struct NonBodyParameter {
|
|||
pub multiple_of: Option<u64>,
|
||||
}
|
||||
|
||||
impl JsonDeref for NonBodyParameter {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"required" => self.required.deref_any(tail),
|
||||
"type" => self._type.deref_any(tail),
|
||||
"format" => self.format.deref_any(tail),
|
||||
"allow_empty_value" => self.allow_empty_value.deref_any(tail),
|
||||
"items" => self.items.deref_any(tail),
|
||||
"collection_format" => self.collection_format.deref_any(tail),
|
||||
"default" => self.default.deref_any(tail),
|
||||
"maximum" => self.maximum.deref_any(tail),
|
||||
"exclusive_maximum" => self.exclusive_maximum.deref_any(tail),
|
||||
"minimum" => self.minimum.deref_any(tail),
|
||||
"exclusive_minimum" => self.exclusive_minimum.deref_any(tail),
|
||||
"max_length" => self.max_length.deref_any(tail),
|
||||
"min_length" => self.min_length.deref_any(tail),
|
||||
"pattern" => self.pattern.deref_any(tail), // should be regex
|
||||
"max_items" => self.max_items.deref_any(tail),
|
||||
"min_items" => self.min_items.deref_any(tail),
|
||||
"unique_items" => self.unique_items.deref_any(tail),
|
||||
"enum" => self._enum.deref_any(tail),
|
||||
"multiple_of" => self.multiple_of.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NonBodyParameter {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if self._type == ParameterType::Array {
|
||||
|
@ -347,6 +690,16 @@ pub enum ParameterType {
|
|||
File,
|
||||
}
|
||||
|
||||
impl JsonDeref for ParameterType {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParameterType {
|
||||
fn matches_value(&self, value: &serde_json::Value) -> bool {
|
||||
match (self, value) {
|
||||
|
@ -370,6 +723,16 @@ pub enum CollectionFormat {
|
|||
Multi,
|
||||
}
|
||||
|
||||
impl JsonDeref for CollectionFormat {
|
||||
fn deref_any<'a>(&'a self, path: &str) -> eyre::Result<&'a dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct Items {
|
||||
|
@ -394,6 +757,35 @@ pub struct Items {
|
|||
pub multiple_of: Option<u64>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Items {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"type" => self._type.deref_any(tail),
|
||||
"format" => self.format.deref_any(tail),
|
||||
"items" => self.items.deref_any(tail),
|
||||
"collection_format" => self.collection_format.deref_any(tail),
|
||||
"default" => self.default.deref_any(tail),
|
||||
"maximum" => self.maximum.deref_any(tail),
|
||||
"exclusive_maximum" => self.exclusive_maximum.deref_any(tail),
|
||||
"minimum" => self.minimum.deref_any(tail),
|
||||
"exclusive_minimum" => self.exclusive_minimum.deref_any(tail),
|
||||
"max_length" => self.max_length.deref_any(tail),
|
||||
"min_length" => self.min_length.deref_any(tail),
|
||||
"pattern" => self.pattern.deref_any(tail), // should be regex
|
||||
"max_items" => self.max_items.deref_any(tail),
|
||||
"min_items" => self.min_items.deref_any(tail),
|
||||
"unique_items" => self.unique_items.deref_any(tail),
|
||||
"enum" => self._enum.deref_any(tail),
|
||||
"multiple_of" => self.multiple_of.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Items {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if self._type == ParameterType::Array {
|
||||
|
@ -451,6 +843,23 @@ pub struct Responses {
|
|||
pub http_codes: BTreeMap<String, MaybeRef<Response>>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Responses {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"default" => self.default.deref_any(tail),
|
||||
code => self
|
||||
.http_codes
|
||||
.get(code)
|
||||
.map(|r| r as _)
|
||||
.ok_or_else(|| eyre::eyre!("not found")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Responses {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if self.default.is_none() && self.http_codes.is_empty() {
|
||||
|
@ -482,6 +891,22 @@ pub struct Response {
|
|||
pub examples: Option<BTreeMap<String, serde_json::Value>>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Response {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"description" => self.description.deref_any(tail),
|
||||
"schema" => self.schema.deref_any(tail),
|
||||
"headers" => self.headers.deref_any(tail),
|
||||
"examples" => self.examples.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if let Some(headers) = &self.headers {
|
||||
|
@ -521,6 +946,36 @@ pub struct Header {
|
|||
pub multiple_of: Option<u64>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Header {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"description" => self.description.deref_any(tail),
|
||||
"type" => self._type.deref_any(tail),
|
||||
"format" => self.format.deref_any(tail),
|
||||
"items" => self.items.deref_any(tail),
|
||||
"collection_format" => self.collection_format.deref_any(tail),
|
||||
"default" => self.default.deref_any(tail),
|
||||
"maximum" => self.maximum.deref_any(tail),
|
||||
"exclusive_maximum" => self.exclusive_maximum.deref_any(tail),
|
||||
"minimum" => self.minimum.deref_any(tail),
|
||||
"exclusive_minimum" => self.exclusive_minimum.deref_any(tail),
|
||||
"max_length" => self.max_length.deref_any(tail),
|
||||
"min_length" => self.min_length.deref_any(tail),
|
||||
"pattern" => self.pattern.deref_any(tail), // should be regex
|
||||
"max_items" => self.max_items.deref_any(tail),
|
||||
"min_items" => self.min_items.deref_any(tail),
|
||||
"unique_items" => self.unique_items.deref_any(tail),
|
||||
"enum" => self._enum.deref_any(tail),
|
||||
"multiple_of" => self.multiple_of.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if self._type == ParameterType::Array {
|
||||
|
@ -575,6 +1030,21 @@ pub struct Tag {
|
|||
pub external_docs: Option<ExternalDocs>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Tag {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"name" => self.name.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
"external_docs" => self.external_docs.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct Schema {
|
||||
|
@ -611,6 +1081,46 @@ pub struct Schema {
|
|||
pub example: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Schema {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"format" => self.format.deref_any(tail),
|
||||
"title" => self.title.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
"default" => self.default.deref_any(tail),
|
||||
"multiple_of" => self.multiple_of.deref_any(tail),
|
||||
"maximum" => self.maximum.deref_any(tail),
|
||||
"exclusive_maximum" => self.exclusive_maximum.deref_any(tail),
|
||||
"minimum" => self.minimum.deref_any(tail),
|
||||
"exclusive_minimum" => self.exclusive_minimum.deref_any(tail),
|
||||
"max_length" => self.max_length.deref_any(tail),
|
||||
"min_length" => self.min_length.deref_any(tail),
|
||||
"pattern" => self.pattern.deref_any(tail), // should be regex
|
||||
"max_items" => self.max_items.deref_any(tail),
|
||||
"min_items" => self.min_items.deref_any(tail),
|
||||
"unique_items" => self.unique_items.deref_any(tail),
|
||||
"max_properties" => self.max_properties.deref_any(tail),
|
||||
"min_properties" => self.min_properties.deref_any(tail),
|
||||
"required" => self.required.deref_any(tail),
|
||||
"enum" => self._enum.deref_any(tail),
|
||||
"type" => self._type.deref_any(tail),
|
||||
"properties" => self.properties.deref_any(tail),
|
||||
"additional_properties" => self.additional_properties.deref_any(tail),
|
||||
"items" => self.items.deref_any(tail),
|
||||
"discriminator" => self.discriminator.deref_any(tail),
|
||||
"read_only" => self.read_only.deref_any(tail),
|
||||
"xml" => self.xml.deref_any(tail),
|
||||
"external_docs" => self.external_docs.deref_any(tail),
|
||||
"example" => self.example.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Schema {
|
||||
fn validate(&self) -> eyre::Result<()> {
|
||||
if let Some(_type) = &self._type {
|
||||
|
@ -701,6 +1211,15 @@ pub enum SchemaType {
|
|||
List(Vec<Primitive>),
|
||||
}
|
||||
|
||||
impl JsonDeref for SchemaType {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
match self {
|
||||
SchemaType::One(i) => i.deref_any(path),
|
||||
SchemaType::List(list) => list.deref_any(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub enum Primitive {
|
||||
|
@ -713,6 +1232,16 @@ pub enum Primitive {
|
|||
String,
|
||||
}
|
||||
|
||||
impl JsonDeref for Primitive {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(eyre::eyre!("not found"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
fn matches_value(&self, value: &serde_json::Value) -> bool {
|
||||
match (self, value) {
|
||||
|
@ -736,6 +1265,23 @@ pub struct Xml {
|
|||
pub wrapped: Option<bool>,
|
||||
}
|
||||
|
||||
impl JsonDeref for Xml {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"name" => self.name.deref_any(tail),
|
||||
"namespace" => self.namespace.deref_any(tail),
|
||||
"prefix" => self.prefix.deref_any(tail),
|
||||
"attribute" => self.attribute.deref_any(tail),
|
||||
"wrapped" => self.wrapped.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub struct SecurityScheme {
|
||||
|
@ -744,6 +1290,20 @@ pub struct SecurityScheme {
|
|||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
impl JsonDeref for SecurityScheme {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match head {
|
||||
"type" => self._type.deref_any(tail),
|
||||
"description" => self.description.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"), tag = "type")]
|
||||
pub enum SecurityType {
|
||||
|
@ -760,6 +1320,28 @@ pub enum SecurityType {
|
|||
},
|
||||
}
|
||||
|
||||
impl JsonDeref for SecurityType {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match self {
|
||||
SecurityType::Basic => eyre::bail!("not found: {head}"),
|
||||
SecurityType::ApiKey { name, _in } => match head {
|
||||
"name" => name.deref_any(tail),
|
||||
"in" => _in.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
SecurityType::OAuth2 { flow, scopes } => match head {
|
||||
"flow" => flow.deref_any(tail),
|
||||
"scopes" => scopes.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"))]
|
||||
pub enum KeyIn {
|
||||
|
@ -767,6 +1349,16 @@ pub enum KeyIn {
|
|||
Header,
|
||||
}
|
||||
|
||||
impl JsonDeref for KeyIn {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
Ok(self)
|
||||
} else {
|
||||
eyre::bail!("not found")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all(deserialize = "camelCase"), tag = "flow")]
|
||||
pub enum OAuth2Flow {
|
||||
|
@ -785,6 +1377,37 @@ pub enum OAuth2Flow {
|
|||
},
|
||||
}
|
||||
|
||||
impl JsonDeref for OAuth2Flow {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match self {
|
||||
OAuth2Flow::Implicit { authorization_url } => match head {
|
||||
"authorizationUrl" => authorization_url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
OAuth2Flow::Password { token_url } => match head {
|
||||
"tokenUrl" => token_url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
OAuth2Flow::Application { token_url } => match head {
|
||||
"tokenUrl" => token_url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
OAuth2Flow::AccessCode {
|
||||
authorization_url,
|
||||
token_url,
|
||||
} => match head {
|
||||
"authorizationUrl" => authorization_url.deref_any(tail),
|
||||
"tokenUrl" => token_url.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, PartialEq)]
|
||||
#[serde(untagged)]
|
||||
pub enum MaybeRef<T> {
|
||||
|
@ -797,3 +1420,28 @@ pub enum MaybeRef<T> {
|
|||
value: T,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: JsonDeref> JsonDeref for MaybeRef<T> {
|
||||
fn deref_any(&self, path: &str) -> eyre::Result<&dyn std::any::Any> {
|
||||
if path.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let (head, tail) = path.split_once("/").unwrap_or((path, ""));
|
||||
match self {
|
||||
MaybeRef::Ref { _ref } => match head {
|
||||
"$ref" => _ref.deref_any(tail),
|
||||
_ => eyre::bail!("not found: {head}"),
|
||||
},
|
||||
MaybeRef::Value { value } => value.deref_any(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::any::Any> MaybeRef<T> {
|
||||
pub fn deref<'a>(&'a self, spec: &'a OpenApiV2) -> eyre::Result<&'a T> {
|
||||
match self {
|
||||
MaybeRef::Ref { _ref } => spec.deref(_ref),
|
||||
MaybeRef::Value { value } => Ok(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue