knave-expand 0.13.2

The reading, scoping and expansion stages of knave
Documentation
use super::Expander;
use super::problems::ExpandProblems;
use super::shape::Shape;
use crate::core::{ParsedTemplate, RustClause, TypeBinder, TypeExpr, TypeExprKind};
use crate::diagnostic::Diagnostic;
use crate::reader::Form;
use crate::source::Span;

pub(super) struct ClausedTemplate<'t> {
    pub parsed: &'t ParsedTemplate,
    pub span: Span,
    pub type_expr: &'t TypeExpr,
    pub type_span: Span,
}

impl Expander<'_> {
    pub(super) const RUST_PARTS: usize = 3;

    pub(super) fn lower_rust_clauses(
        &mut self,
        shape: &Shape,
        template: &ClausedTemplate,
        clauses: &[Form],
    ) -> Result<Vec<RustClause>, Diagnostic> {
        let pairs = clauses
            .iter()
            .map(|clause| Self::clause_pair(shape, clause))
            .collect::<Result<Vec<_>, Diagnostic>>()?;
        let parameters = Self::type_parameters(template.type_expr).count();
        if !pairs.is_empty() && parameters != 1 {
            return Err(ExpandProblems::rust_clauses_need_one_parameter(
                parameters,
                template.type_span,
            ));
        }
        pairs
            .into_iter()
            .map(|pair| self.lower_rust_clause(shape, template, pair))
            .collect()
    }

    fn clause_pair<'f>(shape: &Shape, clause: &'f Form) -> Result<[&'f Form; 2], Diagnostic> {
        match clause.as_list() {
            Some([target, text]) => Ok([target, text]),
            _ => Err(shape.malformed(clause)),
        }
    }

    fn lower_rust_clause(
        &mut self,
        shape: &Shape,
        template: &ClausedTemplate,
        [target, text]: [&Form; 2],
    ) -> Result<RustClause, Diagnostic> {
        let parsed = Self::parse_rust_template(shape.string(text)?, text.span())?;
        self.check_template_type_placeholders(&parsed, template.type_expr, text.span())?;
        if let Some(difference) = template.parsed.passing_difference(&parsed) {
            return Err(ExpandProblems::clause_passes_arguments_unlike_template(
                difference,
                text.span(),
                template.span,
            ));
        }
        Ok(RustClause {
            target: self.lower_type(target)?,
            template: parsed.text().into(),
        })
    }

    pub(super) fn parse_rust_template(
        text: &str,
        span: Span,
    ) -> Result<ParsedTemplate, Diagnostic> {
        ParsedTemplate::parse(text).map_err(|_| ExpandProblems::malformed_template(span))
    }

    pub(super) fn where_binders(type_expr: &TypeExpr) -> &[TypeBinder] {
        match type_expr.kind() {
            TypeExprKind::Where(binders, _) => binders.as_slice(),
            _ => &[],
        }
    }

    fn type_parameters(type_expr: &TypeExpr) -> impl Iterator<Item = &TypeBinder> {
        Self::where_binders(type_expr)
            .iter()
            .filter(|binder| !binder.lifetime)
    }
}