Skip to content

fidius-cli::commands Rust

Functions

fidius-cli::commands::resolve_dep

private

fn resolve_dep (value : & str , version_override : Option < & str >) -> String

Resolve a dependency string to a Cargo.toml dependency value.

Logic: 1. If value is a path that exists on disk → { path = "..." } 2. If version_override is set → "<version>" 3. Check crates.io for value → if found, use latest version 4. Warn and fall back to { path = "<value>" }

Source
fn resolve_dep(value: &str, version_override: Option<&str>) -> String {
    // Check if it's a filesystem path
    if Path::new(value).exists() {
        // Canonicalize so the path works from any crate directory
        if let Ok(abs) = std::fs::canonicalize(value) {
            return format!("{{ path = \"{}\" }}", abs.display());
        }
        return format!("{{ path = \"{}\" }}", value);
    }

    // If version explicitly pinned, use it
    if let Some(ver) = version_override {
        return format!("\"{}\"", ver);
    }

    // Try crates.io
    if let Some(ver) = check_crates_io(value) {
        return format!("\"{}\"", ver);
    }

    // Warn and fall back to path dep
    eprintln!(
        "warning: could not find '{}' as a local path or on crates.io, using path dep",
        value
    );
    format!("{{ path = \"{}\" }}", value)
}

fidius-cli::commands::check_crates_io

private

fn check_crates_io (name : & str) -> Option < String >

Check crates.io for a crate and return its latest version, if found.

Source
fn check_crates_io(name: &str) -> Option<String> {
    let url = format!("https://crates.io/api/v1/crates/{}", name);
    let mut response = ureq::get(&url)
        .header(
            "User-Agent",
            "fidius-cli (https://github.com/colliery-io/fidius)",
        )
        .call()
        .ok()?;

    let body_str = response.body_mut().read_to_string().ok()?;
    let body: serde_json::Value = serde_json::from_str(&body_str).ok()?;
    body["crate"]["max_stable_version"]
        .as_str()
        .map(String::from)
}

fidius-cli::commands::init_interface

pub

fn init_interface (name : & str , trait_name : & str , path : Option < & Path > , version : Option < & str > , extension : Option < & str > ,) -> Result
Source
pub fn init_interface(
    name: &str,
    trait_name: &str,
    path: Option<&Path>,
    version: Option<&str>,
    extension: Option<&str>,
) -> Result {
    let base = path.unwrap_or_else(|| Path::new("."));
    let crate_dir = base.join(name);

    if crate_dir.exists() {
        return Err(format!("directory '{}' already exists", crate_dir.display()).into());
    }

    let src_dir = crate_dir.join("src");
    std::fs::create_dir_all(&src_dir)?;

    // Resolve fidius dependency
    let fidius_dep = resolve_dep("fidius", version);

    let cargo_toml = format!(
        r#"[package]
name = "{name}"
version = "0.1.0"
edition = "2021"

[features]
# Enable host-side typed Client generation ({trait_name}Client).
# Plugin cdylibs (consumers of this crate via `{interface_crate_name}`
# default features) do NOT enable this. Host applications depend on this
# crate with `features = ["host"]` to receive the Client type.
host = ["fidius/host"]

[dependencies]
fidius = {fidius_dep}
"#,
        interface_crate_name = name,
    );

    let lib_rs = format!(
        r#"pub use fidius::{{plugin_impl, PluginError}};

#[fidius::plugin_interface(version = 1, buffer = PluginAllocated)]
pub trait {trait_name}: Send + Sync {{
    fn process(&self, input: String) -> String;
}}
"#
    );

    std::fs::write(crate_dir.join("Cargo.toml"), cargo_toml)?;
    std::fs::write(src_dir.join("lib.rs"), lib_rs)?;

    // Write fidius.toml with interface metadata (extension, etc.)
    if let Some(ext) = extension {
        let fidius_toml = format!("extension = \"{ext}\"\n");
        std::fs::write(crate_dir.join("fidius.toml"), fidius_toml)?;
    }

    println!("Created interface crate: {}", crate_dir.display());
    Ok(())
}

fidius-cli::commands::init_plugin

pub

