#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Severity {
Error,
Warning,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Violation {
pub location: String,
pub severity: Severity,
pub error: SpecError,
}
impl Violation {
pub(in crate::validate) fn error(location: impl Into<String>, error: SpecError) -> Self {
Self {
location: location.into(),
severity: Severity::Error,
error,
}
}
pub(in crate::validate) fn warning(location: impl Into<String>, error: SpecError) -> Self {
Self {
location: location.into(),
severity: Severity::Warning,
error,
}
}
}
impl std::fmt::Display for Violation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let label = match self.severity {
Severity::Error => "error",
Severity::Warning => "warning",
};
write!(f, "{label} at {}: {}", self.location, self.error)
}
}
impl std::error::Error for Violation {}
pub(crate) fn pointer_token(key: &str) -> String {
key.replace('~', "~0").replace('/', "~1")
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum SpecError {
#[error("`operationId` `{operation_id}` is not unique; it is also used at {first}")]
DuplicateOperationId {
operation_id: String,
first: String,
},
#[error(
"path `{template}` is the same path as `{existing}`; templates that differ \
only in variable name are identical"
)]
DuplicatePathTemplate {
template: String,
existing: String,
},
#[error("`{template}` is not a legal path template: {reason}")]
InvalidPathTemplate {
template: String,
reason: crate::model::paths::template::InvalidPathTemplate,
},
#[error("path template variable `{name}` has no matching `in: path` parameter")]
UndeclaredPathVariable {
name: String,
},
#[error("`in: path` parameter `{name}` does not appear in the path template")]
UnusedPathParameter {
name: String,
},
#[error("`in: path` parameter `{name}` must set `required: true`")]
PathParameterNotRequired {
name: String,
},
#[error("parameter `{name}` in `{location}` is declared more than once")]
DuplicateParameter {
name: String,
location: String,
},
#[error(
"`{name}` declares statuses {declared:?} as a short circuit but describes {described:?}"
)]
ShortCircuitMismatch {
name: String,
declared: Vec<u16>,
described: Vec<u16>,
},
#[error("style `{style}` may not be used with `in: {location}`")]
IllegalStyle {
style: String,
location: String,
},
#[error(
"`{name}` must not be declared as a header parameter: the specification says \
such a definition is ignored, so declaring it misdescribes the API"
)]
IgnoredHeaderParameter {
name: String,
},
#[error(
"`{name}` must not be declared here: the media type is stated separately, so \
the specification says such a definition is ignored"
)]
IgnoredHeader {
name: String,
},
#[error("an operation must declare at least one response")]
NoResponses,
#[cfg(feature = "openapi32")]
#[error(
"`encoding` describes named properties and `prefixEncoding`/`itemEncoding` describe array \
positions; a media type declares one or the other, never both"
)]
ConflictingEncoding,
#[error("a response must have a description under OpenAPI 3.1")]
MissingResponseDescription,
#[error("`{name}` is not a valid component name: expected only `A-Z a-z 0-9 . - _`")]
InvalidComponentName {
name: String,
},
#[error("tag `{name}` is declared more than once")]
DuplicateTag {
name: String,
},
#[error("tag `{name}` names parent `{parent}`, which is not declared")]
UnknownTagParent {
name: String,
parent: String,
},
#[error("tag `{name}` is part of a parent cycle")]
TagParentCycle {
name: String,
},
#[error("security requirement `{name}` does not name a declared security scheme")]
UnknownSecurityScheme {
name: String,
},
#[error("operation uses tag `{name}`, which has no metadata in the document `tags`")]
UndocumentedTag {
name: String,
},
#[error("server variable `{name}` declares an empty `enum`")]
EmptyServerVariableEnum {
name: String,
},
#[error("server variable `{name}` has a default that is not in its `enum`")]
ServerVariableDefaultNotInEnum {
name: String,
},
#[error(
"`{name}` is not a usable extension name: expected an `x-` prefix, not `x-oai-`/`x-oas-`"
)]
InvalidExtensionName {
name: String,
},
#[error(
"this payload is described by the permissive schema, so the description does not \
constrain it"
)]
UncheckedSchema,
#[error("this description omits part of the service and is not authoritative")]
NotAuthoritative,
#[error(
"this operation is covered by an `unchecked` waiver ({}), so its description is not \
verified",
reasons.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
)]
OpaqueOperation {
reasons: Vec<crate::annotation::OpaqueReason>,
},
#[error(
"`{pattern}` is served but no path template can express it, so it has no `paths` entry"
)]
OpaqueRoute {
pattern: String,
},
#[error(
"this description contains opaque operations or routes but is not marked \
non-authoritative"
)]
AuthorityNotStamped,
#[error("`{name}` is present but is not in the form Kynos emits: {detail}")]
MalformedAnnotation {
name: String,
detail: String,
},
#[error("a document must declare at least one of `paths`, `components` or `webhooks`")]
EmptyDocument,
#[error("cannot emit as OpenAPI 3.1: {} 3.2-only construct(s) in use: {}", blockers.len(), blockers.join(", "))]
RequiresV3_2 {
blockers: Vec<String>,
},
}