use std::borrow::Cow;
use std::collections::HashMap;
use super::cmd;
use super::model;
use super::query;
use super::syntax;
fn expand_tree_vars(
config: &model::Configuration,
tree_idx: model::TreeIndex,
garden_idx: Option<model::GardenIndex>,
name: &str,
) -> Result<Option<String>, String> {
if syntax::is_digit(name) {
return Ok(Some(format!("${}", name)));
}
if syntax::is_graft(name) {
let (ok, graft_name, _remainder) = syntax::split_graft(name);
if !ok {
return Err(format!("Invalid graft: {}", name));
}
let graft = config
.get_graft(graft_name)
.map_err(|_err| format!("Invalid graft: {}", graft_name))?;
let _graft_id = graft.get_id().ok_or(format!("Invalid graft: {}", name))?;
}
let mut var_idx: usize = 0;
let mut found = false;
if let Some(garden) = garden_idx {
for (idx, var) in config.gardens[garden].variables.iter().enumerate() {
if var.get_name() == name {
if let Some(var_value) = var.get_value() {
return Ok(Some(var_value.to_string()));
}
var_idx = idx;
found = true;
break;
}
}
if found {
let expr = config.gardens[garden].variables[var_idx]
.get_expr()
.to_string();
let result = tree_value(config, &expr, tree_idx, garden_idx);
config.gardens[garden].variables[var_idx].set_value(result.clone());
return Ok(Some(result));
}
}
found = false;
var_idx = 0;
for (idx, var) in config.trees[tree_idx].variables.iter().enumerate() {
if var.get_name() == name {
if let Some(var_value) = var.get_value() {
return Ok(Some(var_value.to_string()));
}
found = true;
var_idx = idx;
break;
}
}
if found {
let expr = config.trees[tree_idx].variables[var_idx]
.get_expr()
.to_string();
let result = tree_value(config, &expr, tree_idx, garden_idx);
config.trees[tree_idx].variables[var_idx].set_value(result.to_string());
return Ok(Some(result));
}
found = false;
var_idx = 0;
for (idx, var) in config.variables.iter().enumerate() {
if var.get_name() == name {
if let Some(var_value) = var.get_value() {
return Ok(Some(var_value.to_string()));
}
found = true;
var_idx = idx;
break;
}
}
if found {
let expr = config.variables[var_idx].get_expr().to_string();
let result = tree_value(config, &expr, tree_idx, garden_idx);
config.variables[var_idx].set_value(result.clone());
return Ok(Some(result));
}
if let Ok(env_value) = std::env::var(name) {
return Ok(Some(env_value));
}
Ok(Some("".to_string()))
}
fn _expand_tree_context_vars(
_app: &model::ApplicationContext,
_tree_context: &model::TreeContext,
_name: &str,
) -> Result<Option<String>, String> {
Ok(None)
}
fn expand_vars(config: &model::Configuration, name: &str) -> Result<Option<String>, String> {
if syntax::is_digit(name) {
return Ok(Some(format!("${}", name)));
}
let mut var_idx: usize = 0;
let mut found = false;
for (idx, var) in config.variables.iter().enumerate() {
if var.get_name() == name {
if let Some(var_value) = var.get_value() {
return Ok(Some(var_value.to_string()));
}
var_idx = idx;
found = true;
break;
}
}
if found {
let expr = config.variables[var_idx].get_expr().to_string();
let result = value(config, &expr);
config.variables[var_idx].set_value(result.clone());
return Ok(Some(result));
}
if let Ok(env_value) = std::env::var(name) {
return Ok(Some(env_value));
}
Ok(Some("".into()))
}
fn home_dir() -> Option<std::path::PathBuf> {
if let Ok(home) = std::env::var("HOME") {
return Some(std::path::PathBuf::from(home));
}
dirs::home_dir()
}
pub fn tree_value(
config: &model::Configuration,
expr: &str,
tree_idx: model::TreeIndex,
garden_idx: Option<model::GardenIndex>,
) -> String {
let expanded = shellexpand::full_with_context(expr, home_dir, |x| {
expand_tree_vars(config, tree_idx, garden_idx, x)
})
.unwrap_or_else(|_| Cow::from(expr))
.to_string();
exec_expression(&expanded)
}
pub fn value(config: &model::Configuration, expr: &str) -> String {
let expanded = shellexpand::full_with_context(expr, home_dir, |x| expand_vars(config, x))
.unwrap_or_else(|_| Cow::from(""))
.to_string();
exec_expression(&expanded)
}
pub fn exec_expression(string: &str) -> String {
if syntax::is_exec(string) {
let cmd = syntax::trim_exec(string);
let capture = subprocess::Exec::shell(cmd)
.stdout(subprocess::Redirection::Pipe)
.capture();
if let Ok(x) = capture {
return cmd::trim_stdout(&x);
}
return "".into();
}
string.into()
}
pub fn multi_variable(
config: &model::Configuration,
multi_var: &mut model::MultiVariable,
context: &model::TreeContext,
) -> Vec<String> {
let mut result = Vec::new();
for var in multi_var.iter() {
if let Some(value) = var.get_value() {
result.push(value.to_string());
continue;
}
let value = tree_value(config, var.get_expr(), context.tree, context.garden);
result.push(value.clone());
var.set_value(value);
}
result
}
pub fn environment(
config: &model::Configuration,
context: &model::TreeContext,
) -> Vec<(String, String)> {
let mut result = Vec::new();
let mut vars = Vec::new();
let mut ready = false;
if let Some(idx) = context.garden {
let garden = &config.gardens[idx];
for ctx in query::trees_from_garden(config, garden) {
for var in &config.trees[ctx.tree].environment {
vars.push((ctx.clone(), var.clone()));
}
}
for var in &garden.environment {
vars.push((context.clone(), var.clone()));
}
ready = true;
} else if let Some(idx) = context.group {
let group = &config.groups[idx];
for ctx in query::trees_from_group(config, None, group) {
for var in &config.trees[ctx.tree].environment {
vars.push((ctx.clone(), var.clone()));
}
}
ready = true;
}
if !ready {
for var in &config.trees[context.tree].environment {
vars.push((context.clone(), var.clone()));
}
}
let mut var_values = Vec::new();
for (ctx, var) in vars.iter_mut() {
var_values.push((
tree_value(config, var.get_name(), ctx.tree, ctx.garden),
multi_variable(config, var, ctx),
));
}
let mut values: HashMap<String, String> = HashMap::new();
for (var_name, env_values) in &var_values {
let mut name = var_name.clone();
let mut is_assign = false;
let mut is_append = false;
if name.ends_with('=') {
is_assign = true;
}
if name.ends_with('+') {
is_append = true;
}
if is_assign || is_append {
let len = name.len();
name.remove(len - 1);
}
for value in env_values {
let mut current = String::new();
let mut exists = false;
if let Some(map_value) = values.get(&name) {
current = map_value.clone();
exists = true;
}
if !exists {
let mut has_env = false;
if let Ok(env_value) = std::env::var(&name) {
let env_str: String = env_value;
if !env_str.is_empty() {
current = env_str;
has_env = true;
}
}
if has_env && !is_assign {
values.insert(name.clone(), current.clone());
} else {
values.insert(name.clone(), value.clone());
result.push((name.clone(), value.clone()));
continue;
}
}
if is_assign {
values.insert(name.clone(), value.clone());
result.push((name.clone(), value.clone()));
continue;
}
let mut path_values: Vec<String> = Vec::new();
if !is_append {
path_values.push(value.clone());
}
for path in current.split(':') {
path_values.push(path.into());
}
if is_append {
path_values.push(value.clone());
}
let path_value = path_values.join(":");
values.insert(name.clone(), path_value.clone());
result.push((name.clone(), path_value));
}
}
result
}
pub fn command(
app: &model::ApplicationContext,
context: &model::TreeContext,
name: &str,
) -> Vec<Vec<String>> {
let mut vars = Vec::new();
let mut result = Vec::new();
let config = match context.config {
Some(config_id) => app.get_config(config_id),
None => app.get_root_config(),
};
let pattern = match glob::Pattern::new(name) {
Ok(value) => value,
Err(_) => return result,
};
for var in &config.commands {
if pattern.matches(var.get_name()) {
vars.push(var.clone());
}
}
for var in &config.trees[context.tree].commands {
if pattern.matches(var.get_name()) {
vars.push(var.clone());
}
}
if let Some(garden) = context.garden {
for var in &config.gardens[garden].commands {
if pattern.matches(var.get_name()) {
vars.push(var.clone());
}
}
}
for var in vars.iter_mut() {
result.push(multi_variable(config, var, context));
}
result
}