fn init_plugin (name : & str , interface : & str , trait_name : & str , path : Option < & Path > , version : Option < & str > ,) -> Result
Source
pub fn init_plugin(
    name: &str,
    interface: &str,
    trait_name: &str,
    path: Option<&Path>,
    version: Option<&str>,
) -> Result {
    let base = path.unwrap_or_else(|| Path::new("."));
    let crate_dir = base.join(name);

    if crate_dir.exists() {
        return Err(format!("directory '{}' already exists", crate_dir.display()).into());
    }

    let src_dir = crate_dir.join("src");
    std::fs::create_dir_all(&src_dir)?;

    // Resolve dependencies
    let interface_dep = resolve_dep(interface, version);
    let fidius_dep = resolve_dep("fidius", version);

    // Extract the crate name from the interface value (strip path components)
    let interface_crate = Path::new(interface)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or(interface);

    // Convert crate name to Rust identifier (hyphens → underscores)
    let interface_mod = interface_crate.replace('-', "_");

    let fidius_test_dep = resolve_dep("fidius-test", version);

    let interface_dev_dep = if interface_dep.trim_start().starts_with('{') {
        let inner = interface_dep
            .trim_start_matches('{')
            .trim_end_matches('}')
            .trim();
        format!("{{ {inner}, features = [\"host\"] }}")
    } else {
        let ver = interface_dep.trim_matches('"');
        format!("{{ version = \"{ver}\", features = [\"host\"] }}")
    };

    let cargo_toml = format!(
        r#"[package]
name = "{name}"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
{interface_crate} = {interface_dep}
fidius = {fidius_dep}

[dev-dependencies]
# Re-declare the interface with the `host` feature enabled so the generated
# {trait_name}Client is available in #[cfg(test)] code. The cdylib build
# uses the non-dev dep above, so libloading stays out of the plugin.
{interface_crate} = {interface_dev_dep}
# Fidius testing helpers (dylib_fixture, signing fixtures).
fidius-test = {fidius_test_dep}
"#
    );

    let struct_name = format!("My{trait_name}");

    let lib_rs = format!(
        r#"use {interface_mod}::{{plugin_impl, {trait_name}, PluginError, __fidius_{trait_name}}};

pub struct {struct_name};

#[plugin_impl({trait_name})]
impl {trait_name} for {struct_name} {{
    fn process(&self, input: String) -> String {{
        format!("processed: {{}}", input)
    }}
}}

fidius::fidius_plugin_registry!();

#[cfg(test)]
mod tests {{
    use {interface_mod}::{trait_name}Client;

    #[test]
    fn process_in_process() {{
        // In-process test: invokes the plugin method directly without building
        // a cdylib or loading via libloading. Fast dev loop.
        let client = {trait_name}Client::in_process("{struct_name}").expect("plugin not registered");
        let out = client.process(&"hello".to_string()).expect("call");
        assert_eq!(out, "processed: hello");
    }}
}}
"#
    );

    std::fs::write(crate_dir.join("Cargo.toml"), cargo_toml)?;
    std::fs::write(src_dir.join("lib.rs"), lib_rs)?;

    // Read interface's fidius.toml for extension (if interface is a local path)
    let interface_path = Path::new(interface);
    let extension = if interface_path.is_dir() {
        let fidius_toml_path = interface_path.join("fidius.toml");
        if fidius_toml_path.exists() {
            let content = std::fs::read_to_string(&fidius_toml_path)?;
            let table: toml::Table = content.parse().unwrap_or_default();
            table
                .get("extension")
                .and_then(|v| v.as_str())
                .map(String::from)
        } else {
            None
        }
    } else {
        None
    };

    // Generate package.toml
    let ext_line = match &extension {
        Some(ext) => format!("\nextension = \"{ext}\""),
        None => String::new(),
    };
    let package_toml = format!(
        r#"[package]
name = "{name}"
version = "0.1.0"
interface = "{interface_crate}"
interface_version = 1{ext_line}

[metadata]
"#
    );
    std::fs::write(crate_dir.join("package.toml"), package_toml)?;

    println!("Created plugin crate: {}", crate_dir.display());
    Ok(())
}

fidius-cli::commands::init_host

pub

