sim-run-core 0.1.7

Core command entry API for the SIM bootloader.
Documentation
use std::collections::BTreeMap;

use sim_kernel::{
    Cx, Error as KernelError, Lib, LibBootDependency, LibLoader, LibManifest,
    LibSource as KernelLibSource, LoadedLib,
};

use crate::{CliError, LibSourceSpec, LoadReceipt, LoadReceiptRole, RuntimeConfigState};

type PlainHostFactory = Box<dyn Fn() -> Box<dyn Lib> + Send + Sync>;
type ConfigHostFactory = Box<dyn Fn(&RuntimeConfigState) -> Box<dyn Lib> + Send + Sync>;

enum HostFactory {
    Plain(PlainHostFactory),
    Config(ConfigHostFactory),
}

#[derive(Default)]
pub(crate) struct HostLibRegistry {
    factories: BTreeMap<String, HostFactory>,
}

impl HostLibRegistry {
    pub(crate) fn add(
        &mut self,
        name: impl Into<String>,
        factory: impl Fn() -> Box<dyn Lib> + Send + Sync + 'static,
    ) {
        self.factories
            .insert(name.into(), HostFactory::Plain(Box::new(factory)));
    }

    pub(crate) fn add_with_config(
        &mut self,
        name: impl Into<String>,
        factory: impl Fn(&RuntimeConfigState) -> Box<dyn Lib> + Send + Sync + 'static,
    ) {
        self.factories
            .insert(name.into(), HostFactory::Config(Box::new(factory)));
    }

    pub(crate) fn instantiate(
        &self,
        name: &str,
        config: &RuntimeConfigState,
    ) -> Result<Box<dyn Lib>, CliError> {
        self.factories
            .get(name)
            .map(|factory| match factory {
                HostFactory::Plain(factory) => factory(),
                HostFactory::Config(factory) => factory(config),
            })
            .ok_or_else(|| CliError::new(format!("unknown host library: {name}")))
    }

    pub(crate) fn inspect_manifest(
        &self,
        name: &str,
        config: &RuntimeConfigState,
    ) -> Result<LibManifest, CliError> {
        Ok(self.instantiate(name, config)?.manifest())
    }

    pub(crate) fn contains(&self, name: &str) -> bool {
        self.factories.contains_key(name)
    }
}

pub(crate) struct HostSourceLoader;

impl LibLoader for HostSourceLoader {
    fn can_load(&self, source: &KernelLibSource) -> bool {
        matches!(source, KernelLibSource::Host(_))
    }

    fn load(&self, _cx: &mut Cx, source: KernelLibSource) -> sim_kernel::Result<Box<dyn Lib>> {
        match source {
            KernelLibSource::Host(lib) => Ok(lib),
            _ => Err(KernelError::Lib(
                "host loader received a non-host source".to_owned(),
            )),
        }
    }
}

pub(crate) fn host_receipt(
    source: LibSourceSpec,
    role: LoadReceiptRole,
    loaded: LoadedLib,
    loaded_libs: &[LoadedLib],
) -> LoadReceipt {
    let dependencies = loaded
        .manifest
        .requires
        .iter()
        .filter_map(|dependency| {
            let loaded = loaded_libs
                .iter()
                .find(|candidate| candidate.manifest.id == dependency.id)?;
            Some(LibBootDependency {
                lib_id: loaded.id,
                symbol: loaded.manifest.id.clone(),
            })
        })
        .collect();
    LoadReceipt {
        lib_id: loaded.id,
        role,
        requested_source: source.clone(),
        resolved_source: source,
        manifest: loaded.manifest,
        dependencies,
        exports: loaded.exports,
    }
}