From 5ba3eda924c506b04baaa45cdcd426770d692686 Mon Sep 17 00:00:00 2001 From: sigoden Date: Sat, 19 Oct 2024 06:44:42 +0800 Subject: [PATCH] feat: enhence dotenv (#346) --- src/argc_value.rs | 8 +++++--- src/build.rs | 13 ++++++------ src/runtime/mod.rs | 18 +++++++++++++---- src/utils.rs | 20 +++++++++++++++---- .../snapshots/integration__meta__dotenv.snap | 20 ++++++++++++++++--- ...integration__meta__dotenv_custom_path.snap | 20 ++++++++++++++++--- 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/src/argc_value.rs b/src/argc_value.rs index 6776cd51..b459c94b 100644 --- a/src/argc_value.rs +++ b/src/argc_value.rs @@ -2,8 +2,8 @@ use indexmap::IndexMap; #[cfg(feature = "eval-bash")] use crate::utils::{ - argc_var_name, escape_shell_words, expand_dotenv, AFTER_HOOK, ARGC_REQUIRE_TOOLS, BEFORE_HOOK, - VARIABLE_PREFIX, + argc_var_name, escape_shell_words, AFTER_HOOK, ARGC_LOAD_DOTENV, ARGC_REQUIRE_TOOLS, + BEFORE_HOOK, VARIABLE_PREFIX, }; #[derive(Debug, PartialEq, Eq)] @@ -109,7 +109,9 @@ impl ArgcValue { } } ArgcValue::Dotenv(value) => { - list.push(expand_dotenv(value)); + let value = escape_shell_words(value); + list.push(ARGC_LOAD_DOTENV.to_string()); + list.push(format!("_argc_load_dotenv {value}")); } ArgcValue::RequireTools(tools) => { require_tools = tools.to_vec(); diff --git a/src/build.rs b/src/build.rs index 7f99b949..449f4de4 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,7 +1,7 @@ use crate::{ command::Command, param::{FlagOptionParam, Param, PositionalParam}, - utils::{escape_shell_words, expand_dotenv, ARGC_REQUIRE_TOOLS}, + utils::{escape_shell_words, ARGC_LOAD_DOTENV, ARGC_REQUIRE_TOOLS}, ChoiceValue, DefaultValue, }; use anyhow::Result; @@ -248,11 +248,6 @@ pub fn build(source: &str, root_name: &str, wrap_width: Option) -> Result fn build_root(cmd: &Command, wrap_width: Option) -> String { let command = build_command(cmd, wrap_width); - let dotenv = if let Some(value) = cmd.dotenv() { - format!("\n {}", expand_dotenv(value)) - } else { - String::new() - }; let (before_hook, after_hook) = cmd.exist_hooks(); let before_hook = if before_hook { "\n _argc_before" @@ -266,6 +261,12 @@ fn build_root(cmd: &Command, wrap_width: Option) -> String { util_fns.push_str(util_fn); } } + let dotenv = if let Some(value) = cmd.dotenv() { + util_fns.push_str(&format!("\n{ARGC_LOAD_DOTENV}\n")); + format!("\n _argc_load_dotenv {}", escape_shell_words(value)) + } else { + String::new() + }; let require_tools = if command.contains("_argc_tools") { util_fns.push_str(&format!("\n{ARGC_REQUIRE_TOOLS}\n")); r#" diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index cbef9b99..a9955aa8 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -2,7 +2,7 @@ pub mod navite; use anyhow::Result; -use std::collections::HashMap; +use std::{collections::HashMap, env}; pub trait Runtime where @@ -65,9 +65,19 @@ where continue; } if let Some((key, value)) = line.split_once('=') { - let key = key.trim().to_string(); - let value = value.trim().to_string(); - output.insert(key, value); + let env_name = key.trim().to_string(); + let env_value = value.trim().to_string(); + let env_value = if (env_value.starts_with('"') && env_value.ends_with('"')) + || (env_value.starts_with('\'') && env_value.ends_with('\'')) + { + &env_value[1..env_value.len() - 1] + } else { + &env_value + }; + + if env::var(&env_name).is_err() { + output.insert(env_name, env_value.to_string()); + } } } Some(output) diff --git a/src/utils.rs b/src/utils.rs index ddee3edb..f9e7313b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -33,10 +33,22 @@ pub const ARGC_REQUIRE_TOOLS: &str = r#"_argc_require_tools() { }"#; #[cfg(any(feature = "build", feature = "eval-bash"))] -pub fn expand_dotenv(value: &str) -> String { - let value = escape_shell_words(value); - format!("[ -f {value} ] && set -o allexport && . {value} && set +o allexport") -} +pub const ARGC_LOAD_DOTENV: &str = r#"_argc_load_dotenv() { + local env_file="$1" env_vars="" + if [[ -f "$env_file" ]]; then + while IFS='=' read -r key value; do + if [[ "$key" == $'#'* ]] || [[ -z "$key" ]]; then + continue + fi + if [[ -z "${!key+x}" ]]; then + env_vars="$env_vars $key=$value" + fi + done < <(cat "$env_file"; echo "") + if [[ -n "$env_vars" ]]; then + eval "export $env_vars" + fi + fi +}"#; pub fn to_cobol_case(value: &str) -> String { Converter::new() diff --git a/tests/snapshots/integration__meta__dotenv.snap b/tests/snapshots/integration__meta__dotenv.snap index 71c43b33..40d3892c 100644 --- a/tests/snapshots/integration__meta__dotenv.snap +++ b/tests/snapshots/integration__meta__dotenv.snap @@ -6,12 +6,26 @@ RUN prog # OUTPUT -[ -f .env ] && set -o allexport && . .env && set +o allexport +_argc_load_dotenv() { + local env_file="$1" env_vars="" + if [[ -f "$env_file" ]]; then + while IFS='=' read -r key value; do + if [[ "$key" == $'#'* ]] || [[ -z "$key" ]]; then + continue + fi + if [[ -z "${!key+x}" ]]; then + env_vars="$env_vars $key=$value" + fi + done < <(cat "$env_file"; echo "") + if [[ -n "$env_vars" ]]; then + eval "export $env_vars" + fi + fi +} +_argc_load_dotenv .env argc__args=( prog ) argc__positionals=( ) # BUILD_OUTPUT argc__args=([0]="prog") argc__positionals=() - - diff --git a/tests/snapshots/integration__meta__dotenv_custom_path.snap b/tests/snapshots/integration__meta__dotenv_custom_path.snap index 079448a1..b49918e9 100644 --- a/tests/snapshots/integration__meta__dotenv_custom_path.snap +++ b/tests/snapshots/integration__meta__dotenv_custom_path.snap @@ -6,12 +6,26 @@ RUN prog # OUTPUT -[ -f .env.local ] && set -o allexport && . .env.local && set +o allexport +_argc_load_dotenv() { + local env_file="$1" env_vars="" + if [[ -f "$env_file" ]]; then + while IFS='=' read -r key value; do + if [[ "$key" == $'#'* ]] || [[ -z "$key" ]]; then + continue + fi + if [[ -z "${!key+x}" ]]; then + env_vars="$env_vars $key=$value" + fi + done < <(cat "$env_file"; echo "") + if [[ -n "$env_vars" ]]; then + eval "export $env_vars" + fi + fi +} +_argc_load_dotenv .env.local argc__args=( prog ) argc__positionals=( ) # BUILD_OUTPUT argc__args=([0]="prog") argc__positionals=() - -