fn init_host (name : & str , interface : & str , trait_name : & str , path : Option < & Path > , version : Option < & str > ,) -> Result
Source
pub fn init_host(
    name: &str,
    interface: &str,
    trait_name: &str,
    path: Option<&Path>,
    version: Option<&str>,
) -> Result {
    let base = path.unwrap_or_else(|| Path::new("."));
    let crate_dir = base.join(name);

    if crate_dir.exists() {
        return Err(format!("directory '{}' already exists", crate_dir.display()).into());
    }

    let src_dir = crate_dir.join("src");
    std::fs::create_dir_all(&src_dir)?;

    let interface_dep = resolve_dep(interface, version);
    let fidius_host_dep = resolve_dep("fidius-host", version);

    let interface_crate = Path::new(interface)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or(interface);
    let interface_mod = interface_crate.replace('-', "_");

    // Interface dep with `host` feature enabled to pull the typed Client.
    let interface_dep_with_host = if interface_dep.trim_start().starts_with('{') {
        // table form: inject features
        let inner = interface_dep
            .trim_start_matches('{')
            .trim_end_matches('}')
            .trim();
        format!("{{ {inner}, features = [\"host\"] }}")
    } else {
        // string version: promote to table with features
        let ver = interface_dep.trim_matches('"');
        format!("{{ version = \"{ver}\", features = [\"host\"] }}")
    };

    let client_name = format!("{trait_name}Client");

    let fidius_test_dep = resolve_dep("fidius-test", version);

    let cargo_toml = format!(
        r#"[package]
name = "{name}"
version = "0.1.0"
edition = "2021"

[dependencies]
{interface_crate} = {interface_dep_with_host}
fidius-host = {fidius_host_dep}

[dev-dependencies]
# Testing helpers — dylib_fixture, signing fixtures, etc.
# See https://docs.rs/fidius-test for the typical test patterns.
fidius-test = {fidius_test_dep}
"#
    );

    let main_rs = format!(
        r#"use {interface_mod}::{client_name};
use fidius_host::{{PluginHandle, PluginHost}};

fn main() -> Result<(), Box<dyn std::error::Error>> {{
    // Adjust search path to where your plugin .dylib/.so/.dll files live.
    let host = PluginHost::builder().search_path("./plugins").build()?;

    let plugins = host.discover()?;
    if plugins.is_empty() {{
        eprintln!("No plugins found. Build a plugin with `fidius init-plugin` + `cargo build`, ");
        eprintln!("then place the compiled cdylib into ./plugins/.");
        return Ok(());
    }}

    for info in &plugins {{
        println!(
            "Found plugin '{{}}' (interface '{{}}', version {{}})",
            info.name, info.interface_name, info.interface_version,
        );
    }}

    // Load the first plugin and call a method via the typed Client.
    let first = &plugins[0];
    let loaded = host.load(&first.name)?;
    let handle = PluginHandle::from_loaded(loaded);
    let _client = {client_name}::from_handle(handle);

    // TODO: call methods on _client, e.g.:
    //   let result = _client.process(&"hello".to_string())?;
    //   println!("plugin returned: {{}}", result);

    Ok(())
}}
"#
    );

    std::fs::write(crate_dir.join("Cargo.toml"), cargo_toml)?;
    std::fs::write(src_dir.join("main.rs"), main_rs)?;

    println!("Created host crate: {}", crate_dir.display());
    println!();
    println!("Next steps:");
    println!("  1. Adjust search_path in src/main.rs to your plugins directory");
    println!("  2. Fill in the TODO with actual method calls on _client");
    println!("  3. Run: cd {name} && cargo run");
    Ok(())
}

fidius-cli::commands::keygen

pub

fn keygen (out : & str) -> Result
Source
pub fn keygen(out: &str) -> Result {
    use rand::rngs::OsRng;

    let signing_key = SigningKey::generate(&mut OsRng);
    let verifying_key = signing_key.verifying_key();

    let secret_path = format!("{}.secret", out);
    let public_path = format!("{}.public", out);

    std::fs::write(&secret_path, signing_key.to_bytes())?;
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::fs::set_permissions(&secret_path, std::fs::Permissions::from_mode(0o600))?;
    }
    std::fs::write(&public_path, verifying_key.to_bytes())?;

    println!("Generated keypair:");
    println!("  Secret: {}", secret_path);
    println!("  Public: {}", public_path);
    Ok(())
}

