use crate::commands::WholeStreamCommand; use crate::prelude::*; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, UntaggedValue}; use serde::Deserialize; use serde::Serialize; use sha2::{Digest, Sha256}; use std::io::Read; use std::path::PathBuf; pub struct Autoenv; #[derive(Deserialize, Serialize, Debug, Default)] pub struct Trusted { pub files: IndexMap>, } impl Trusted { pub fn new() -> Self { Trusted { files: IndexMap::new(), } } } pub fn file_is_trusted(nu_env_file: &PathBuf, content: &[u8]) -> Result { let contentdigest = Sha256::digest(&content).as_slice().to_vec(); let nufile = std::fs::canonicalize(nu_env_file)?; let trusted = read_trusted()?; Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest)) } pub fn read_trusted() -> Result { let config_path = config::default_path_for(&Some(PathBuf::from("nu-env.toml")))?; let mut file = std::fs::OpenOptions::new() .read(true) .create(true) .write(true) .open(config_path) .map_err(|_| ShellError::untagged_runtime_error("Couldn't open nu-env.toml"))?; let mut doc = String::new(); file.read_to_string(&mut doc)?; let allowed = toml::de::from_str(doc.as_str()).unwrap_or_else(|_| Trusted::new()); Ok(allowed) } #[async_trait] impl WholeStreamCommand for Autoenv { fn name(&self) -> &str { "autoenv" } fn usage(&self) -> &str { // "Mark a .nu-env file in a directory as trusted. Needs to be re-run after each change to the file or its filepath." r#"Manage directory specific environment variables and scripts. Create a file called .nu-env in any directory and run 'autoenv trust' to let nushell read it when entering the directory. The file can contain several optional sections: env: environment variables to set when visiting the directory. The variables are unset after leaving the directory and any overwritten values are restored. scriptvars: environment variables that should be set to the return value of a script. After they have been set, they behave in the same way as variables set in the env section. scripts: scripts to run when entering the directory or leaving it. Note that exitscripts are not run in the directory they are declared in."# } fn signature(&self) -> Signature { Signature::build("autoenv") } async fn run( &self, _args: CommandArgs, registry: &CommandRegistry, ) -> Result { let registry = registry.clone(); Ok(OutputStream::one(ReturnSuccess::value( UntaggedValue::string(crate::commands::help::get_help(&Autoenv, ®istry)) .into_value(Tag::unknown()), ))) } fn examples(&self) -> Vec { vec![Example { description: "Example .nu-env file", example: r#"cat .nu-env [env] mykey = "myvalue" [scriptvars] myscript = "echo myval" [scripts] entryscripts = ["touch hello.txt", "touch hello2.txt"] exitscripts = ["touch bye.txt"]"#, result: None, }] } }