teo-parser 0.3.0

Parser for Teo schema language
Documentation
use maplit::btreemap;
use crate::availability::Availability;
use crate::ast::handler::{HandlerDeclaration, HandlerGroupDeclaration, HandlerInputFormat};
use crate::ast::model::Model;
use crate::ast::reference_space::ReferenceSpace;
use crate::ast::span::Span;
use crate::r#type::keyword::Keyword;
use crate::r#type::r#type::Type;
use crate::r#type::reference::Reference;
use crate::resolver::resolve_decorator::resolve_decorator;
use crate::resolver::resolve_type_expr::resolve_type_expr;
use crate::resolver::resolver_context::ResolverContext;
use crate::traits::node_trait::NodeTrait;
use crate::traits::resolved::Resolve;

pub(super) fn resolve_handler_group_references<'a>(
    handler_group: &'a HandlerGroupDeclaration,
    context: &'a ResolverContext<'a>
) {
    if context.has_examined_default_path(&handler_group.string_path, Availability::default()) {
        context.insert_duplicated_identifier(handler_group.identifier().span);
    }
    for handler_declaration in handler_group.handler_declarations() {
        resolve_handler_declaration_types(handler_declaration, context, None)
    }
    context.add_examined_default_path(handler_group.string_path.clone(), Availability::default());
}

pub(super) fn resolve_handler_group_decorators<'a>(
    handler_group: &'a HandlerGroupDeclaration,
    context: &'a ResolverContext<'a>
) {
    for handler_declaration in handler_group.handler_declarations() {
        resolve_handler_declaration_decorators(handler_declaration, context, None)
    }
}

pub(super) fn resolve_handler_declaration_types<'a>(
    handler_declaration: &'a HandlerDeclaration,
    context: &'a ResolverContext<'a>,
    model: Option<&'a Model>,
) {
    if context.has_examined_default_path(&handler_declaration.string_path, Availability::default()) {
        context.insert_duplicated_identifier(handler_declaration.identifier().span);
    }
    context.add_examined_default_path(handler_declaration.string_path.clone(), Availability::default());
    let mut keywords_map = btreemap! {};
    if let Some(model) = model {
        keywords_map.insert(Keyword::SelfIdentifier, Type::ModelObject(Reference::new(model.path.clone(), model.string_path.clone())));
    }
    if let Some(input_type) = handler_declaration.input_type() {
        resolve_type_expr(input_type, &vec![], &vec![], &keywords_map, context, context.current_availability());
    }
    resolve_type_expr(handler_declaration.output_type(), &vec![], &vec![], &keywords_map, context, context.current_availability());
    if let Some(input_type) = handler_declaration.input_type() {
        match handler_declaration.input_format {
            HandlerInputFormat::Form => validate_handler_related_types(input_type.resolved(), input_type.span(), context, is_valid_form_input_type),
            HandlerInputFormat::Json => validate_handler_related_types(input_type.resolved(), input_type.span(), context, is_valid_json_input_type),
        }
    }
    validate_handler_related_types(&handler_declaration.output_type().resolved(), handler_declaration.output_type().span(), context, is_valid_json_output_type);
}

pub(super) fn resolve_handler_declaration_decorators<'a>(
    handler_declaration: &'a HandlerDeclaration,
    context: &'a ResolverContext<'a>,
    model: Option<&'a Model>,
) {
    let mut keywords_map = btreemap!{};
    if let Some(model) = model {
        keywords_map.insert(Keyword::SelfIdentifier, Type::ModelObject(Reference::new(model.path.clone(), model.string_path.clone())));
    }
    for decorator in handler_declaration.decorators() {
        resolve_decorator(decorator, context, &keywords_map, ReferenceSpace::HandlerDecorator);
    }
    let is_get_or_delete = if let Some(decorator) = handler_declaration.decorators().find(|d| d.identifier_path().identifiers().last().unwrap().name() == "map") {
        if let Some(argument_list) = decorator.argument_list() {
            if let Some(first_argument) = argument_list.arguments().next() {
                if first_argument.resolved().name.as_str() == "method" {
                    if let Some(value) = first_argument.value().resolved().value() {
                        if let Some(enum_variant) = value.as_interface_enum_variant() {
                            enum_variant.value.as_str() == "get" || enum_variant.value.as_str() == "delete"
                        } else {
                            false
                        }
                    } else {
                        false
                    }
                } else {
                    false
                }
            } else {
                false
            }
        } else {
            false
        }
    } else {
        false
    };
    if is_get_or_delete && handler_declaration.input_type().is_some() {
        context.insert_diagnostics_error(handler_declaration.input_type().unwrap().span(), "get or delete handler requires no input type");
    }
    if !is_get_or_delete && handler_declaration.input_type().is_none() {
        context.insert_diagnostics_error(handler_declaration.identifier().span(), "handler requires input type");
    }
}