fidius-cli::commands::sign

pub

fn sign (key_path : & Path , dylib_path : & Path) -> Result
Source
pub fn sign(key_path: &Path, dylib_path: &Path) -> Result {
    let key_bytes: [u8; 32] = std::fs::read(key_path)?
        .try_into()
        .map_err(|_| "secret key must be exactly 32 bytes")?;

    let signing_key = SigningKey::from_bytes(&key_bytes);
    let dylib_bytes = std::fs::read(dylib_path)?;
    let signature = signing_key.sign(&dylib_bytes);

    let sig_path = dylib_path.with_extension(format!(
        "{}.sig",
        dylib_path
            .extension()
            .and_then(|e| e.to_str())
            .unwrap_or("")
    ));

    std::fs::write(&sig_path, signature.to_bytes())?;
    println!("Signed: {} -> {}", dylib_path.display(), sig_path.display());
    Ok(())
}

fidius-cli::commands::verify

pub

fn verify (key_path : & Path , dylib_path : & Path) -> Result
Source
pub fn verify(key_path: &Path, dylib_path: &Path) -> Result {
    let key_bytes: [u8; 32] = std::fs::read(key_path)?
        .try_into()
        .map_err(|_| "public key must be exactly 32 bytes")?;

    let verifying_key =
        VerifyingKey::from_bytes(&key_bytes).map_err(|e| format!("invalid public key: {e}"))?;

    let dylib_bytes = std::fs::read(dylib_path)?;

    let sig_path = dylib_path.with_extension(format!(
        "{}.sig",
        dylib_path
            .extension()
            .and_then(|e| e.to_str())
            .unwrap_or("")
    ));

    let sig_bytes: [u8; 64] = std::fs::read(&sig_path)
        .map_err(|_| format!("signature file not found: {}", sig_path.display()))?
        .try_into()
        .map_err(|_| "signature must be exactly 64 bytes")?;

    let signature = Signature::from_bytes(&sig_bytes);

    match verifying_key.verify(&dylib_bytes, &signature) {
        Ok(()) => {
            println!("Signature valid: {}", dylib_path.display());
            Ok(())
        }
        Err(_) => Err(format!("Signature INVALID: {}", dylib_path.display()).into()),
    }
}

fidius-cli::commands::inspect

pub

fn inspect (dylib_path : & Path) -> Result
Source
pub fn inspect(dylib_path: &Path) -> Result {
    use fidius_host::PluginHandle;

    let loaded = fidius_host::loader::load_library(dylib_path)
        .map_err(|e| format!("failed to load {}: {e}", dylib_path.display()))?;

    println!("Plugin Registry: {}", dylib_path.display());
    println!("  Plugins: {}", loaded.plugins.len());
    println!();

    for (i, plugin) in loaded.plugins.into_iter().enumerate() {
        let info = plugin.info.clone();
        let method_count = plugin.method_count;
        println!("  [{}] {}", i, info.name);
        println!("      Interface: {}", info.interface_name);
        println!("      Interface hash: {:#018x}", info.interface_hash);
        println!("      Interface version: {}", info.interface_version);
        println!("      Buffer strategy: {:?}", info.buffer_strategy);
        println!("      Capabilities: {:#018x}", info.capabilities);

        let handle = PluginHandle::from_loaded(plugin);
        let trait_meta = handle.trait_metadata();
        if !trait_meta.is_empty() {
            println!("      Trait metadata:");
            for (k, v) in &trait_meta {
                println!("        {k} = {v}");
            }
        }

        let any_method_meta = (0..method_count).any(|m| !handle.method_metadata(m).is_empty());
        if any_method_meta {
            println!("      Method metadata:");
            for m in 0..method_count {
                let metas = handle.method_metadata(m);
                if metas.is_empty() {
                    continue;
                }
                println!("        [{m}]:");
                for (k, v) in &metas {
                    println!("          {k} = {v}");
                }
            }
        }
    }

    Ok(())
}

fidius-cli::commands::test

pub

