oasert 0.1.4

A library for verifying in-flight requests against a provided OpenAPI 3.1.x or 3.0.x specification.
Documentation
use crate::error::{
    OperationSection, PayloadSection, Section, SpecificationSection, ValidationErrorType,
};
use serde_json::{json, Value};
use std::fmt::{Display, Formatter};
use std::str::FromStr;

#[derive(PartialEq, Debug)]
pub enum OpenApiPrimitives {
    Null,
    Bool,
    Integer,
    Array,
    Number,
    String,
    Object,
}

impl Display for OpenApiPrimitives {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            OpenApiPrimitives::Null => write!(f, "null"),
            OpenApiPrimitives::Bool => write!(f, "bool"),
            OpenApiPrimitives::Integer => write!(f, "integer"),
            OpenApiPrimitives::Array => write!(f, "array"),
            OpenApiPrimitives::Number => write!(f, "number"),
            OpenApiPrimitives::String => write!(f, "string"),
            OpenApiPrimitives::Object => write!(f, "object"),
        }
    }
}

impl FromStr for OpenApiPrimitives {
    type Err = ValidationErrorType;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "null" => Ok(OpenApiPrimitives::Null),
            "bool" => Ok(OpenApiPrimitives::Bool),
            "integer" => Ok(OpenApiPrimitives::Integer),
            "number" => Ok(OpenApiPrimitives::Number),
            "string" => Ok(OpenApiPrimitives::String),
            "array" => Ok(OpenApiPrimitives::Array),
            "object" => Ok(OpenApiPrimitives::Object),
            &_ => todo!(),
        }
    }
}

impl OpenApiPrimitives {
    pub fn get_type_from_serde(schema: &Value) -> Option<OpenApiPrimitives> {
        if schema.is_string() {
            return Some(OpenApiPrimitives::String);
        } else if schema.is_array() {
            return Some(OpenApiPrimitives::Array);
        } else if schema.is_object() {
            return Some(OpenApiPrimitives::Object);
        } else if schema.is_null() {
            return Some(OpenApiPrimitives::Null);
        } else if schema.is_boolean() {
            return Some(OpenApiPrimitives::Bool);
        } else if schema.is_number() {
            return Some(OpenApiPrimitives::Number);
        }
        None
    }

    pub fn convert_string_to_schema_type(
        schema: &Value,
        input: &str,
    ) -> Result<Value, ValidationErrorType> {
        let type_field = match schema
            .get("type")
            .and_then(|type_value| type_value.as_str())
        {
            None => {
                return Err(ValidationErrorType::FieldExpected(
                    String::from("type"),
                    Section::Specification(SpecificationSection::Paths(
                        OperationSection::Parameters,
                    )),
                ));
            }
            Some(v) => v,
        };
        let openapi_type = OpenApiPrimitives::from_str(type_field)?;
        openapi_type.convert_value_to_type(input)
    }

    pub fn convert_value_to_type(&self, input: &str) -> Result<Value, ValidationErrorType> {
        match self {
            OpenApiPrimitives::Null => Ok(json!(Value::Null)),
            OpenApiPrimitives::Bool => Self::convert_to_type::<bool>(input),
            OpenApiPrimitives::Integer => Self::convert_to_type::<i64>(input),
            OpenApiPrimitives::Number => Self::convert_to_type::<f64>(input),
            OpenApiPrimitives::String => Self::convert_to_type::<String>(input),
            _ => todo!(),
        }
    }

    fn convert_to_type<T: for<'de> serde::de::Deserialize<'de> + serde::Serialize + FromStr>(
        input: &str,
    ) -> Result<Value, ValidationErrorType> {
        let converted_value: T = match input.parse::<T>() {
            Ok(val) => val,
            Err(_) => {
                return Err(ValidationErrorType::UnableToParse(
                    input.to_string(),
                    Section::Payload(PayloadSection::Path),
                ));
            }
        };
        Ok(json!(converted_value))
    }
}