feat(v2): initial commit

This commit is contained in:
TotallyNot 2025-04-24 13:32:02 +02:00
parent 48868983b3
commit 5a84558d89
44 changed files with 20091 additions and 3489 deletions

View file

@ -0,0 +1,4 @@
pub mod parameter;
pub mod path;
pub mod schema;
pub mod r#type;

View file

@ -0,0 +1,40 @@
use std::borrow::Cow;
use serde::Deserialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ParameterLocation {
Query,
Path,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum OpenApiParameterDefault<'a> {
Int(i32),
Str(&'a str),
}
#[derive(Debug, Clone, Deserialize)]
pub struct OpenApiParameterSchema<'a> {
#[serde(rename = "$ref")]
pub ref_path: Option<&'a str>,
pub r#type: Option<&'a str>,
pub r#enum: Option<Vec<&'a str>>,
pub format: Option<&'a str>,
pub default: Option<OpenApiParameterDefault<'a>>,
pub maximum: Option<i32>,
pub minimum: Option<i32>,
pub items: Option<Box<OpenApiParameterSchema<'a>>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OpenApiParameter<'a> {
pub name: &'a str,
pub description: Option<Cow<'a, str>>,
pub r#in: ParameterLocation,
pub required: bool,
#[serde(borrow)]
pub schema: OpenApiParameterSchema<'a>,
}

View file

@ -0,0 +1,81 @@
use std::borrow::Cow;
use serde::{Deserialize, Deserializer};
use super::parameter::OpenApiParameter;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum OpenApiPathParameter<'a> {
Link {
#[serde(rename = "$ref")]
ref_path: &'a str,
},
Inline(OpenApiParameter<'a>),
}
#[derive(Debug, Clone, Deserialize)]
pub struct SchemaLink<'a> {
#[serde(rename = "$ref")]
pub ref_path: &'a str,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum OpenApiResponseBody<'a> {
Schema(SchemaLink<'a>),
Union {
#[serde(borrow, rename = "anyOf")]
any_of: Vec<SchemaLink<'a>>,
},
}
fn deserialize_response_body<'de, D>(deserializer: D) -> Result<OpenApiResponseBody<'de>, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct Json<'a> {
#[serde(borrow)]
schema: OpenApiResponseBody<'a>,
}
#[derive(Deserialize)]
struct Content<'a> {
#[serde(borrow, rename = "application/json")]
json: Json<'a>,
}
#[derive(Deserialize)]
struct StatusOk<'a> {
#[serde(borrow)]
content: Content<'a>,
}
#[derive(Deserialize)]
struct Responses<'a> {
#[serde(borrow, rename = "200")]
ok: StatusOk<'a>,
}
let responses = Responses::deserialize(deserializer)?;
Ok(responses.ok.content.json.schema)
}
#[derive(Debug, Clone, Deserialize)]
pub struct OpenApiPathBody<'a> {
pub summary: Option<Cow<'a, str>>,
pub description: Cow<'a, str>,
#[serde(borrow, default)]
pub parameters: Vec<OpenApiPathParameter<'a>>,
#[serde(
borrow,
rename = "responses",
deserialize_with = "deserialize_response_body"
)]
pub response_content: OpenApiResponseBody<'a>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OpenApiPath<'a> {
#[serde(borrow)]
pub get: OpenApiPathBody<'a>,
}

View file

@ -0,0 +1,38 @@
use indexmap::IndexMap;
use serde::Deserialize;
use super::{parameter::OpenApiParameter, path::OpenApiPath, r#type::OpenApiType};
#[derive(Debug, Clone, Deserialize)]
pub struct Components<'a> {
#[serde(borrow)]
pub schemas: IndexMap<&'a str, OpenApiType<'a>>,
#[serde(borrow)]
pub parameters: IndexMap<&'a str, OpenApiParameter<'a>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct OpenApiSchema<'a> {
#[serde(borrow)]
pub paths: IndexMap<&'a str, OpenApiPath<'a>>,
#[serde(borrow)]
pub components: Components<'a>,
}
impl OpenApiSchema<'_> {
pub fn read() -> Result<Self, serde_json::Error> {
let s = include_str!("../../openapi.json");
serde_json::from_str(s)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn read() {
OpenApiSchema::read().unwrap();
}
}

View file

@ -0,0 +1,98 @@
use std::borrow::Cow;
use indexmap::IndexMap;
use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum OpenApiVariants<'a> {
Int(Vec<i32>),
#[serde(borrow)]
Str(Vec<&'a str>),
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenApiType<'a> {
#[serde(default)]
pub deprecated: bool,
pub description: Option<Cow<'a, str>>,
pub r#type: Option<&'a str>,
pub format: Option<&'a str>,
#[serde(rename = "$ref")]
pub ref_path: Option<&'a str>,
pub one_of: Option<Vec<OpenApiType<'a>>>,
pub all_of: Option<Vec<OpenApiType<'a>>>,
pub required: Option<Vec<&'a str>>,
#[serde(borrow)]
pub properties: Option<IndexMap<&'a str, OpenApiType<'a>>>,
pub items: Option<Box<OpenApiType<'a>>>,
pub r#enum: Option<OpenApiVariants<'a>>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn object() {
let json = r##"
{
"required": [
"name",
"branches"
],
"properties": {
"name": {
"type": "string"
},
"branches": {
"type": "array",
"items": {
"$ref": "#/components/schemas/TornFactionTreeBranch"
}
}
},
"type": "object"
}
"##;
let obj: OpenApiType = serde_json::from_str(json).unwrap();
assert_eq!(obj.r#type, Some("object"));
let props = obj.properties.unwrap();
assert!(props.contains_key("name"));
let branches = props.get("branches").unwrap();
assert_eq!(branches.r#type, Some("array"));
let items = branches.items.as_ref().unwrap();
assert!(items.ref_path.is_some());
}
#[test]
fn enum_variants() {
let int_json = r#"
[1, 2, 3, 4]
"#;
let de: OpenApiVariants = serde_json::from_str(int_json).unwrap();
assert_eq!(de, OpenApiVariants::Int(vec![1, 2, 3, 4]));
let str_json = r#"
["foo", "bar", "baz"]
"#;
let de: OpenApiVariants = serde_json::from_str(str_json).unwrap();
assert_eq!(de, OpenApiVariants::Str(vec!["foo", "bar", "baz"]));
}
}