fn test (dir : & Path , release : bool) -> Result
Source
pub fn test(dir: &Path, release: bool) -> Result {
    use fidius_host::{CallError, PluginHandle};

    // Build
    let dylib_path = fidius_host::package::build_package(dir, release)?;
    println!("Built: {}", dylib_path.display());

    // Load
    let loaded = fidius_host::loader::load_library(&dylib_path)
        .map_err(|e| format!("failed to load plugin: {e}"))?;

    if loaded.plugins.is_empty() {
        return Err("no plugins found in registry".into());
    }

    let plugin_count = loaded.plugins.len();
    let mut failed_methods = 0usize;
    let mut smoke_ok = 0usize;

    for plugin in loaded.plugins {
        let info = plugin.info.clone();
        let method_count = plugin.method_count;
        let capabilities = info.capabilities;

        println!();
        println!(
            "Plugin: {} (interface {} v{}, {} methods)",
            info.name, info.interface_name, info.interface_version, method_count
        );

        let handle = PluginHandle::from_loaded(plugin);

        for i in 0..method_count as usize {
            // Smoke-test each method by invoking with a zero-arg input.
            // - Zero-arg methods: full round-trip succeeds (or Deserialization if
            //   the output isn't JSON-compatible, which still proves the call worked).
            // - Methods with args: plugin-side deserialization fails → reported as
            //   "needs input" (not a smoke failure, just a signal).
            // - Unimplemented optional methods: reported distinctly.
            let result = handle.call_method::<(), serde_json::Value>(i, &());
            match result {
                Ok(_) => {
                    println!("  [{i}] ✓ invoked (output decoded as JSON)");
                    smoke_ok += 1;
                }
                Err(CallError::Deserialization(_)) => {
                    println!("  [{i}] ✓ invoked (output not JSON-compat — use a specific type to inspect)");
                    smoke_ok += 1;
                }
                Err(CallError::Serialization(_)) => {
                    println!("  [{i}] — needs input (method takes args)");
                }
                Err(CallError::NotImplemented { bit }) => {
                    println!("  [{i}] — optional, not implemented (capability bit {bit})");
                }
                Err(CallError::InvalidMethodIndex { index, count }) => {
                    println!("  [{i}] ✗ invalid index {index} (plugin has {count} methods)");
                    failed_methods += 1;
                }
                Err(e) => {
                    println!("  [{i}] ✗ error: {e}");
                    failed_methods += 1;
                }
            }
        }

        let cap_bits_set = capabilities.count_ones();
        if cap_bits_set > 0 {
            println!("  capabilities: {cap_bits_set} bit(s) set ({capabilities:#018x})");
        }
    }

    println!();
    if failed_methods == 0 {
        println!(
            "Smoke passed: {plugin_count} plugin(s), {smoke_ok} zero-arg method(s) invoked cleanly"
        );
        Ok(())
    } else {
        Err(format!("{failed_methods} method(s) failed smoke test").into())
    }
}

fidius-cli::commands::package_validate

pub

fn package_validate (dir : & Path) -> Result
Source
pub fn package_validate(dir: &Path) -> Result {
    let manifest = fidius_core::package::load_manifest_untyped(dir)?;
    let pkg = &manifest.package;

    println!("Package: {} v{}", pkg.name, pkg.version);
    println!(
        "  Interface: {} (version {})",
        pkg.interface, pkg.interface_version
    );
    println!(
        "  Metadata: {} field(s)",
        manifest.metadata.as_table().map_or(0, |t| t.len())
    );
    println!("\nManifest valid.");
    Ok(())
}

fidius-cli::commands::package_build

pub

fn package_build (dir : & Path , release : bool) -> Result
Source
pub fn package_build(dir: &Path, release: bool) -> Result {
    let manifest = fidius_core::package::load_manifest_untyped(dir)?;
    let cargo_toml = dir.join("Cargo.toml");
    if !cargo_toml.exists() {
        return Err(format!("Cargo.toml not found in {}", dir.display()).into());
    }

    println!(
        "Building package: {} v{}",
        manifest.package.name, manifest.package.version
    );

    let mut cmd = std::process::Command::new("cargo");
    cmd.arg("build").arg("--manifest-path").arg(&cargo_toml);
    if release {
        cmd.arg("--release");
    }

    let output = cmd.output()?;
    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(format!("build failed:\n{}", stderr).into());
    }

    let profile = if release { "release" } else { "debug" };
    println!(
        "Build successful. Output in {}/target/{}/",
        dir.display(),
        profile
    );
    Ok(())
}

