sigmd 0.1.0

Windows API signature metadata
Documentation
//! Interface building.

use sigmd::model::Interface;
use uuid::Uuid;

use super::{BuildContext, Error, Score, clang, function::build_function, ty::strip_modifiers};

/// Per-TU interface ready for cross-TU dedup arbitration.
#[derive(Debug)]
pub struct ScoredInterface {
    /// Parsed interface.
    pub interface: Interface,

    /// Arbitration weight for dedup.
    pub score: Score,
}

/// Builds a [`ScoredInterface`] from a `StructDecl` or `ClassDecl` cursor.
pub fn build_interface(
    cursor: clang::Entity<'_>,
    ctx: &BuildContext,
) -> Result<ScoredInterface, Error> {
    let name = match cursor.get_name() {
        Some(name) => name,
        None => {
            return Err(Error::UndefinedProperty {
                property: "name",
                entity: format!("{:?}", cursor.get_kind()),
            });
        }
    };

    let mut annotation = None;
    let mut base = None;
    let mut method_scores = Vec::new();
    let mut methods = Vec::new();

    for child in cursor.get_children() {
        match child.get_kind() {
            clang::EntityKind::AnnotateAttr if annotation.is_none() => {
                annotation = child.get_name();
            }
            clang::EntityKind::BaseSpecifier if base.is_none() => {
                base = child
                    .get_name()
                    .map(|spelling| strip_modifiers(&spelling).to_string());
            }
            clang::EntityKind::Method => match build_function(child, ctx) {
                Ok(scored) => {
                    method_scores.push(scored.score);
                    methods.push(scored.function);
                }
                Err(err) => {
                    tracing::trace!(%err, name, "skipping non-method decl");
                }
            },
            _ => {}
        }
    }

    let annotation = match annotation {
        Some(annotation) => annotation,
        None => {
            return Err(Error::UndefinedProperty {
                property: "annotation",
                entity: name.clone(),
            });
        }
    };

    let uuid_text = match annotation.strip_prefix("__UUID|") {
        Some(uuid_text) => uuid_text.to_lowercase(),
        None => {
            return Err(Error::UndefinedProperty {
                property: "uuid annotation",
                entity: name.clone(),
            });
        }
    };

    let uuid = Uuid::try_from(uuid_text.as_str())?;

    let interface = Interface::builder()
        .name(name)
        .uuid(uuid)
        .maybe_base(base)
        .methods(methods)
        .build();

    let score = interface_score(&method_scores);

    Ok(ScoredInterface { interface, score })
}

/// Computes the interface score.
fn interface_score(method_scores: &[Score]) -> Score {
    Score(method_scores.iter().map(|score| score.0 + 1).sum())
}