use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct Spec {
pub paths: HashMap<String, SpecPathItem>,
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct SpecPathItem {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub get: Option<SpecPathMethod>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub post: Option<SpecPathMethod>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub put: Option<SpecPathMethod>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub delete: Option<SpecPathMethod>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub patch: Option<SpecPathMethod>,
}
impl SpecPathItem {
pub fn methods(&self) -> impl Iterator<Item = &SpecPathMethod> {
[&self.get, &self.post, &self.put, &self.delete, &self.patch]
.into_iter()
.flatten()
}
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
pub struct SpecPathMethod {
#[serde(
rename = "operationId",
default,
skip_serializing_if = "Option::is_none"
)]
pub operation_id: Option<String>,
}
impl Spec {
pub fn operation_index(&self) -> HashMap<String, String> {
let mut index = HashMap::new();
for (path, item) in &self.paths {
let path = path.strip_prefix('/').unwrap_or(path);
for op_id in item.methods().filter_map(|m| m.operation_id.as_ref()) {
index.insert(op_id.clone(), path.to_owned());
}
}
index
}
}
#[cfg(test)]
mod tests {
use super::Spec;
const FIXTURE: &str = include_str!("../resources/test/openapi.json");
#[test]
fn test_parse_openapi_fixture() {
let spec: Spec = serde_json::from_str(FIXTURE).expect("fixture should parse");
let index = spec.operation_index();
assert!(index.len() > 200, "only {} operations", index.len());
assert_eq!(
index.get("GetMarketsRegionIdOrders").map(String::as_str),
Some("markets/{region_id}/orders")
);
assert_eq!(
index.get("GetCharactersDetail").map(String::as_str),
Some("characters/{character_id}")
);
}
#[test]
fn test_parse_ignores_unknown_keys() {
let source = r#"{
"openapi": "3.1.0",
"paths": {
"/status": {
"parameters": [{"name": "x", "in": "header"}],
"summary": "Server status",
"get": {"operationId": "GetStatus", "tags": ["Status"]}
},
"/no-op-id": {"get": {"summary": "missing id"}}
}
}"#;
let spec: Spec = serde_json::from_str(source).unwrap();
let index = spec.operation_index();
assert_eq!(index.len(), 1);
assert_eq!(index["GetStatus"], "status");
}
}