fidius-cli::commands::package_inspect

pub

fn package_inspect (dir : & Path) -> Result
Source
pub fn package_inspect(dir: &Path) -> Result {
    let manifest = fidius_core::package::load_manifest_untyped(dir)?;
    let pkg = &manifest.package;

    println!("Package: {}", dir.display());
    println!("  Name: {}", pkg.name);
    println!("  Version: {}", pkg.version);
    println!("  Interface: {}", pkg.interface);
    println!("  Interface version: {}", pkg.interface_version);
    if let Some(table) = manifest.metadata.as_table() {
        println!("  Metadata:");
        for (key, value) in table {
            println!("    {} = {}", key, value);
        }
    }
    Ok(())
}

fidius-cli::commands::package_sign

pub

fn package_sign (key_path : & Path , dir : & Path) -> Result
Source
pub fn package_sign(key_path: &Path, dir: &Path) -> Result {
    if !dir.join("package.toml").exists() {
        return Err(format!("package.toml not found in {}", dir.display()).into());
    }

    let key_bytes: [u8; 32] = std::fs::read(key_path)?
        .try_into()
        .map_err(|_| "secret key must be exactly 32 bytes")?;

    let signing_key = SigningKey::from_bytes(&key_bytes);
    let digest = fidius_core::package::package_digest(dir)?;
    let signature = signing_key.sign(&digest);

    let sig_path = dir.join("package.sig");
    std::fs::write(&sig_path, signature.to_bytes())?;
    println!(
        "Signed package: {} -> {}",
        dir.display(),
        sig_path.display()
    );
    Ok(())
}

fidius-cli::commands::package_verify

pub

fn package_verify (key_path : & Path , dir : & Path) -> Result
Source
pub fn package_verify(key_path: &Path, dir: &Path) -> Result {
    if !dir.join("package.toml").exists() {
        return Err(format!("package.toml not found in {}", dir.display()).into());
    }

    let key_bytes: [u8; 32] = std::fs::read(key_path)?
        .try_into()
        .map_err(|_| "public key must be exactly 32 bytes")?;

    let verifying_key =
        VerifyingKey::from_bytes(&key_bytes).map_err(|e| format!("invalid public key: {e}"))?;

    let sig_path = dir.join("package.sig");
    let sig_bytes: [u8; 64] = std::fs::read(&sig_path)
        .map_err(|_| format!("signature file not found: {}", sig_path.display()))?
        .try_into()
        .map_err(|_| "signature must be exactly 64 bytes")?;

    let signature = Signature::from_bytes(&sig_bytes);
    let digest = fidius_core::package::package_digest(dir)?;

    match verifying_key.verify(&digest, &signature) {
        Ok(()) => {
            println!("Package signature valid: {}", dir.display());
            Ok(())
        }
        Err(_) => Err(format!("Package signature INVALID: {}", dir.display()).into()),
    }
}

fidius-cli::commands::package_pack

pub

fn package_pack (dir : & Path , output : Option < & Path >) -> Result
Source
pub fn package_pack(dir: &Path, output: Option<&Path>) -> Result {
    let result = fidius_core::package::pack_package(dir, output)?;

    if result.unsigned {
        eprintln!("warning: package is unsigned (no package.sig found)");
    }

    let size = std::fs::metadata(&result.path)?.len();
    let human_size = if size >= 1024 * 1024 {
        format!("{:.1} MB", size as f64 / (1024.0 * 1024.0))
    } else if size >= 1024 {
        format!("{:.1} KB", size as f64 / 1024.0)
    } else {
        format!("{size} B")
    };

    println!("Packed: {} ({human_size})", result.path.display());
    Ok(())
}

fidius-cli::commands::package_unpack

pub

fn package_unpack (archive : & Path , dest : Option < & Path >) -> Result
Source
pub fn package_unpack(archive: &Path, dest: Option<&Path>) -> Result {
    let dest = dest.unwrap_or_else(|| Path::new("."));
    let pkg_dir = fidius_core::package::unpack_package(archive, dest)?;
    println!("Unpacked: {}", pkg_dir.display());
    Ok(())
}