xidl-parser 0.72.0

A IDL codegen.
Documentation
use crate::hir;
use convert_case::{Case, Casing};

use super::mapping;
use super::route::{attribute_path, default_path, operation_id, readonly_attr_names};
use super::semantics::{
    DeprecatedInfo, HttpSecurityProfile, HttpStreamCodec, HttpStreamConfig, HttpStreamKind,
};
use super::{
    HttpMethod, HttpOperation, HttpOperationMeta, HttpOperationSignature, HttpOperationSource,
    HttpParam, HttpParamKind, HttpRoute, HttpSignatureParam, HttpSignatureParamAnnotation,
    HttpSignatureParamDirection,
};

#[cfg(test)]
mod tests;

pub(super) fn project_attribute(
    interface_name: &str,
    module_path: &[String],
    attr: &hir::AttrDcl,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
    emit_watch: bool,
) -> Vec<HttpOperation> {
    let mut operations = Vec::new();
    match &attr.decl {
        hir::AttrDclInner::ReadonlyAttrSpec(spec) => {
            for raw_name in readonly_attr_names(spec) {
                operations.push(attribute_get_operation(
                    interface_name,
                    module_path,
                    &raw_name,
                    &spec.ty,
                    security.clone(),
                    deprecated.clone(),
                ));
                if emit_watch {
                    operations.push(attribute_watch_operation(
                        interface_name,
                        module_path,
                        &raw_name,
                        &spec.ty,
                        security.clone(),
                        deprecated.clone(),
                    ));
                }
            }
        }
        hir::AttrDclInner::AttrSpec(spec) => match &spec.declarator {
            hir::AttrDeclarator::SimpleDeclarator(values) => {
                for decl in values {
                    operations.extend(attribute_rw_operations(
                        interface_name,
                        module_path,
                        &decl.0,
                        &spec.ty,
                        security.clone(),
                        deprecated.clone(),
                        emit_watch,
                    ));
                }
            }
            hir::AttrDeclarator::WithRaises { declarator, .. } => {
                operations.extend(attribute_rw_operations(
                    interface_name,
                    module_path,
                    &declarator.0,
                    &spec.ty,
                    security,
                    deprecated,
                    emit_watch,
                ));
            }
        },
    }
    operations
}

fn attribute_rw_operations(
    interface_name: &str,
    module_path: &[String],
    raw_name: &str,
    ty: &hir::TypeSpec,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
    emit_watch: bool,
) -> Vec<HttpOperation> {
    let mut ops = vec![
        attribute_get_operation(
            interface_name,
            module_path,
            raw_name,
            ty,
            security.clone(),
            deprecated.clone(),
        ),
        attribute_set_operation(
            interface_name,
            module_path,
            raw_name,
            ty,
            security.clone(),
            deprecated.clone(),
        ),
    ];
    if emit_watch {
        ops.push(attribute_watch_operation(
            interface_name,
            module_path,
            raw_name,
            ty,
            security,
            deprecated,
        ));
    }
    ops
}

fn attribute_get_operation(
    interface_name: &str,
    module_path: &[String],
    raw_name: &str,
    ty: &hir::TypeSpec,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
) -> HttpOperation {
    let name = format!("get_attribute_{raw_name}");
    let signature = HttpOperationSignature {
        params: Vec::new(),
        return_type: Some(ty.clone()),
    };
    base_attribute_operation(AttributeOperationArgs {
        interface_name,
        module_path,
        name: &name,
        source: HttpOperationSource::AttributeGet,
        method: HttpMethod::Get,
        routes: vec![HttpRoute {
            path: attribute_path(raw_name),
            path_params: Vec::new(),
            query_params: Vec::new(),
        }],
        stream: HttpStreamConfig {
            kind: None,
            codec: HttpStreamCodec::Ndjson,
        },
        security,
        deprecated,
        request_params: Vec::new(),
        response_params: Vec::new(),
        return_type: Some(ty.clone()),
        signature,
    })
}