pub(super) fn validate_handler_related_types<'a, F>(r#type: &'a Type, span: Span, context: &'a ResolverContext<'a>, f: F) where F: Fn(&Type) -> Option<&'static str> {
    match r#type {
        Type::Any => (),
        Type::Keyword(_) => (),
        Type::SynthesizedShapeReference(shape_reference) => {
            let t = shape_reference.fetch_synthesized_definition(context.schema);
            if t.is_none() {
                return ();
            }
            if let Some(shape) = t.unwrap().as_synthesized_shape() {
                for (_, t) in shape.iter() {
                    if let Some(e) = t.as_enum_variant() {
                        let enum_declaration = context.schema.find_top_by_path(e.path()).unwrap().as_enum().unwrap();
                        if enum_declaration.interface || enum_declaration.option {
                            context.insert_diagnostics_error(span, "interface or option enum is disallowed");
                            break
                        }
                    } else {
                        if let Some(msg) = f(t) {
                            context.insert_diagnostics_error(span, msg);
                            break
                        }
                    }
                }
            } else {
                context.insert_diagnostics_error(span, "handler argument type should be interface or any");
            }
        }
        Type::InterfaceObject(reference, gen) => {
            let interface = context.schema.find_top_by_path(reference.path()).unwrap().as_interface_declaration().unwrap();
            let shape = interface.shape_from_generics(gen);
            for (_, t) in shape.iter() {
                if let Some(e) = t.as_enum_variant() {
                    let enum_declaration = context.schema.find_top_by_path(e.path()).unwrap().as_enum().unwrap();
                    if enum_declaration.interface || enum_declaration.option {
                        context.insert_diagnostics_error(span, "interface or option enum is disallowed");
                        break
                    }
                } else {
                    if let Some(msg) = f(t) {
                        context.insert_diagnostics_error(span, msg);
                        break
                    }
                }
            }
        }
        Type::DeclaredSynthesizedShape(_, __) => (),
        _ => context.insert_diagnostics_error(span, "handler argument type should be interface or any"),
    }
}

pub(super) fn is_valid_form_input_type<'a>(r#type: &'a Type) -> Option<&'static str> {
    match r#type {
        Type::Any => None,
        Type::Null => None,
        Type::Bool => None,
        Type::Int => None,
        Type::Int64 => None,
        Type::Float32 => None,
        Type::Float => None,
        Type::Decimal => None,
        Type::String => None,
        Type::ObjectId => None,
        Type::Date => None,
        Type::DateTime => None,
        Type::File => None,
        Type::Array(_) => None,
        Type::Dictionary(_) => Some("invalid form handler input type: Dictionary is not supported"),
        Type::Tuple(_) => Some("invalid form handler input type: Tuple is not supported"),
        Type::Range(_) => Some("invalid form handler input type: Range is not supported"),
        Type::Union(_) => Some("invalid form handler input type: Union is not supported"),
        Type::Ignored => None,
        Type::EnumVariant(_) => None,
        Type::Model => Some("invalid form handler input type: Model is not supported"),
        Type::InterfaceObject(_, _items) => None,
        Type::FieldType(_, _) => Some("invalid form handler input type: FieldType is not supported"),
        Type::FieldName(_) => Some("invalid form handler input type: FieldReference is not supported"),
        Type::GenericItem(_) => Some("invalid form handler input type: GenericsItem is not supported"),
        Type::Optional(inner) => is_valid_form_input_type(inner.as_ref()),
        Type::Undetermined => Some("found unresolved type"),
        Type::ModelObject(_) => Some("invalid form handler input type: Object is not supported"),
        Type::Keyword(_) => Some("found keyword type"),
        Type::Regex => Some("invalid form handler input type: Regex is not supported"),
        Type::StructObject(_, _) => Some("invalid form handler input type: StructObject is not supported"),
        Type::Pipeline(_, _) => Some("invalid form handler input type: Pipeline is not supported"),
        _ => None,
    }
}

