planus-codegen 1.2.0

Internal codegen library for planus.
Documentation
use std::{
    borrow::Cow,
    collections::{BTreeMap, HashSet},
};

use planus_types::{ast, intermediate::*};

use crate::backend_translation::BackendVariant;

pub type Keywords = HashSet<&'static str>;

pub struct Names<'keywords> {
    keywords: &'keywords Keywords,
    names: BTreeMap<&'static str, HashSet<String>>,
}

pub struct NamespaceNames<'a, 'keywords> {
    pub _global_names: &'a mut Names<'keywords>,
    pub namespace_names: &'a mut Names<'keywords>,
}

pub struct DeclarationNames<'a, 'keywords> {
    pub _global_names: &'a mut Names<'keywords>,
    pub _namespace_names: &'a mut Names<'keywords>,
    pub declaration_names: &'a mut Names<'keywords>,
}

impl<'keywords> Names<'keywords> {
    pub fn new(keywords: &'keywords Keywords) -> Self {
        Self {
            keywords,
            names: Default::default(),
        }
    }

    pub fn try_reserve(&mut self, binding_kind: &'static str, value: &str) -> bool {
        if value.is_empty() || self.keywords.contains(value) {
            false
        } else {
            let names = self.names.entry(binding_kind).or_default();
            if names.contains(value) {
                false
            } else {
                names.insert(value.to_string());
                true
            }
        }
    }

    pub fn try_reserve_repeat<'a>(
        &mut self,
        binding_kind: &'static str,
        value: Cow<'a, str>,
        padding: char,
    ) -> Cow<'a, str> {
        if self.try_reserve(binding_kind, &value) {
            return value;
        }

        let mut value = format!("{value}{padding}");
        while !self.try_reserve(binding_kind, &value) {
            value.push(padding);
        }

        value.into()
    }
}

pub enum DeclInfo<'a, B: ?Sized + Backend> {
    Table(B::TableInfo, &'a Table),
    Struct(B::StructInfo, &'a Struct),
    Enum(B::EnumInfo, &'a Enum),
    Union(B::UnionInfo, &'a Union),
    RpcService(B::RpcServiceInfo, &'a RpcService),
}

impl<B: ?Sized + Backend> Clone for DeclInfo<'_, B> {
    fn clone(&self) -> Self {
        match self {
            Self::Table(translated_decl, decl) => Self::Table(translated_decl.clone(), decl),
            Self::Struct(translated_decl, decl) => Self::Struct(translated_decl.clone(), decl),
            Self::Enum(translated_decl, decl) => Self::Enum(translated_decl.clone(), decl),
            Self::Union(translated_decl, decl) => Self::Union(translated_decl.clone(), decl),
            Self::RpcService(translated_decl, decl) => {
                Self::RpcService(translated_decl.clone(), decl)
            }
        }
    }
}

pub struct RelativeNamespace<'a, B: ?Sized + Backend> {
    pub ascend_count: usize,
    pub path: Vec<&'a B::NamespaceInfo>,
}

impl<'a, B: ?Sized + Backend> RelativeNamespace<'a, B> {
    pub fn new(
        current_namespace: &AbsolutePath,
        other_namespace: &AbsolutePath,
        translated_namespaces: &'a [B::NamespaceInfo],
        declarations: &'a Declarations,
    ) -> RelativeNamespace<'a, B> {
        let mut current_namespace = current_namespace.0.iter().peekable();
        let mut other_namespace = other_namespace.0.iter().peekable();
        let mut shared = AbsolutePath(Vec::new());

        while current_namespace.peek().is_some()
            && current_namespace.peek() == other_namespace.peek()
        {
            shared.0.push(current_namespace.next().unwrap().clone());
            other_namespace.next();
        }
        let index = declarations.namespaces.get_index_of(&shared).unwrap();
        let mut path = vec![&translated_namespaces[index]];

        for remaining in other_namespace {
            shared.0.push(remaining.clone());
            let index = declarations.namespaces.get_index_of(&shared).unwrap();
            path.push(&translated_namespaces[index]);
        }

        RelativeNamespace {
            ascend_count: current_namespace.count(),
            path,
        }
    }
}

struct FormattedRelativeNamespace<'a, B: ?Sized + Backend, F> {
    super_name: &'a str,
    self_name: Option<&'a str>,
    separator: &'a str,
    value: &'a RelativeNamespace<'a, B>,
    output_shared_ancestor: bool,
    name: F,
    trailing_part: &'a str,
}

impl<'a, B: ?Sized + Backend> RelativeNamespace<'a, B> {
    pub fn format(
        &'a self,
        output_shared_ancestor: bool,
        super_name: &'a str,
        self_name: Option<&'a str>,
        separator: &'a str,
        name: impl 'a + Fn(&B::NamespaceInfo) -> &str,
        trailing_part: &'a str,
    ) -> impl 'a + std::fmt::Display {
        FormattedRelativeNamespace {
            super_name,
            self_name,
            separator,
            output_shared_ancestor,
            value: self,
            name,
            trailing_part,
        }
    }
}

