connect2axum-codegen 0.2.0

Protoc generators for REST, WebSocket, OpenAPI, and AsyncAPI wrappers over ConnectRPC services
Documentation
use serde_json::{Map, Value};
use uni_error::UniError;

use crate::error::{CodegenErrKind, CodegenResult};

pub fn ensure_object_at<'a>(root: &'a mut Value, field: &str) -> &'a mut Map<String, Value> {
    root.as_object_mut()
        .expect("OpenAPI document root should be an object")
        .entry(field.to_owned())
        .or_insert_with(|| Value::Object(Map::new()))
        .as_object_mut()
        .expect("OpenAPI root field should be an object")
}

pub fn ensure_nested_object<'a>(
    root: &'a mut Map<String, Value>,
    field: &str,
) -> CodegenResult<&'a mut Map<String, Value>> {
    root.entry(field.to_owned())
        .or_insert_with(|| Value::Object(Map::new()))
        .as_object_mut()
        .ok_or_else(|| {
            UniError::from_kind_context(
                CodegenErrKind::OpenApiInvalidDocument,
                format!("OpenAPI field {field} was not an object"),
            )
        })
}

pub fn ensure_array_at<'a>(root: &'a mut Value, field: &str) -> CodegenResult<&'a mut Vec<Value>> {
    root.as_object_mut()
        .expect("OpenAPI document root should be an object")
        .entry(field.to_owned())
        .or_insert_with(|| Value::Array(Vec::new()))
        .as_array_mut()
        .ok_or_else(|| {
            UniError::from_kind_context(
                CodegenErrKind::OpenApiInvalidDocument,
                format!("OpenAPI field {field} was not an array"),
            )
        })
}

pub fn merge_named_values(
    target: &mut Map<String, Value>,
    source: &Map<String, Value>,
    context: &str,
) -> CodegenResult<()> {
    for (key, value) in source {
        match target.get(key) {
            Some(existing) if existing != value => {
                return Err(UniError::from_kind_context(
                    CodegenErrKind::OpenApiMergeConflict,
                    format!("conflicting OpenAPI key {key:?} while merging {context}"),
                ));
            }
            Some(_) => {}
            None => {
                target.insert(key.clone(), value.clone());
            }
        }
    }
    Ok(())
}