fn attribute_set_operation(
    interface_name: &str,
    module_path: &[String],
    raw_name: &str,
    ty: &hir::TypeSpec,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
) -> HttpOperation {
    let name = format!("set_attribute_{raw_name}");
    let param_name = raw_name.to_case(Case::Snake);
    let value = HttpParam {
        name: param_name.clone(),
        wire_name: raw_name.to_string(),
        ty: ty.clone(),
        kind: HttpParamKind::Body,
        optional: false,
        flatten: false,
    };
    let signature = HttpOperationSignature {
        params: vec![HttpSignatureParam {
            name: param_name,
            ty: ty.clone(),
            direction: HttpSignatureParamDirection::In,
            is_optional: false,
            is_flatten: false,
            annotations: vec![HttpSignatureParamAnnotation::Body],
        }],
        return_type: None,
    };
    base_attribute_operation(AttributeOperationArgs {
        interface_name,
        module_path,
        name: &name,
        source: HttpOperationSource::AttributeSet,
        method: HttpMethod::Post,
        routes: vec![HttpRoute {
            path: attribute_path(raw_name),
            path_params: Vec::new(),
            query_params: Vec::new(),
        }],
        stream: HttpStreamConfig {
            kind: None,
            codec: HttpStreamCodec::Ndjson,
        },
        security,
        deprecated,
        request_params: vec![value],
        response_params: Vec::new(),
        return_type: None,
        signature,
    })
}

fn attribute_watch_operation(
    interface_name: &str,
    module_path: &[String],
    raw_name: &str,
    ty: &hir::TypeSpec,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
) -> HttpOperation {
    let name = format!("watch_attribute_{raw_name}");
    let signature = HttpOperationSignature {
        params: Vec::new(),
        return_type: Some(ty.clone()),
    };
    base_attribute_operation(AttributeOperationArgs {
        interface_name,
        module_path,
        name: &name,
        source: HttpOperationSource::AttributeWatch,
        method: HttpMethod::Get,
        routes: vec![HttpRoute {
            path: default_path(module_path, interface_name, &name),
            path_params: Vec::new(),
            query_params: Vec::new(),
        }],
        stream: HttpStreamConfig {
            kind: Some(HttpStreamKind::Server),
            codec: HttpStreamCodec::Sse,
        },
        security,
        deprecated,
        request_params: Vec::new(),
        response_params: Vec::new(),
        return_type: Some(ty.clone()),
        signature,
    })
}

fn base_attribute_operation(args: AttributeOperationArgs<'_>) -> HttpOperation {
    let AttributeOperationArgs {
        interface_name,
        module_path,
        name,
        source,
        method,
        routes,
        stream,
        security,
        deprecated,
        request_params,
        response_params,
        return_type,
        signature,
    } = args;

    let request_content_type = "application/json".to_string();
    let response_content_type = "application/json".to_string();

    let http = mapping::build_http_mapping(
        method,
        &stream,
        &request_content_type,
        &response_content_type,
        &request_params,
        &response_params,
        &return_type,
    );

    HttpOperation {
        meta: HttpOperationMeta {
            name: name.to_string(),
            operation_id: operation_id(module_path, interface_name, name),
            source,
            method,
            routes,
            stream,
            cors: None,
            security,
            basic_auth_realm: None,
            deprecated,
            upgrade_protocol: None,
        },
        signature,
        http,
    }
}

struct AttributeOperationArgs<'a> {
    interface_name: &'a str,
    module_path: &'a [String],
    name: &'a str,
    source: HttpOperationSource,
    method: HttpMethod,
    routes: Vec<HttpRoute>,
    stream: HttpStreamConfig,
    security: Option<HttpSecurityProfile>,
    deprecated: Option<DeprecatedInfo>,
    request_params: Vec<HttpParam>,
    response_params: Vec<HttpParam>,
    return_type: Option<hir::TypeSpec>,
    signature: HttpOperationSignature,
}