diff --git a/Cargo.lock b/Cargo.lock index b2709a7..21015b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,15 +147,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "borsh" version = "1.5.1" @@ -322,15 +313,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.4.2" @@ -374,26 +356,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "dirs" version = "5.0.1" @@ -486,16 +448,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1001,7 +953,6 @@ dependencies = [ "nu-plugin", "nu-protocol", "once_cell", - "rust-embed", "serde", "serde_json", "shellexpand", @@ -1380,40 +1331,6 @@ dependencies = [ "serde", ] -[[package]] -name = "rust-embed" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.75", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" -dependencies = [ - "sha2", - "walkdir", -] - [[package]] name = "rust_decimal" version = "1.36.0" @@ -1455,15 +1372,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - [[package]] name = "seahash" version = "4.1.0" @@ -1508,17 +1416,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sharded-slab" version = "0.1.7" @@ -1828,12 +1725,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "typetag" version = "0.2.18" @@ -1935,16 +1826,6 @@ dependencies = [ "quote", ] -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2028,15 +1909,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index c17e419..dcf9197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ itertools = "0.13.0" nu-plugin = "0.98.0" nu-protocol = "0.98.0" once_cell = "1.19.0" -rust-embed = "8.5.0" serde = "1.0.208" serde_json = "1.0.125" shellexpand = "3.1.0" diff --git a/README.md b/README.md index 38755ff..dc26098 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Prior to 0.13.0 this plugin was written in Bash, with the Nu plugin protocol don Since 0.13.0, the plugin is written in Rust, with the much simplified Bash script embedded. -By default the embedded Bash script is extracted at runtime into a temporary directory. This behaviour may be overridden by setting the ``NU_PLUGIN_BASH_ENV_SCRIPT` environment variable, which is then expected to resolve to the path of the pre-installed script. +By default the embedded Bash script is extracted at runtime into a temporary directory. This behaviour may be overridden by setting the ``NU_PLUGIN_BASH_ENV_JSON` environment variable, which is then expected to resolve to the path of the pre-installed script. ## Logging diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..745231c --- /dev/null +++ b/build.rs @@ -0,0 +1,54 @@ +use std::{ + env, + path::{Path, PathBuf}, +}; + +const BASH_ENV_JSON_VERSION: &str = "0.6.0"; + +fn fetch_bash_env_json() -> Option { + let out_dir: PathBuf = env::var("OUT_DIR").unwrap().into(); + let bash_env_json_repo_dir = PathBuf::from("bash-env-json"); + let bash_env_json_repo_path = out_dir.join(bash_env_json_repo_dir.as_path()); + + if Path::exists(&bash_env_json_repo_path) { + Some(bash_env_json_repo_path) + } else { + let bash_env_json_repo_path_str = bash_env_json_repo_path.to_string_lossy(); + let git_args = [ + "clone", + "--filter=blob:none", + "--branch", + BASH_ENV_JSON_VERSION, + "https://github.com/tesujimath/bash-env-json.git", + bash_env_json_repo_path_str.as_ref(), + ]; + println!("cargo:warning=git {}", &git_args.join(" ")); + match std::process::Command::new("git").args(git_args).output() { + Ok(output) => { + if output.status.success() { + Some(bash_env_json_repo_path) + } else { + println!( + "cargo:warning=git {:?} failed: {}", + &git_args, output.status + ); + + None + } + } + Err(e) => { + println!("cargo:warning=git clone failed: {}", e); + + None + } + } + } +} + +fn main() -> std::io::Result<()> { + if let Some(_bash_env_json_repo_path) = fetch_bash_env_json() { + // TODO what now? + } + + Ok(()) +} diff --git a/flake.lock b/flake.lock index 96cb1a5..fbb505d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,9 +1,49 @@ { "nodes": { + "bash-env-json": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1729208536, + "narHash": "sha256-qaZ6oOg1RMquvjND1mNnRPZ/CKfZkVii+plagQyq/Xs=", + "owner": "tesujimath", + "repo": "bash-env-json", + "rev": "6ab0dfff468ffa39975a9f8d53e2f43b2890ed47", + "type": "github" + }, + "original": { + "owner": "tesujimath", + "ref": "main", + "repo": "bash-env-json", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { "lastModified": 1710146030, "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", @@ -52,7 +92,8 @@ }, "root": { "inputs": { - "flake-utils": "flake-utils", + "bash-env-json": "bash-env-json", + "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" } @@ -89,6 +130,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index abe842b..b4a399f 100644 --- a/flake.nix +++ b/flake.nix @@ -3,11 +3,18 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + + bash-env-json = { + url = "github:tesujimath/bash-env-json/main"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, rust-overlay, flake-utils }: + outputs = { nixpkgs, rust-overlay, flake-utils, bash-env-json, ... }: flake-utils.lib.eachDefaultSystem (system: let @@ -17,6 +24,10 @@ inherit system overlays; }; + flakePkgs = { + bash-env-json = bash-env-json.packages.${system}.default; + }; + # cargo-nightly based on https://github.com/oxalica/rust-overlay/issues/82 nightly = pkgs.rust-bin.selectLatestNightlyWith (t: t.default); cargo-nightly = pkgs.writeShellScriptBin "cargo-nightly" '' @@ -24,10 +35,6 @@ exec "${nightly}/bin/cargo" "$@" ''; - nu_plugin_bash_env_script = pkgs.writeShellScriptBin "nu_plugin_bash_env_script" - (builtins.replaceStrings [ "jq" "(cat)" " sed " ] [ "${pkgs.jq}/bin/jq" "(${pkgs.coreutils}/bin/cat)" " ${pkgs.gnused}/bin/sed " ] - (builtins.readFile ./scripts/bash_env.sh)); - nu_plugin_bash_env = let cargoConfig = builtins.fromTOML (builtins.readFile ./Cargo.toml); in @@ -49,10 +56,13 @@ maintainers = [ maintainers.tailhook ]; }; - buildInputs = [ pkgs.makeWrapper ]; + buildInputs = [ + pkgs.makeWrapper + flakePkgs.bash-env-json + ]; postFixup = '' - wrapProgram $out/bin/nu_plugin_bash_env --set NU_PLUGIN_BASH_ENV_SCRIPT ${nu_plugin_bash_env_script}/bin/nu_plugin_bash_env_script + wrapProgram $out/bin/nu_plugin_bash_env --set NU_PLUGIN_BASH_ENV_JSON ${flakePkgs.bash-env-json}/bin/bash-env-json ''; }; in diff --git a/src/main.rs b/src/main.rs index c378fac..f7c4564 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,11 @@ use nu_protocol::{ Type, Value, }; use once_cell::sync::OnceCell; -use rust_embed::Embed; use serde::{Deserialize, Serialize}; use shellexpand::tilde; use std::{ - env, fs, + collections::HashMap, + concat, env, fs, io::Write, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, @@ -154,7 +154,7 @@ fn bash_env( export: Vec, cwd: String, ) -> anyhow::Result { - let script_path = bash_env_script_path(); + let script_path = bash_env_json_path(); let mut argv: Vec<_> = [script_path].into(); if stdin.is_some() { argv.push("--stdin"); @@ -162,10 +162,6 @@ fn bash_env( if let Some(ref path) = path { argv.push(path.as_str()); } - let exports = - itertools::Itertools::intersperse(export.into_iter(), ",".to_string()).collect::(); - argv.push("--export"); - argv.push(exports.as_str()); trace!("Popen::create({:?})", &argv); @@ -193,19 +189,35 @@ fn bash_env( .with_context(|| "stderr.write_all()")?; } - match serde_json::from_str(out.as_ref().unwrap()) - .with_context(|| "serde_json::from_reader()")? - { - BashEnvResult::Env(env) => Ok(create_record(env, input_span, creation_site_span)), - BashEnvResult::Error(msg) => Err(anyhow!(msg)), + let result: BashEnvResult = + serde_json::from_str(out.as_ref().unwrap()).with_context(|| "serde_json::from_reader()")?; + + if let Some(msg) = &result.error { + Err(anyhow!(msg.clone())) + } else if let (Some(env), Some(shellvars)) = (&result.env, &result.shellvars) { + Ok(create_record( + env, + shellvars, + &export, + input_span, + creation_site_span, + )) + } else { + Err(anyhow!("unexpected result from bash-env-json")) } } -fn create_record(env: Vec, input_span: Span, creation_site_span: Span) -> Value { - let cols = env.iter().map(|kv| kv.k.clone()).collect::>(); +fn create_record( + env: &HashMap, + _shellvars: &HashMap, + _export: &[String], + input_span: Span, + creation_site_span: Span, +) -> Value { + let cols = env.iter().map(|(k, _v)| k.clone()).collect::>(); let vals = env .iter() - .map(|kv| Value::string(kv.v.clone(), Span::unknown())) + .map(|(_k, v)| Value::string(v.clone(), Span::unknown())) .collect::>(); Value::record( Record::from_raw_cols_vals(cols, vals, input_span, creation_site_span).unwrap(), @@ -214,9 +226,10 @@ fn create_record(env: Vec, input_span: Span, creation_site_span: Span) -> Va } #[derive(Serialize, Deserialize)] -enum BashEnvResult { - Env(Vec), - Error(String), +struct BashEnvResult { + env: Option>, + shellvars: Option>, + error: Option, } #[derive(Serialize, Deserialize)] @@ -243,7 +256,7 @@ fn main() { // prefer to take the path from the environment variable, falling back to writing a temporary file // with contents taken from the embedded script - let script_path_env_var = "NU_PLUGIN_BASH_ENV_SCRIPT"; + let script_path_env_var = "NU_PLUGIN_BASH_ENV_JSON"; let script_path_from_env = env::var(script_path_env_var).ok(); #[allow(unused_assignments)] let mut tempdir: Option = None; @@ -259,7 +272,7 @@ fn main() { } }; - BASH_ENV_SCRIPT_PATH.get_or_init(|| script_path); + BASH_ENV_JSON_PATH.get_or_init(|| script_path); serve_plugin(&BashEnvPlugin, JsonSerializer); @@ -271,30 +284,26 @@ fn main() { } fn extract_embedded_script(tempdir: &TempDir) -> String { - let script = "bash_env.sh"; - let path = tempdir.path().join(script).to_path_buf(); - fs::write(&path, Scripts::get(script).unwrap().data.as_ref()).unwrap(); + const SCRIPT: &str = "bash-env-json"; + let out_path = tempdir.path().join(SCRIPT).to_path_buf(); + let script_body = include_str!(concat!(env!("OUT_DIR"), "/bash-env-json/bash-env-json")); + fs::write(&out_path, script_body).unwrap(); // make executable - let mut perms = fs::metadata(&path) - .unwrap_or_else(|e| panic!("metadata({:?}): {}", &path, e)) + let mut perms = fs::metadata(&out_path) + .unwrap_or_else(|e| panic!("metadata({:?}): {}", &out_path, e)) .permissions(); perms.set_mode(0o755); - fs::set_permissions(&path, perms) - .unwrap_or_else(|e| panic!("set_permissions({:?}): {}", &path, e)); + fs::set_permissions(&out_path, perms) + .unwrap_or_else(|e| panic!("set_permissions({:?}): {}", &out_path, e)); - let path = path.into_os_string().into_string().unwrap(); - info!("extracted {} into {}", script, &path); + let path = out_path.into_os_string().into_string().unwrap(); + info!("extracted {} into {}", SCRIPT, &path); path } -fn bash_env_script_path() -> &'static str { - BASH_ENV_SCRIPT_PATH.get().unwrap() +fn bash_env_json_path() -> &'static str { + BASH_ENV_JSON_PATH.get().unwrap() } -static BASH_ENV_SCRIPT_PATH: OnceCell = OnceCell::new(); - -// embed the bash script -#[derive(Embed)] -#[folder = "scripts"] -struct Scripts; +static BASH_ENV_JSON_PATH: OnceCell = OnceCell::new();