nu_plugin_vulkan_matrix 0.1.1

GPU matrix operations for Nushell using Vulkan
use super::*;
use std::path::PathBuf;

fn path(engine: &EngineInterface, call: &EvaluatedCall) -> Result<PathBuf, LabeledError> {
    let path = PathBuf::from(call.req::<String>(0)?);
    Ok(if path.is_absolute() {
        path
    } else {
        PathBuf::from(engine.get_current_dir()?).join(path)
    })
}

pub(super) struct FromNpy;
impl SimplePluginCommand for FromNpy {
    type Plugin = MatrixPlugin;
    fn name(&self) -> &str {
        "vulkan matrix from-npy"
    }
    fn description(&self) -> &str {
        "Import a C-order f32/f64 NumPy file without a userspace payload copy."
    }
    fn signature(&self) -> Signature {
        Signature::build(SimplePluginCommand::name(self))
            .input_output_type(Type::Nothing, Type::Custom("vulkan matrix".into()))
            .required("path", SyntaxShape::Filepath, "NumPy .npy input file.")
            .named("device", SyntaxShape::String, "Explicit GPU selector (dN or iN).", None)
            .switch("shared", "Keep a read-only file mapping as the GPU tensor; requires an aligned header. Keep the file unchanged while in use.", None)
    }
    fn run(
        &self,
        plugin: &MatrixPlugin,
        engine: &EngineInterface,
        call: &EvaluatedCall,
        _input: &Value,
    ) -> Result<Value, LabeledError> {
        let device: String = call
            .get_flag("device")?
            .ok_or_else(|| error("--device is required; select dN or iN", call.head))?;
        let gpu = plugin.device(&device, call.head)?;
        let tensor = crate::file_io::load(&gpu, &path(engine, call)?, call.has_flag("shared")?)
            .map_err(|e| error(e.to_string(), call.head))?;
        let dtype = if tensor.dtype == <f64 as cubecl::CubeElement>::cube_type() {
            "f64"
        } else {
            "f32"
        };
        plugin.register(engine, gpu, tensor, &device, dtype, call.head)
    }
}

pub(super) struct ToNpy;
impl SimplePluginCommand for ToNpy {
    type Plugin = MatrixPlugin;
    fn name(&self) -> &str {
        "vulkan matrix to-npy"
    }
    fn description(&self) -> &str {
        "Write GPU matrix data directly into NumPy file pages and sync the file."
    }
    fn signature(&self) -> Signature {
        Signature::build(SimplePluginCommand::name(self))
            .input_output_type(Type::Custom("vulkan matrix".into()), Type::Nothing)
            .required("path", SyntaxShape::Filepath, "NumPy .npy output file.")
            .switch("force", "Atomically replace an existing output file.", None)
    }
    fn run(
        &self,
        plugin: &MatrixPlugin,
        engine: &EngineInterface,
        call: &EvaluatedCall,
        input: &Value,
    ) -> Result<Value, LabeledError> {
        let resident = plugin
            .resources
            .lock()
            .map_err(|e| error(e.to_string(), call.head))?
            .get(input, call.head)?;
        crate::file_io::save(
            &resident.gpu,
            &resident.tensor,
            &path(engine, call)?,
            call.has_flag("force")?,
        )
        .map_err(|e| error(e.to_string(), call.head))?;
        Ok(Value::nothing(call.head))
    }
}