use crate::models::expression::is_strict_expr;
use regex::Regex;
use std::sync::LazyLock;
mod authentication;
mod document;
mod enum_validators;
mod one_of_validators;
mod task;
#[cfg(test)]
mod tests;
pub use authentication::{
validate_auth_policy, validate_basic_auth, validate_bearer_auth, validate_digest_auth,
validate_oauth2_auth, validate_oidc_auth,
};
pub use document::validate_workflow;
pub use enum_validators::{
validate_asyncapi_protocol, validate_container_cleanup, validate_container_lifetime,
validate_extension_task_type, validate_http_method, validate_http_output,
validate_oauth2_client_auth_method, validate_oauth2_grant_type,
validate_oauth2_request_encoding, validate_pull_policy, validate_script_language,
};
pub use one_of_validators::{
validate_auth_policy_one_of, validate_backoff_one_of, validate_process_type_one_of,
validate_schedule_one_of, validate_schema_one_of,
};
pub use task::{
validate_set_task, validate_switch_task, validate_task_map, validate_workflow_process,
};
#[derive(Debug, Clone, PartialEq)]
pub struct ValidationError {
pub field: String,
pub rule: ValidationRule,
pub message: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ValidationRule {
Required,
Semver,
Hostname,
Uri,
Iso8601Duration,
MutualExclusion,
InvalidValue,
Custom(String),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ValidationResult {
pub errors: Vec<ValidationError>,
}
impl ValidationResult {
pub fn new() -> Self {
Self { errors: Vec::new() }
}
pub fn add_error(&mut self, field: &str, rule: ValidationRule, message: &str) {
self.errors.push(ValidationError {
field: field.to_string(),
rule,
message: message.to_string(),
});
}
pub fn is_valid(&self) -> bool {
self.errors.is_empty()
}
pub fn merge_with_prefix(&mut self, prefix: &str, other: ValidationResult) {
for error in other.errors {
self.errors.push(ValidationError {
field: format!("{}.{}", prefix, error.field),
rule: error.rule,
message: error.message,
});
}
}
}
impl Default for ValidationResult {
fn default() -> Self {
Self::new()
}
}
static SEMVER_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$").expect("static semver regex is valid")
});
static HOSTNAME_RFC1123_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z]{2,63}|[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)$").expect("static hostname regex is valid")
});
static URI_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^[A-Za-z][A-Za-z0-9+\-.]*://[^{}\s]+$").expect("static URI regex is valid")
});
static URI_TEMPLATE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^[A-Za-z][A-Za-z0-9+\-.]*://.*\{.*}.*$")
.expect("static URI template regex is valid")
});
static JSON_POINTER_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^(/([^/~]|~[01])*)*$").expect("static JSON pointer regex is valid")
});
pub fn is_valid_semver(version: &str) -> bool {
SEMVER_PATTERN.is_match(version)
}
pub fn is_valid_hostname(hostname: &str) -> bool {
HOSTNAME_RFC1123_PATTERN.is_match(hostname)
}
pub fn validate_required_hostname(value: &str, field: &str, result: &mut ValidationResult) {
if value.is_empty() {
result.add_error(
field,
ValidationRule::Required,
&format!("{} is required", field),
);
} else if !is_valid_hostname(value) {
result.add_error(
field,
ValidationRule::Hostname,
&format!("{} must be a valid RFC 1123 hostname", field),
);
}
}
pub fn validate_required_semver(value: &str, field: &str, result: &mut ValidationResult) {
if value.is_empty() {
result.add_error(
field,
ValidationRule::Required,
&format!("{} is required", field),
);
} else if !is_valid_semver(value) {
result.add_error(
field,
ValidationRule::Semver,
&format!("{} must be a valid semantic version", field),
);
}
}
pub fn is_non_empty_string(value: &str) -> bool {
!value.is_empty()
}
pub fn is_uri_or_runtime_expr(value: &str) -> bool {
if value.is_empty() {
return false;
}
if is_strict_expr(value) {
return true;
}
URI_PATTERN.is_match(value) || URI_TEMPLATE_PATTERN.is_match(value)
}
pub fn is_json_pointer_or_runtime_expr(value: &str) -> bool {
if value.is_empty() {
return false;
}
if is_strict_expr(value) {
return true;
}
JSON_POINTER_PATTERN.is_match(value)
}
pub fn is_valid_uri(value: &str) -> bool {
URI_PATTERN.is_match(value)
}
pub fn is_valid_uri_template(value: &str) -> bool {
URI_TEMPLATE_PATTERN.is_match(value)
}
pub fn is_valid_json_pointer(value: &str) -> bool {
JSON_POINTER_PATTERN.is_match(value)
}