impl<B: ?Sized + Backend, F: Fn(&B::NamespaceInfo) -> &str> std::fmt::Display
    for FormattedRelativeNamespace<'_, B, F>
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        for _ in 0..self.value.ascend_count {
            write!(f, "{}{}", self.super_name, self.separator)?;
        }
        if self.value.ascend_count == 0 {
            if let Some(self_name) = self.self_name {
                write!(f, "{self_name}")?;
            }
        }
        #[allow(clippy::bool_to_int_with_if)]
        let skip = if self.output_shared_ancestor { 0 } else { 1 };
        let mut has_output = false;
        for (i, info) in self.value.path.iter().skip(skip).enumerate() {
            if i > 0 {
                write!(f, "{}{}", self.separator, (self.name)(info))?;
            } else {
                write!(f, "{}", (self.name)(info))?;
            }
            has_output = true;
        }
        if !self.trailing_part.is_empty() {
            if has_output {
                write!(f, "{}{}", self.separator, self.trailing_part)?;
            } else {
                write!(f, "{}", self.trailing_part)?;
            }
        }

        Ok(())
    }
}

pub enum ResolvedType<'a, B: ?Sized + Backend> {
    Struct(
        DeclarationIndex,
        &'a Struct,
        &'a B::StructInfo,
        RelativeNamespace<'a, B>,
    ),
    #[allow(dead_code)]
    Table(
        DeclarationIndex,
        &'a Table,
        &'a B::TableInfo,
        RelativeNamespace<'a, B>,
    ),
    #[allow(dead_code)]
    Enum(
        DeclarationIndex,
        &'a Enum,
        &'a B::EnumInfo,
        RelativeNamespace<'a, B>,
        &'a [BackendVariant<B::EnumVariantInfo>],
    ),
    #[allow(dead_code)]
    Union(
        DeclarationIndex,
        &'a Union,
        &'a B::UnionInfo,
        RelativeNamespace<'a, B>,
    ),
    Vector(Box<ResolvedType<'a, B>>),
    Array(Box<ResolvedType<'a, B>>, u32),
    String,
    Bool,
    Integer(ast::IntegerType),
    Float(ast::FloatType),
}

pub struct DeclarationTranslationContext<'a, 'keywords, B: ?Sized + Backend> {
    pub declaration_names: DeclarationNames<'a, 'keywords>,
    pub translated_namespaces: &'a [B::NamespaceInfo],
    pub translated_decls: &'a [(AbsolutePath, DeclInfo<'a, B>)],
}

pub trait Backend {
    type NamespaceInfo: std::fmt::Debug + Clone;
    type TableInfo: std::fmt::Debug + Clone;
    type StructInfo: std::fmt::Debug + Clone;
    type EnumInfo: std::fmt::Debug + Clone;
    type UnionInfo: std::fmt::Debug + Clone;
    type RpcServiceInfo: std::fmt::Debug + Clone;
    type TableFieldInfo: std::fmt::Debug + Clone;
    type StructFieldInfo: std::fmt::Debug + Clone;
    type EnumVariantInfo: std::fmt::Debug + Clone;
    type UnionVariantInfo: std::fmt::Debug + Clone;
    type RpcMethodInfo: std::fmt::Debug + Clone;

    const KEYWORDS: &'static [&'static str];

    fn generate_namespace(
        &mut self,
        namespace_names: &mut NamespaceNames<'_, '_>,
        namespace_name: &AbsolutePath,
        namespace: &Namespace,
    ) -> Self::NamespaceInfo;

    fn generate_table(
        &mut self,
        declaration_names: &mut DeclarationNames<'_, '_>,
        translated_namespaces: &[Self::NamespaceInfo],
        decl_id: DeclarationIndex,
        decl_name: &AbsolutePath,
        decl: &Table,
    ) -> Self::TableInfo;

    fn generate_struct(
        &mut self,
        declaration_names: &mut DeclarationNames<'_, '_>,
        translated_namespaces: &[Self::NamespaceInfo],
        decl_id: DeclarationIndex,
        decl_name: &AbsolutePath,
        decl: &Struct,
    ) -> Self::StructInfo;

    fn generate_enum(
        &mut self,
        declaration_names: &mut DeclarationNames<'_, '_>,
        translated_namespaces: &[Self::NamespaceInfo],
        decl_id: DeclarationIndex,
        decl_name: &AbsolutePath,
        decl: &Enum,
    ) -> Self::EnumInfo;

    fn generate_union(
        &mut self,
        declaration_names: &mut DeclarationNames<'_, '_>,
        translated_namespaces: &[Self::NamespaceInfo],
        decl_id: DeclarationIndex,
        decl_name: &AbsolutePath,
        decl: &Union,
    ) -> Self::UnionInfo;

    fn generate_table_field(
        &mut self,
        translation_context: &mut DeclarationTranslationContext<'_, '_, Self>,
        parent_info: &Self::TableInfo,
        parent: &Table,
        field_name: &str,
        field: &TableField,
        resolved_type: ResolvedType<'_, Self>,
    ) -> Self::TableFieldInfo;

    fn generate_struct_field(
        &mut self,
        translation_context: &mut DeclarationTranslationContext<'_, '_, Self>,
        parent_info: &Self::StructInfo,
        parent: &Struct,
        field_name: &str,
        field: &StructField,
        resolved_type: ResolvedType<'_, Self>,
    ) -> Self::StructFieldInfo;

    fn generate_enum_variant(
        &mut self,
        translation_context: &mut DeclarationTranslationContext<'_, '_, Self>,
        parent_info: &Self::EnumInfo,
        parent: &Enum,
        key: &str,
        value: &IntegerLiteral,
    ) -> Self::EnumVariantInfo;

    #[allow(clippy::too_many_arguments)]
    fn generate_union_variant(
        &mut self,
        translation_context: &mut DeclarationTranslationContext<'_, '_, Self>,
        parent_info: &Self::UnionInfo,
        parent: &Union,
        key: &str,
        index: u8,
        value: &UnionVariant,
        resolved_type: ResolvedType<'_, Self>,
    ) -> Self::UnionVariantInfo;
}