use std::{error::Error, fmt, path::PathBuf};
use httpgenerator_core::NormalizedHttpMethod;
use reqwest::StatusCode;
use url::Url;
use crate::{OpenApiContentFormat, OpenApiSource, OpenApiSpecificationVersion};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SourceClassificationError {
EmptyInput,
UnsupportedUrlScheme(String),
InvalidUrl { value: String, reason: String },
}
impl fmt::Display for SourceClassificationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyInput => write!(f, "OpenAPI source input cannot be empty"),
Self::UnsupportedUrlScheme(scheme) => {
write!(f, "unsupported OpenAPI source URL scheme '{scheme}'")
}
Self::InvalidUrl { value, reason } => {
write!(f, "invalid OpenAPI source URL '{value}': {reason}")
}
}
}
}
impl Error for SourceClassificationError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ContentFormatDetectionError {
EmptyContent,
UnknownFormat,
}
impl fmt::Display for ContentFormatDetectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyContent => write!(f, "OpenAPI content cannot be empty"),
Self::UnknownFormat => write!(f, "unable to detect OpenAPI content format"),
}
}
}
impl Error for ContentFormatDetectionError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RawOpenApiLoadError {
SourceClassification(SourceClassificationError),
FileRead {
path: PathBuf,
reason: String,
},
HttpRequest {
url: Url,
reason: String,
},
HttpStatus {
url: Url,
status: StatusCode,
},
HttpBodyRead {
url: Url,
reason: String,
},
FormatDetection {
source: OpenApiSource,
error: ContentFormatDetectionError,
},
Decode {
source: OpenApiSource,
format: OpenApiContentFormat,
reason: String,
},
}
impl fmt::Display for RawOpenApiLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SourceClassification(error) => {
write!(f, "failed to classify OpenAPI source: {error}")
}
Self::FileRead { path, reason } => {
write!(
f,
"failed to read OpenAPI file '{}': {reason}",
path.display()
)
}
Self::HttpRequest { url, reason } => {
write!(f, "failed to fetch OpenAPI URL '{url}': {reason}")
}
Self::HttpStatus { url, status } => {
write!(f, "OpenAPI URL '{url}' returned HTTP {status}")
}
Self::HttpBodyRead { url, reason } => {
write!(
f,
"failed to read OpenAPI response body from '{url}': {reason}"
)
}
Self::FormatDetection { source, error } => {
write!(
f,
"failed to detect OpenAPI content format for '{source}': {error}"
)
}
Self::Decode {
source,
format,
reason,
} => {
write!(
f,
"failed to decode {format} OpenAPI document from '{source}': {reason}"
)
}
}
}
}
impl Error for RawOpenApiLoadError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SpecificationVersionDetectionError {
MissingVersionField,
InvalidVersionFieldType { field: &'static str },
UnsupportedVersion { field: &'static str, value: String },
}
impl fmt::Display for SpecificationVersionDetectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingVersionField => {
write!(
f,
"OpenAPI document is missing a top-level 'openapi' or 'swagger' version field"
)
}
Self::InvalidVersionFieldType { field } => {
write!(f, "OpenAPI document field '{field}' must be a string")
}
Self::UnsupportedVersion { field, value } => {
write!(
f,
"unsupported OpenAPI version '{value}' in field '{field}'"
)
}
}
}
}
impl Error for SpecificationVersionDetectionError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpenApiInspectionError {
Load(RawOpenApiLoadError),
VersionDetection(SpecificationVersionDetectionError),
}
impl fmt::Display for OpenApiInspectionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Load(error) => write!(f, "{error}"),
Self::VersionDetection(error) => write!(f, "{error}"),
}
}
}
impl Error for OpenApiInspectionError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypedOpenApiParseError {
VersionDetection {
source: OpenApiSource,
error: SpecificationVersionDetectionError,
},
UnsupportedVersion {
source: OpenApiSource,
version: OpenApiSpecificationVersion,
},
Deserialize {
source: OpenApiSource,
version: OpenApiSpecificationVersion,
reason: String,
},
}
impl fmt::Display for TypedOpenApiParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::VersionDetection { source, error } => {
write!(
f,
"failed to detect OpenAPI specification version for '{source}': {error}"
)
}
Self::UnsupportedVersion { source, version } => {
write!(
f,
"typed OpenAPI parsing is not implemented for {version} documents from '{source}'"
)
}
Self::Deserialize {
source,
version,
reason,
} => {
write!(
f,
"failed to deserialize {version} document from '{source}': {reason}"
)
}
}
}
}
impl Error for TypedOpenApiParseError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpenApiDocumentLoadError {
RawLoad(RawOpenApiLoadError),
TypedParse(TypedOpenApiParseError),
}
impl fmt::Display for OpenApiDocumentLoadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RawLoad(error) => write!(f, "{error}"),
Self::TypedParse(error) => write!(f, "{error}"),
}
}
}
impl Error for OpenApiDocumentLoadError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpenApiNormalizationError {
InvalidStructure {
path: String,
context: String,
},
UnsupportedPathItemReference {
path: String,
reference: String,
},
UnsupportedParameterReference {
path: String,
method: NormalizedHttpMethod,
reference: String,
},
UnsupportedRequestBodyReference {
path: String,
method: NormalizedHttpMethod,
reference: String,
},
}
impl fmt::Display for OpenApiNormalizationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidStructure { path, context } => {
write!(
f,
"OpenAPI document contains an unexpected structure at '{path}' ({context})"
)
}
Self::UnsupportedPathItemReference { path, reference } => {
write!(
f,
"path item '{path}' uses unsupported $ref '{reference}' during normalization"
)
}
Self::UnsupportedParameterReference {
path,
method,
reference,
} => {
write!(
f,
"{method:?} operation '{path}' uses unsupported parameter $ref '{reference}' during normalization"
)
}
Self::UnsupportedRequestBodyReference {
path,
method,
reference,
} => {
write!(
f,
"{method:?} operation '{path}' uses unsupported requestBody $ref '{reference}' during normalization"
)
}
}
}
}
impl Error for OpenApiNormalizationError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum OpenApiDocumentNormalizationError {
Load(OpenApiDocumentLoadError),
Normalize(OpenApiNormalizationError),
}
impl fmt::Display for OpenApiDocumentNormalizationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Load(error) => write!(f, "{error}"),
Self::Normalize(error) => write!(f, "{error}"),
}
}
}
impl Error for OpenApiDocumentNormalizationError {}