pub(super) fn is_valid_json_input_type<'a>(r#type: &'a Type) -> Option<&'static str> {
    match r#type {
        Type::Any => None,
        Type::Null => None,
        Type::Bool => None,
        Type::Int => None,
        Type::Int64 => None,
        Type::Float32 => None,
        Type::Float => None,
        Type::Decimal => None,
        Type::String => None,
        Type::ObjectId => None,
        Type::Date => None,
        Type::DateTime => None,
        Type::File => Some("invalid form handler input type: file is not supported in json input"),
        Type::Array(inner) => is_valid_json_input_type(inner.as_ref()),
        Type::Dictionary(v) => {
            if let Some(msg) = is_valid_json_input_type(v.as_ref()) {
                return Some(msg);
            }
            None
        }
        Type::DeclaredSynthesizedShape(_, _) => None,
        Type::Tuple(_) => Some("invalid handler input type: Tuple is not supported"),
        Type::Range(_) => Some("invalid handler input type: Range is not supported"),
        Type::Union(_) => Some("invalid handler input type: Union is not supported"),
        Type::Ignored => None,
        Type::EnumVariant(_) => None,
        Type::Model => Some("invalid form handler input type: Model is not supported"),
        Type::InterfaceObject(_, _) => None,
        Type::FieldType(_, _) => Some("invalid handler input type: FieldType is not supported"),
        Type::FieldName(_) => Some("invalid handler input type: FieldReference is not supported"),
        Type::GenericItem(_) => Some("invalid form handler input type: GenericsItem is not supported"),
        Type::Optional(inner) => is_valid_json_input_type(inner.as_ref()),
        Type::Undetermined => Some("found unresolved type"),
        Type::ModelObject(_) => Some("invalid handler input type: Object is not supported"),
        Type::Keyword(_) => Some("found keyword type"),
        Type::Regex => Some("invalid handler input type: Regex is not supported"),
        Type::StructObject(_, _) => Some("invalid handler input type: StructObject is not supported"),
        Type::Pipeline(_, _) => Some("invalid handler input type: Pipeline is not supported"),
        _ => None,
    }
}

pub(super) fn is_valid_json_output_type<'a>(r#type: &'a Type) -> Option<&'static str> {
    match r#type {
        Type::Any => None,
        Type::Null => None,
        Type::Bool => None,
        Type::Int => None,
        Type::Int64 => None,
        Type::Float32 => None,
        Type::Float => None,
        Type::Decimal => None,
        Type::String => None,
        Type::ObjectId => None,
        Type::Date => None,
        Type::DateTime => None,
        Type::File => Some("invalid form handler output type: file is not supported in json output"),
        Type::Array(inner) => is_valid_json_output_type(inner.as_ref()),
        Type::Dictionary(v) => {
            if let Some(msg) = is_valid_json_output_type(v.as_ref()) {
                return Some(msg);
            }
            None
        }
        Type::Tuple(_) => Some("invalid handler output type: Tuple is not supported"),
        Type::Range(_) => Some("invalid handler output type: Range is not supported"),
        Type::Union(_) => Some("invalid handler output type: Union is not supported"),
        Type::Ignored => None,
        Type::EnumVariant(_) => None,
        Type::Model => Some("invalid form handler output type: Model is not supported"),
        Type::InterfaceObject(_, _) => None,
        Type::FieldType(_, _) => Some("invalid handler output type: FieldType is not supported"),
        Type::FieldName(_) => Some("invalid handler output type: FieldReference is not supported"),
        Type::GenericItem(_) => Some("invalid form handler output type: GenericsItem is not supported"),
        Type::Optional(inner) => is_valid_json_output_type(inner.as_ref()),
        Type::Undetermined => Some("found unresolved type"),
        Type::ModelObject(_) => Some("invalid handler output type: Object is not supported"),
        Type::Keyword(_) => Some("found keyword type"),
        Type::Regex => Some("invalid handler output type: Regex is not supported"),
        Type::StructObject(_, _) => Some("invalid handler output type: StructObject is not supported"),
        Type::Pipeline(_, _) => Some("invalid handler output type: Pipeline is not supported"),
        Type::DeclaredSynthesizedShape(_, _) => None,
        _ => None,
    }
}