use serde::{Deserialize, Deserializer};
extern crate serde_json;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
pub struct Spec {
#[serde(rename = "auth")]
pub auth: Option<Auth>,
#[serde(rename = "event")]
pub event: Option<Vec<Event>>,
#[serde(rename = "info")]
pub info: Information,
#[serde(rename = "item")]
pub item: Vec<Items>,
#[serde(rename = "variable")]
pub variable: Option<Vec<Variable>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Auth {
#[serde(rename = "awsv4")]
pub awsv4: Option<AuthAttributeUnion>,
#[serde(rename = "basic")]
pub basic: Option<AuthAttributeUnion>,
#[serde(rename = "bearer")]
pub bearer: Option<AuthAttributeUnion>,
#[serde(rename = "jwt")]
pub jwt: Option<AuthAttributeUnion>,
#[serde(rename = "digest")]
pub digest: Option<AuthAttributeUnion>,
#[serde(rename = "hawk")]
pub hawk: Option<AuthAttributeUnion>,
#[serde(rename = "noauth")]
pub noauth: Option<serde_json::Value>,
#[serde(rename = "ntlm")]
pub ntlm: Option<AuthAttributeUnion>,
#[serde(rename = "oauth1")]
pub oauth1: Option<AuthAttributeUnion>,
#[serde(rename = "oauth2")]
pub oauth2: Option<Oauth2>,
#[serde(rename = "apikey")]
pub apikey: Option<ApiKey>,
#[serde(rename = "type")]
pub auth_type: AuthType,
}
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
pub struct ApiKey {
pub key: Option<String>,
pub location: ApiKeyLocation,
pub value: Option<String>,
}
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
pub enum ApiKeyLocation {
Header,
Query,
}
impl<'de> Deserialize<'de> for ApiKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut key = None;
let mut location = ApiKeyLocation::Header;
let mut value = None;
let deserialized = AuthAttributeUnion::deserialize(deserializer)?;
if let AuthAttributeUnion::AuthAttribute21(v) = deserialized {
for item in v {
if let Some(serde_json::Value::String(str)) = item.value {
match item.key.as_str() {
"key" => key = Some(str),
"in" => {
location = match str.as_str() {
"query" => ApiKeyLocation::Query,
"header" => ApiKeyLocation::Header,
_ => ApiKeyLocation::Header,
}
}
"value" => value = Some(str),
_ => {}
}
}
}
}
Ok(ApiKey {
key,
location,
value,
})
}
}
#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
pub struct Oauth2 {
pub grant_type: Oauth2GrantType,
pub access_token_url: Option<String>,
pub add_token_to: Option<String>,
pub auth_url: Option<String>,
pub client_id: Option<String>,
pub client_secret: Option<String>,
pub refresh_token_url: Option<String>,
pub scope: Option<Vec<String>>,
pub state: Option<String>,
pub token_name: Option<String>,
}
impl<'de> Deserialize<'de> for Oauth2 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut grant_type = Oauth2GrantType::AuthorizationCode;
let mut access_token_url = None;
let mut add_token_to = None;
let mut auth_url = None;
let mut client_id = None;
let mut client_secret = None;
let mut refresh_token_url = None;
let mut scope = None;
let mut state = None;
let mut token_name = None;
let deserialized = AuthAttributeUnion::deserialize(deserializer)?;
if let AuthAttributeUnion::AuthAttribute21(v) = deserialized {
for item in v {
if let Some(serde_json::Value::String(str)) = item.value {
match item.key.as_str() {
"grantType" => {
grant_type = match str.as_str() {
"authorization_code" => Oauth2GrantType::AuthorizationCode,
"authorization_code_with_pkce" => {
Oauth2GrantType::AuthorizationCodeWithPkce
}
"client_credentials" => Oauth2GrantType::ClientCredentials,
"implicit" => Oauth2GrantType::Implicit,
"password_credentials" => Oauth2GrantType::PasswordCredentials,
_ => Oauth2GrantType::AuthorizationCode,
}
}
"accessTokenUrl" => access_token_url = Some(str),
"addTokenTo" => add_token_to = Some(str),
"authUrl" => auth_url = Some(str),
"clientId" => client_id = Some(str),
"clientSecret" => client_secret = Some(str),
"refreshTokenUrl" => refresh_token_url = Some(str),
"scope" => scope = Some(str.split(' ').map(|s| s.to_string()).collect()),
"state" => state = Some(str),
"tokenName" => token_name = Some(str),
_ => {}
}
}
}
}
Ok(Oauth2 {
grant_type,
access_token_url,
add_token_to,
auth_url,
client_id,
client_secret,
refresh_token_url,
scope,
state,
token_name,
})
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum Oauth2GrantType {
AuthorizationCodeWithPkce,
ClientCredentials,
Implicit,
PasswordCredentials,
AuthorizationCode,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct AuthAttribute {
#[serde(rename = "key")]
pub key: String,
#[serde(rename = "type")]
pub auth_type: Option<String>,
#[serde(rename = "value")]
pub value: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum AuthAttributeUnion {
AuthAttribute21(Vec<AuthAttribute>),
AuthAttribute20(Option<serde_json::Value>),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Event {
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "id")]
pub id: Option<String>,
#[serde(rename = "listen")]
pub listen: String,
#[serde(rename = "script")]
pub script: Option<Script>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Script {
#[serde(rename = "exec")]
pub exec: Option<Host>,
#[serde(rename = "id")]
pub id: Option<String>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "src")]
pub src: Option<Url>,
#[serde(rename = "type")]
pub script_type: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct UrlClass {
#[serde(rename = "hash")]
pub hash: Option<String>,
#[serde(rename = "host")]
pub host: Option<Host>,
#[serde(rename = "path")]
pub path: Option<UrlPath>,
#[serde(rename = "port")]
pub port: Option<String>,
#[serde(rename = "protocol")]
pub protocol: Option<String>,
#[serde(rename = "query")]
pub query: Option<Vec<QueryParam>>,
#[serde(rename = "raw")]
pub raw: Option<String>,
#[serde(rename = "variable")]
pub variable: Option<Vec<Variable>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct PathClass {
#[serde(rename = "type")]
pub path_type: Option<String>,
#[serde(rename = "value")]
pub value: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct GraphQlBodyClass {
#[serde(rename = "query")]
pub query: Option<String>,
#[serde(rename = "variables")]
pub variables: Option<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct QueryParam {
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "key")]
pub key: Option<String>,
#[serde(rename = "value")]
pub value: Option<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct Description {
#[serde(rename = "content")]
pub content: Option<String>,
#[serde(rename = "type")]
pub description_type: Option<String>,
#[serde(rename = "version")]
pub version: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct Variable {
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "id")]
pub id: Option<String>,
#[serde(rename = "key")]
pub key: Option<String>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "system")]
pub system: Option<bool>,
#[serde(rename = "type")]
pub variable_type: Option<VariableType>,
#[serde(rename = "value")]
pub value: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)]
pub struct Information {
#[serde(rename = "_postman_id")]
pub postman_id: Option<String>,
#[serde(rename = "_exporter_id")]
pub exporter_id: Option<String>,
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "schema")]
pub schema: String,
#[serde(rename = "version")]
pub version: Option<CollectionVersion>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct CollectionVersionClass {
#[serde(rename = "identifier")]
pub identifier: Option<String>,
#[serde(rename = "major")]
pub major: i64,
#[serde(rename = "meta")]
pub meta: Option<serde_json::Value>,
#[serde(rename = "minor")]
pub minor: i64,
#[serde(rename = "patch")]
pub patch: i64,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Items {
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "event")]
pub event: Option<Vec<Event>>,
#[serde(rename = "id")]
pub id: Option<String>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "protocolProfileBehavior")]
pub protocol_profile_behavior: Option<ProtocolProfileBehavior>,
#[serde(rename = "request")]
pub request: Option<RequestUnion>,
#[serde(rename = "response")]
pub response: Option<Vec<Option<ResponseClass>>>,
#[serde(rename = "variable")]
pub variable: Option<Vec<Variable>>,
#[serde(rename = "auth")]
pub auth: Option<Auth>,
#[serde(rename = "item")]
pub item: Option<Vec<Items>>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ProtocolProfileBehavior {
#[serde(rename = "disableBodyPruning")]
pub disable_body_pruning: Option<bool>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct RequestClass {
#[serde(rename = "auth")]
pub auth: Option<Auth>,
#[serde(rename = "body")]
pub body: Option<Body>,
#[serde(rename = "certificate")]
pub certificate: Option<Certificate>,
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "header")]
pub header: Option<HeaderUnion>,
#[serde(rename = "method")]
pub method: Option<String>,
#[serde(rename = "proxy")]
pub proxy: Option<ProxyConfig>,
#[serde(rename = "url")]
pub url: Option<Url>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Body {
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "file")]
pub file: Option<File>,
#[serde(rename = "formdata")]
pub formdata: Option<Vec<FormParameter>>,
#[serde(rename = "mode")]
pub mode: Option<Mode>,
#[serde(rename = "raw")]
pub raw: Option<String>,
#[serde(rename = "options")]
pub options: Option<BodyOptions>,
#[serde(rename = "urlencoded")]
pub urlencoded: Option<Vec<UrlEncodedParameter>>,
#[serde(rename = "graphql")]
pub graphql: Option<GraphQlBody>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct BodyOptions {
#[serde(rename = "raw")]
pub raw: Option<RawOptions>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct RawOptions {
#[serde(rename = "language")]
pub language: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct File {
#[serde(rename = "content")]
pub content: Option<String>,
#[serde(rename = "src")]
pub src: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct FormParameter {
#[serde(rename = "contentType")]
pub content_type: Option<String>,
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "key")]
pub key: String,
#[serde(rename = "type")]
pub form_parameter_type: Option<String>,
#[serde(rename = "value")]
pub value: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct UrlEncodedParameter {
#[serde(rename = "description")]
pub description: Option<DescriptionUnion>,
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "key")]
pub key: String,
#[serde(rename = "value")]
pub value: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Certificate {
#[serde(rename = "cert")]
pub cert: Option<Cert>,
#[serde(rename = "key")]
pub key: Option<Key>,
#[serde(rename = "matches")]
pub matches: Option<Vec<Option<serde_json::Value>>>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "passphrase")]
pub passphrase: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Cert {
#[serde(rename = "src")]
pub src: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Key {
#[serde(rename = "src")]
pub src: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Header {
#[serde(rename = "description", default)]
pub description: Option<DescriptionUnion>,
#[serde(rename = "disabled", default)]
pub disabled: Option<bool>,
#[serde(rename = "key", default)]
pub key: Option<String>,
#[serde(rename = "value", deserialize_with = "deserialize_as_string", default)]
pub value: Option<String>,
}
struct DeserializeAnyAsString;
impl<'de> serde::de::Visitor<'de> for DeserializeAnyAsString {
type Value = Option<String>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("missing, null, an integer, or a string")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Some(v.to_string()))
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
Ok(Some(v.to_string()))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
Ok(Some(v))
}
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Some(v.to_string()))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Some(v.to_string()))
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
}
fn deserialize_as_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(DeserializeAnyAsString)
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ProxyConfig {
#[serde(rename = "disabled")]
pub disabled: Option<bool>,
#[serde(rename = "host")]
pub host: Option<String>,
#[serde(rename = "match")]
pub proxy_config_match: Option<String>,
#[serde(rename = "port")]
pub port: Option<i64>,
#[serde(rename = "tunnel")]
pub tunnel: Option<bool>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ResponseClass {
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "body")]
pub body: Option<String>,
#[serde(rename = "code")]
pub code: Option<i64>,
#[serde(rename = "cookie")]
pub cookie: Option<Vec<Cookie>>,
#[serde(rename = "header")]
pub header: Option<Headers>,
#[serde(rename = "id")]
pub id: Option<String>,
#[serde(rename = "originalRequest")]
pub original_request: Option<RequestClass>,
#[serde(rename = "responseTime")]
pub response_time: Option<ResponseTime>,
#[serde(rename = "status")]
pub status: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Cookie {
#[serde(rename = "domain")]
pub domain: Option<String>,
#[serde(rename = "expires")]
pub expires: Option<String>,
#[serde(rename = "extensions")]
pub extensions: Option<Vec<Option<serde_json::Value>>>,
#[serde(rename = "hostOnly")]
pub host_only: Option<bool>,
#[serde(rename = "httpOnly")]
pub http_only: Option<bool>,
#[serde(rename = "maxAge")]
pub max_age: Option<String>,
#[serde(rename = "name")]
pub name: Option<String>,
#[serde(rename = "path")]
pub path: Option<String>,
#[serde(rename = "secure")]
pub secure: Option<bool>,
#[serde(rename = "session")]
pub session: Option<bool>,
#[serde(rename = "value")]
pub value: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Host {
String(String),
StringArray(Vec<String>),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Url {
String(String),
UrlClass(UrlClass),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum UrlPath {
String(String),
UnionArray(Vec<PathElement>),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum PathElement {
PathClass(PathClass),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum DescriptionUnion {
Description(Description),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum CollectionVersion {
CollectionVersionClass(CollectionVersionClass),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum RequestUnion {
RequestClass(Box<RequestClass>),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum HeaderUnion {
HeaderArray(Vec<Header>),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Response {
ResponseClass(Box<ResponseClass>),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Headers {
String(String),
UnionArray(Vec<HeaderElement>),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum HeaderElement {
Header(Header),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum ResponseTime {
Number(u64),
String(String),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum GraphQlBody {
String(String),
GraphQlBodyClass(GraphQlBodyClass),
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum AuthType {
#[serde(rename = "awsv4")]
Awsv4,
#[serde(rename = "basic")]
Basic,
#[serde(rename = "bearer")]
Bearer,
#[serde(rename = "digest")]
Digest,
#[serde(rename = "jwt")]
Jwt,
#[serde(rename = "hawk")]
Hawk,
#[serde(rename = "noauth")]
Noauth,
#[serde(rename = "ntlm")]
Ntlm,
#[serde(rename = "oauth1")]
Oauth1,
#[serde(rename = "oauth2")]
Oauth2,
#[serde(rename = "apikey")]
Apikey,
}
impl Default for AuthType {
fn default() -> AuthType {
AuthType::Noauth
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum VariableType {
#[serde(rename = "any")]
Any,
#[serde(rename = "boolean")]
Boolean,
#[serde(rename = "number")]
Number,
#[serde(rename = "string")]
String,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub enum Mode {
#[serde(rename = "file")]
File,
#[serde(rename = "formdata")]
Formdata,
#[serde(rename = "raw")]
Raw,
#[serde(rename = "urlencoded")]
Urlencoded,
#[serde(rename = "graphql")]
GraphQl,
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deserializes_oauth2() {
let spec: Spec =
serde_json::from_str(get_fixture("oauth2-code.postman.json").as_ref()).unwrap();
let oauth2 = spec.auth.unwrap().oauth2.unwrap();
assert_eq!(oauth2.grant_type, Oauth2GrantType::AuthorizationCode);
assert_eq!(
oauth2.auth_url,
Some("https://example.com/oauth2/authorization".to_string())
);
assert_eq!(
oauth2.access_token_url,
Some("https://example.com/oauth2/token".to_string())
);
}
#[test]
fn deserializes_apikey() {
let spec: Spec =
serde_json::from_str(get_fixture("api-key.postman.json").as_ref()).unwrap();
let apikey = spec.auth.unwrap().apikey.unwrap();
assert_eq!(apikey.key, Some("Authorization".to_string()));
assert_eq!(apikey.location, ApiKeyLocation::Header);
assert_eq!(apikey.value, None);
}
fn get_fixture(filename: &str) -> String {
use std::fs;
let filename: std::path::PathBuf =
[env!("CARGO_MANIFEST_DIR"), "./tests/fixtures/", filename]
.iter()
.collect();
let file = filename.into_os_string().into_string().unwrap();
fs::read_to_string(file).unwrap()
}
}