systemprompt-extension 0.2.1

Compile-time extension framework for systemprompt.io AI governance infrastructure. Built on the inventory crate — registers schemas, API routes, jobs, and providers in the MCP governance pipeline.
Documentation
use super::{ExtensionRegistration, ExtensionRegistry};
use crate::error::LoaderError;
use std::sync::Arc;
use tracing::{debug, info, warn};

impl ExtensionRegistry {
    #[must_use]
    pub fn discover() -> Self {
        let mut registry = Self::new();

        debug!("Starting extension discovery via inventory");

        for ext in inventory::iter::<ExtensionRegistration> {
            let ext_arc = (ext.factory)();
            let ext_id = ext_arc.id().to_string();
            let ext_name = ext_arc.name();
            debug!(
                id = %ext_id,
                name = %ext_name,
                priority = ext_arc.priority(),
                "Discovered extension via inventory"
            );
            registry.extensions.insert(ext_id, Arc::clone(&ext_arc));
            registry.sorted_extensions.push(ext_arc);
        }

        let injected = crate::runtime_config::get_injected_extensions();
        if !injected.is_empty() {
            debug!(
                count = injected.len(),
                "Including injected extensions in discovery"
            );
            for ext in injected {
                let ext_id = ext.id().to_string();
                if registry.extensions.contains_key(&ext_id) {
                    debug!(
                        id = %ext_id,
                        "Skipping injected extension - already discovered via inventory"
                    );
                    continue;
                }
                debug!(
                    id = %ext_id,
                    name = %ext.name(),
                    priority = ext.priority(),
                    "Including injected extension"
                );
                registry.extensions.insert(ext_id, Arc::clone(&ext));
                registry.sorted_extensions.push(ext);
            }
        }

        registry.sort_by_priority();

        let extension_names: Vec<_> = registry
            .sorted_extensions
            .iter()
            .map(|e| e.name())
            .collect();

        if registry.is_empty() {
            warn!(
                "No extensions discovered via inventory. This may indicate LTO stripped the \
                 inventory registrations, or no extensions were registered with \
                 register_extension!(). Check that extension crates are linked and #[used] \
                 attributes are present if using LTO."
            );
        } else {
            info!(
                count = registry.len(),
                extensions = ?extension_names,
                "Extension discovery completed"
            );
        }

        registry
    }

    pub fn discover_and_merge(
        injected: Vec<Arc<dyn crate::Extension>>,
    ) -> Result<Self, LoaderError> {
        let mut registry = Self::discover();
        registry.merge(injected)?;
        registry.validate()?;
        Ok(registry)
    }
}