herolib-code 0.3.13

Code analysis and parsing utilities for Rust source files
Documentation
use super::{BuildResult, RustBuilder, RustBuilderError};
use rhai::{Engine, EvalAltResult, Position};

/// Rhai wrapper for RustBuilder to enable scripting support.
#[derive(Debug, Clone)]
pub struct RhaiRustBuilder {
    inner: RustBuilder,
}

impl RhaiRustBuilder {
    /// Creates a new RhaiRustBuilder from the current directory.
    pub fn new() -> Self {
        Self {
            inner: RustBuilder::new(),
        }
    }

    /// Creates a new RhaiRustBuilder from the specified path.
    pub fn from_path(path: String) -> Self {
        Self {
            inner: RustBuilder::from_path(path),
        }
    }

    /// Sets the build profile to Release.
    pub fn release(mut self) -> Self {
        self.inner = self.inner.release();
        self
    }

    /// Sets the build profile to Debug.
    pub fn debug(mut self) -> Self {
        self.inner = self.inner.debug();
        self
    }

    /// Sets the build target to a specific binary.
    pub fn bin(mut self, name: String) -> Self {
        self.inner = self.inner.bin(name);
        self
    }

    /// Sets the build target to the library.
    pub fn lib(mut self) -> Self {
        self.inner = self.inner.lib();
        self
    }

    /// Sets the build target to a specific example.
    pub fn example(mut self, name: String) -> Self {
        self.inner = self.inner.example(name);
        self
    }

    /// Sets the build target to all binaries.
    pub fn all_bins(mut self) -> Self {
        self.inner = self.inner.all_bins();
        self
    }

    /// Adds a feature to enable.
    pub fn feature(mut self, feature: String) -> Self {
        self.inner = self.inner.feature(feature);
        self
    }

    /// Enables all features.
    pub fn all_features(mut self) -> Self {
        self.inner = self.inner.all_features();
        self
    }

    /// Disables default features.
    pub fn no_default_features(mut self) -> Self {
        self.inner = self.inner.no_default_features();
        self
    }

    /// Enables copying to ~/hero/bin.
    pub fn copy_to_hero_bin(mut self) -> Self {
        self.inner = self.inner.copy_to_hero_bin();
        self
    }

    /// Sets a custom output directory.
    pub fn output_dir(mut self, path: String) -> Self {
        self.inner = self.inner.output_dir(path);
        self
    }

    /// Adds an extra cargo argument.
    pub fn arg(mut self, arg: String) -> Self {
        self.inner = self.inner.arg(arg);
        self
    }

    /// Enables verbose output.
    pub fn verbose(mut self) -> Self {
        self.inner = self.inner.verbose();
        self
    }

    /// Executes the build.
    /// Returns a RhaiBuildResult with success=false on build failures,
    /// or throws an error for configuration issues (e.g., Cargo.toml not found).
    pub fn build(self) -> Result<RhaiBuildResult, Box<EvalAltResult>> {
        match self.inner.build() {
            Ok(result) => Ok(result.into()),
            Err(RustBuilderError::BuildFailed { code, stderr }) => {
                // Return a result with success=false for build failures
                Ok(RhaiBuildResult {
                    success: false,
                    exit_code: code,
                    stdout: String::new(),
                    stderr,
                    artifacts: Vec::new(),
                    copied_to: None,
                })
            }
            Err(e) => Err(Box::new(EvalAltResult::ErrorRuntime(
                e.to_string().into(),
                Position::NONE,
            ))),
        }
    }
}

impl Default for RhaiRustBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// Rhai-compatible wrapper for BuildResult.
#[derive(Debug, Clone)]
pub struct RhaiBuildResult {
    /// Whether the build succeeded
    pub success: bool,

    /// Exit code from cargo
    pub exit_code: i32,

    /// Stdout from cargo
    pub stdout: String,

    /// Stderr from cargo
    pub stderr: String,

    /// List of built artifact paths
    pub artifacts: Vec<String>,

    /// Path to copied artifact (if applicable)
    pub copied_to: Option<String>,
}

impl From<BuildResult> for RhaiBuildResult {
    fn from(result: BuildResult) -> Self {
        Self {
            success: result.success,
            exit_code: result.exit_code,
            stdout: result.stdout,
            stderr: result.stderr,
            artifacts: result
                .artifacts
                .iter()
                .map(|p| p.to_string_lossy().to_string())
                .collect(),
            copied_to: result
                .copied_to
                .as_ref()
                .map(|p| p.to_string_lossy().to_string()),
        }
    }
}

/// Registers the rust_builder module with Rhai.
pub fn register_rust_builder_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    eprintln!("[DEBUG] Registering rust_builder module with Rhai");

    // Register RhaiBuildResult type
    engine.register_type_with_name::<RhaiBuildResult>("BuildResult");

    // Register BuildResult properties
    engine.register_get("success", |r: &mut RhaiBuildResult| r.success);
    engine.register_get("exit_code", |r: &mut RhaiBuildResult| r.exit_code);
    engine.register_get("stdout", |r: &mut RhaiBuildResult| {
        r.stdout.clone()
    });
    engine.register_get("stderr", |r: &mut RhaiBuildResult| {
        r.stderr.clone()
    });
    engine.register_get("artifacts", |r: &mut RhaiBuildResult| {
        r.artifacts.clone()
    });
    engine.register_get("copied_to", |r: &mut RhaiBuildResult| {
        r.copied_to.clone()
    });

    // Register builder functions
    engine.register_fn("rust_builder", || RhaiRustBuilder::new());
    engine.register_fn("rust_builder_from", |path: String| RhaiRustBuilder::from_path(path));

    // Register builder methods
    engine.register_fn("release", |b: RhaiRustBuilder| b.release());
    engine.register_fn("debug", |b: RhaiRustBuilder| b.debug());
    engine.register_fn("bin", |b: RhaiRustBuilder, name: String| b.bin(name));
    engine.register_fn("lib", |b: RhaiRustBuilder| b.lib());
    engine.register_fn("example", |b: RhaiRustBuilder, name: String| b.example(name));
    engine.register_fn("all_bins", |b: RhaiRustBuilder| b.all_bins());
    engine.register_fn("feature", |b: RhaiRustBuilder, name: String| b.feature(name));
    engine.register_fn("all_features", |b: RhaiRustBuilder| b.all_features());
    engine.register_fn("no_default_features", |b: RhaiRustBuilder| b.no_default_features());
    engine.register_fn("copy_to_hero_bin", |b: RhaiRustBuilder| b.copy_to_hero_bin());
    engine.register_fn("output_dir", |b: RhaiRustBuilder, path: String| b.output_dir(path));
    engine.register_fn("arg", |b: RhaiRustBuilder, arg: String| b.arg(arg));
    engine.register_fn("verbose", |b: RhaiRustBuilder| b.verbose());
    engine.register_fn("build", |b: RhaiRustBuilder| b.build());

    Ok(())
}