use super::cli;
use super::collections::{append_hashmap, append_indexset};
use super::config;
use super::errors;
use super::eval;
use super::path;
use super::syntax;
use indexmap::{IndexMap, IndexSet};
use indextree::{Arena, NodeId};
use is_terminal::IsTerminal;
use std::cell::RefCell;
use std::collections::HashMap;
use std::str::FromStr;
use strum::VariantNames;
use strum_macros;
use which::which;
pub type TreeName = String;
pub type GroupName = String;
pub type GardenName = String;
pub type GraftName = String;
pub type ConfigId = NodeId;
#[derive(Clone, Debug, Default)]
pub struct Variable {
expr: String,
value: RefCell<Option<String>>,
}
impl_display_brief!(Variable);
impl Variable {
pub fn new(expr: String, value: Option<String>) -> Self {
Variable {
expr,
value: RefCell::new(value),
}
}
pub fn is_empty(&self) -> bool {
self.expr.is_empty()
}
pub fn get_expr(&self) -> &String {
&self.expr
}
pub fn get_expr_mut(&mut self) -> &mut String {
&mut self.expr
}
pub fn set_expr(&mut self, expr: String) {
self.expr = expr;
}
pub fn set_value(&self, value: String) {
*self.value.borrow_mut() = Some(value);
}
pub fn get_value(&self) -> Option<&String> {
let ptr = self.value.as_ptr();
unsafe { (*ptr).as_ref() }
}
pub fn reset(&self) {
*self.value.borrow_mut() = None;
}
}
pub type MultiVariableHashMap = HashMap<String, Vec<Variable>>;
fn reset_hashmap_variables(vec_variables: &MultiVariableHashMap) {
for variables in vec_variables.values() {
for variable in variables {
variable.reset();
}
}
}
pub type VariableHashMap = HashMap<String, Variable>;
#[derive(Clone, Debug)]
pub struct NamedVariable {
name: String,
variable: Variable,
}
impl_display_brief!(NamedVariable);
impl NamedVariable {
pub fn new(name: String, expr: String, value: Option<String>) -> Self {
NamedVariable {
name,
variable: Variable::new(expr, value),
}
}
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_expr(&self) -> &String {
self.variable.get_expr()
}
pub fn set_expr(&mut self, expr: String) {
self.variable.set_expr(expr);
}
pub fn set_value(&self, value: String) {
self.variable.set_value(value);
}
pub fn get_value(&self) -> Option<&String> {
self.variable.get_value()
}
pub fn reset(&self) {
self.variable.reset();
}
}
#[derive(Clone, Debug)]
pub struct MultiVariable {
name: String,
variables: Vec<Variable>,
}
impl_display!(MultiVariable);
impl MultiVariable {
pub fn new(name: String, variables: Vec<Variable>) -> Self {
MultiVariable { name, variables }
}
pub fn get(&self, idx: usize) -> &Variable {
&self.variables[idx]
}
pub fn get_name(&self) -> &String {
&self.name
}
pub fn len(&self) -> usize {
self.variables.len()
}
pub fn is_empty(&self) -> bool {
self.variables.is_empty()
}
pub fn reset(&self) {
for var in &self.variables {
var.reset();
}
}
pub fn iter(&self) -> std::slice::Iter<Variable> {
self.variables.iter()
}
}
#[derive(Clone, Debug, Default)]
pub struct Tree {
pub commands: MultiVariableHashMap,
pub environment: Vec<MultiVariable>,
pub gitconfig: MultiVariableHashMap,
pub remotes: VariableHashMap,
pub symlink: Variable,
pub templates: IndexSet<String>,
pub variables: VariableHashMap,
pub branch: Variable,
pub branches: VariableHashMap,
pub worktree: Variable,
pub clone_depth: i64,
pub is_single_branch: bool,
pub is_symlink: bool,
pub is_bare_repository: bool,
pub is_worktree: bool,
name: String,
path: Variable,
}
impl_display!(Tree);
impl Tree {
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_name_mut(&mut self) -> &mut String {
&mut self.name
}
pub fn get_path(&self) -> &Variable {
&self.path
}
pub fn get_path_mut(&mut self) -> &mut Variable {
&mut self.path
}
pub fn path_is_valid(&self) -> bool {
self.path.get_value().is_some()
}
pub fn canonical_pathbuf(&self) -> Option<std::path::PathBuf> {
if let Some(pathbuf) = self.pathbuf() {
if let Ok(canon_path) = pathbuf.canonicalize() {
return Some(canon_path);
}
}
None
}
pub fn pathbuf(&self) -> Option<std::path::PathBuf> {
if !self.path_is_valid() {
return None;
}
self.path.get_value().map(std::path::PathBuf::from)
}
pub fn path_as_ref(&self) -> Result<&String, errors::GardenError> {
match self.path.get_value() {
Some(value) => Ok(value),
None => Err(errors::GardenError::ConfigurationError(format!(
"unset tree path for {}",
self.name
))),
}
}
pub fn symlink_as_ref(&self) -> Result<&String, errors::GardenError> {
match self.symlink.get_value() {
Some(value) => Ok(value),
None => Err(errors::GardenError::ConfigurationError(format!(
"unset tree path for {}",
self.name
))),
}
}
pub fn reset_variables(&self) {
for var in self.variables.values() {
var.reset();
}
for env in &self.environment {
env.reset();
}
reset_hashmap_variables(&self.gitconfig);
reset_hashmap_variables(&self.commands);
}
pub fn clone_from_tree(&mut self, tree: &Tree) {
append_hashmap(&mut self.commands, &tree.commands);
append_hashmap(&mut self.gitconfig, &tree.gitconfig);
append_hashmap(&mut self.variables, &tree.variables);
append_hashmap(&mut self.remotes, &tree.remotes);
append_indexset(&mut self.templates, &tree.templates);
self.environment.append(&mut tree.environment.clone());
if tree.clone_depth > 0 {
self.clone_depth = tree.clone_depth;
}
if tree.is_bare_repository {
self.is_bare_repository = tree.is_bare_repository;
}
if tree.is_single_branch {
self.is_single_branch = tree.is_single_branch;
}
if tree.is_worktree {
self.is_worktree = tree.is_worktree;
}
if tree.is_symlink {
self.is_symlink = tree.is_symlink;
}
if !tree.branch.is_empty() {
self.branch = tree.branch.clone();
}
if !tree.symlink.is_empty() {
self.symlink = tree.symlink.clone();
}
if !tree.worktree.is_empty() {
self.worktree = tree.worktree.clone();
}
self.update_flags();
}
pub fn update_flags(&mut self) {
if !self.symlink.is_empty() {
self.is_symlink = true;
}
if !self.worktree.is_empty() {
self.is_worktree = true;
}
if self.is_worktree {
self.is_bare_repository = false;
}
}
}
#[derive(Clone, Debug, Default)]
pub struct Group {
name: String,
pub members: IndexSet<String>,
}
impl_display!(Group);
impl Group {
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_name_owned(&self) -> String {
self.get_name().to_owned()
}
pub fn get_name_mut(&mut self) -> &mut String {
&mut self.name
}
}
#[derive(Clone, Debug, Default)]
pub struct Template {
pub tree: Tree,
pub extend: IndexSet<String>,
name: String,
}
impl_display!(Template);
impl Template {
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_name_mut(&mut self) -> &mut String {
&mut self.name
}
pub fn apply(&self, tree: &mut Tree) {
tree.clone_from_tree(&self.tree);
}
}
#[derive(Clone, Debug, Default)]
pub struct Garden {
pub commands: MultiVariableHashMap,
pub environment: Vec<MultiVariable>,
pub gitconfig: MultiVariableHashMap,
pub groups: IndexSet<String>,
pub trees: IndexSet<String>,
pub variables: VariableHashMap,
name: GardenName,
}
impl_display!(Garden);
impl Garden {
pub fn get_name(&self) -> &GardenName {
&self.name
}
pub fn get_name_mut(&mut self) -> &mut String {
&mut self.name
}
}
fn get_default_shell() -> String {
if which("zsh").is_ok() {
"zsh"
} else if which("bash").is_ok() {
"bash"
} else {
"sh"
}
.to_string()
}
#[derive(Clone, Debug, Default)]
pub struct Configuration {
pub commands: MultiVariableHashMap,
pub debug: HashMap<String, u8>,
pub environment: Vec<MultiVariable>,
pub gardens: IndexMap<GardenName, Garden>,
pub grafts: IndexMap<GraftName, Graft>,
pub groups: IndexMap<GroupName, Group>,
pub path: Option<std::path::PathBuf>,
pub dirname: Option<std::path::PathBuf>,
pub root: Variable,
pub root_path: std::path::PathBuf,
pub shell: String,
pub templates: HashMap<String, Template>,
pub tree_search_path: Vec<std::path::PathBuf>,
pub trees: IndexMap<TreeName, Tree>,
pub variables: VariableHashMap,
pub verbose: u8,
pub parent_id: Option<ConfigId>,
id: Option<ConfigId>,
}
impl_display!(Configuration);
impl Configuration {
pub fn new() -> Self {
Configuration {
id: None,
parent_id: None,
shell: get_default_shell(),
..std::default::Default::default()
}
}
pub fn initialize(&mut self, app_context: &ApplicationContext) {
let expr = String::from(self.root.get_expr());
let value = eval::value(app_context, self, &expr);
self.root_path = std::path::PathBuf::from(&value);
if let Ok(root_path_canon) = self.root_path.canonicalize() {
self.root_path = root_path_canon;
}
self.root.set_value(value);
self.update_tree_paths(app_context);
self.reset();
}
pub fn update(
&mut self,
app_context: &ApplicationContext,
config: Option<&std::path::PathBuf>,
root: Option<&std::path::PathBuf>,
config_verbose: u8,
parent: Option<ConfigId>,
) -> Result<(), errors::GardenError> {
if let Some(parent_id) = parent {
self.set_parent(parent_id);
}
self.verbose = config_verbose;
if let Some(root_path) = root {
self.root.set_expr(root_path.to_string_lossy().to_string());
}
let mut basename: String = "garden.yaml".into();
let mut found = false;
if let Some(config_path) = config {
if config_path.is_file() || config_path.is_absolute() {
self.set_path(config_path.to_path_buf());
found = true;
} else {
basename = config_path.to_string_lossy().into();
}
}
if !found {
for entry in config::search_path() {
let mut candidate = entry.to_path_buf();
candidate.push(basename.clone());
if candidate.exists() {
self.set_path(candidate);
found = true;
break;
}
}
}
if config_verbose > 0 {
debug!(
"config: path: {:?}, root: {:?}, found: {}",
self.path, self.root, found
);
}
if found {
let config_path = self.get_path()?;
if let Ok(config_string) = std::fs::read_to_string(config_path) {
config::parse(app_context, &config_string, config_verbose, self)?;
} else {
return Ok(());
}
}
if self.root.get_expr().is_empty() {
self.root.set_expr(path::current_dir_string());
}
Ok(())
}
pub fn update_options(
&mut self,
options: &cli::MainOptions,
) -> Result<(), errors::GardenError> {
let config_verbose = options.debug_level("config");
if self.path.is_none() {
error!("unable to find a configuration file -- use --config <path>");
}
if config_verbose > 1 {
eprintln!("config: {:?}", self.get_path()?);
}
if config_verbose > 2 {
debug!("{}", self);
}
for key in &options.debug {
let current = *self.debug.get(key).unwrap_or(&0);
self.debug.insert(key.into(), current + 1);
}
for k_eq_v in &options.define {
let name: String;
let expr: String;
let values: Vec<&str> = k_eq_v.splitn(2, '=').collect();
if values.len() == 1 {
name = values[0].to_string();
expr = string!("");
} else if values.len() == 2 {
name = values[0].to_string();
expr = values[1].to_string();
} else {
error!("unable to split '{}'", k_eq_v);
}
self.variables.insert(name, Variable::new(expr, None));
}
Ok(())
}
pub fn reset(&mut self) {
self.reset_variables();
self.reset_builtin_variables()
}
fn reset_builtin_variables(&mut self) {
if let Some(var) = self.variables.get_mut("GARDEN_ROOT") {
if let Some(value) = self.root.get_value() {
var.set_expr(value.into());
var.set_value(value.into());
}
}
for tree in self.trees.values_mut() {
let tree_name = String::from(tree.get_name());
if let Some(var) = tree.variables.get_mut("TREE_NAME") {
var.set_expr(tree_name.to_string());
var.set_value(tree_name);
}
let tree_path = match tree.path_as_ref() {
Ok(path) => String::from(path),
Err(_) => continue,
};
if let Some(var) = tree.variables.get_mut("TREE_PATH") {
var.set_expr(tree_path.to_string());
var.set_value(tree_path);
}
}
}
fn update_tree_paths(&mut self, app_context: &ApplicationContext) {
let mut path_values = Vec::new();
let mut symlink_values = Vec::new();
for (name, tree) in &self.trees {
path_values.push((name.clone(), tree.path.get_expr().clone()));
if tree.is_symlink {
symlink_values.push((name.clone(), tree.symlink.get_expr().clone()));
}
}
for (name, value) in &path_values {
let result = self.eval_tree_path(app_context, value);
if let Some(tree) = self.trees.get_mut(name) {
tree.path.set_value(result);
}
}
for (name, value) in &symlink_values {
let result = self.eval_tree_path(app_context, value);
if let Some(tree) = self.trees.get_mut(name) {
tree.symlink.set_value(result);
}
}
}
pub fn tree_path(&self, path: &str) -> String {
if std::path::PathBuf::from(path).is_absolute() {
path.into()
} else {
let mut path_buf = self.root_path.to_path_buf();
path_buf.push(path);
path_buf.to_string_lossy().into()
}
}
pub fn relative_pathbuf(&self, path: &str) -> std::path::PathBuf {
let pathbuf = std::path::PathBuf::from(path);
if pathbuf.is_absolute() {
if let Ok(pathbuf_canon) = pathbuf.canonicalize() {
pathbuf_canon
} else {
pathbuf
}
} else {
let mut path_buf = self.root_path.to_path_buf();
path_buf.push(path);
path_buf
}
}
pub fn eval_tree_path(&mut self, app_context: &ApplicationContext, path: &str) -> String {
let value = eval::value(app_context, self, path);
self.tree_path(&value)
}
pub fn config_pathbuf(&self, path: &str) -> Option<std::path::PathBuf> {
let path_buf = std::path::PathBuf::from(path);
if path_buf.is_absolute() {
Some(path_buf)
} else if let Some(dirname) = self.dirname.as_ref() {
let mut abs_path_buf = dirname.to_path_buf();
abs_path_buf.push(path_buf);
Some(abs_path_buf)
} else {
None
}
}
pub fn config_pathbuf_from_include(
&self,
include_path: &std::path::Path,
path: &str,
) -> Option<std::path::PathBuf> {
let mut path_buf = std::path::PathBuf::from(path);
if path_buf.is_absolute() {
return Some(path_buf);
}
if let Some(dirname) = include_path.parent() {
path_buf = dirname.to_path_buf();
path_buf.push(path);
if path_buf.exists() {
return Some(path_buf);
}
}
self.config_pathbuf(path)
}
pub fn config_path(&self, path: &str) -> String {
if let Some(path_buf) = self.config_pathbuf(path) {
path_buf.to_string_lossy().to_string()
} else {
self.tree_path(path)
}
}
pub fn eval_config_path(&self, app_context: &ApplicationContext, path: &str) -> String {
let value = eval::value(app_context, self, path);
self.config_path(&value)
}
pub fn eval_config_pathbuf(
&self,
app_context: &ApplicationContext,
path: &str,
) -> Option<std::path::PathBuf> {
let value = eval::value(app_context, self, path);
self.config_pathbuf(&value)
}
pub fn eval_config_pathbuf_from_include(
&self,
app_context: &ApplicationContext,
include_path: Option<&std::path::Path>,
path: &str,
) -> Option<std::path::PathBuf> {
let value = eval::value(app_context, self, path);
if let Some(include_path) = include_path {
self.config_pathbuf_from_include(include_path, &value)
} else {
self.config_pathbuf(&value)
}
.or_else(|| Some(std::path::PathBuf::from(&value)))
}
pub fn reset_variables(&mut self) {
for var in self.variables.values() {
var.reset();
}
for env in &self.environment {
env.reset();
}
reset_hashmap_variables(&self.commands);
for tree in self.trees.values() {
tree.reset_variables();
}
}
pub fn set_id(&mut self, id: ConfigId) {
self.id = Some(id);
}
pub fn get_id(&self) -> Option<ConfigId> {
self.id
}
pub fn set_parent(&mut self, id: ConfigId) {
self.parent_id = Some(id);
}
pub fn set_path(&mut self, path: std::path::PathBuf) {
let mut dirname = path.clone();
dirname.pop();
self.dirname = Some(dirname);
self.path = Some(path);
}
pub fn get_path(&self) -> Result<&std::path::PathBuf, errors::GardenError> {
self.path
.as_ref()
.ok_or_else(|| errors::GardenError::AssertionError("cfg.path is unset".into()))
}
pub fn get_path_for_display(&self) -> String {
let default_pathbuf = std::path::PathBuf::from(".");
self.path
.as_ref()
.unwrap_or(&default_pathbuf)
.display()
.to_string()
}
pub fn contains_graft(&self, name: &str) -> bool {
let graft_name = syntax::trim(name);
self.grafts.contains_key(graft_name)
}
pub fn get_graft(&self, name: &str) -> Result<&Graft, errors::GardenError> {
let graft_name = syntax::trim(name);
self.grafts.get(graft_name).ok_or_else(|| {
errors::GardenError::ConfigurationError(format!("{name}: no such graft"))
})
}
pub fn get_graft_id<'a>(
&self,
value: &'a str,
) -> Result<(ConfigId, &'a str), errors::GardenError> {
let (ok, graft_name, remainder) = syntax::split_graft(value);
if !ok {
return Err(errors::GardenError::ConfigurationError(format!(
"{value}: invalid graft expression"
)));
}
let graft = self.get_graft(graft_name)?;
let graft_id = graft
.get_id()
.ok_or(errors::GardenError::ConfigurationError(format!(
"{graft_name}: no such graft"
)))?;
Ok((graft_id, remainder))
}
pub fn get_tree(&self, name: &str) -> Option<&Tree> {
self.trees.get(name)
}
pub fn get_tree_pathbuf(&self, tree_name: &str) -> Option<std::path::PathBuf> {
self.get_tree(tree_name)
.map(|tree| tree.canonical_pathbuf())
.unwrap_or(None)
}
}
#[derive(Clone, Debug, Default)]
pub struct Graft {
id: Option<ConfigId>,
name: String,
pub root: String,
pub config: String,
}
impl_display!(Graft);
impl Graft {
pub fn new(name: String, root: String, config: String) -> Self {
Graft {
id: None,
name,
root,
config,
}
}
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_id(&self) -> Option<ConfigId> {
self.id
}
pub fn set_id(&mut self, id: ConfigId) {
self.id = Some(id);
}
}
#[derive(Clone, Debug)]
pub struct EvalContext {
pub config: ConfigId,
pub tree: Option<TreeName>,
pub garden: Option<GardenName>,
pub group: Option<GroupName>,
}
impl_display_brief!(EvalContext);
impl EvalContext {
pub fn new(
config: ConfigId,
tree: Option<TreeName>,
garden: Option<GardenName>,
group: Option<GroupName>,
) -> Self {
EvalContext {
config,
tree,
garden,
group,
}
}
}
#[derive(Clone, Debug)]
pub struct TreeContext {
pub tree: TreeName,
pub config: Option<ConfigId>,
pub garden: Option<GardenName>,
pub group: Option<String>,
}
impl_display_brief!(TreeContext);
impl TreeContext {
pub fn new(
tree: &str,
config: Option<ConfigId>,
garden: Option<GardenName>,
group: Option<String>,
) -> Self {
TreeContext {
tree: TreeName::from(tree),
config,
garden,
group,
}
}
}
#[derive(Debug, Default)]
pub struct TreeQuery {
pub query: String,
pub pattern: glob::Pattern,
pub is_default: bool,
pub is_garden: bool,
pub is_group: bool,
pub is_tree: bool,
pub include_gardens: bool,
pub include_groups: bool,
pub include_trees: bool,
}
impl_display_brief!(TreeQuery);
impl TreeQuery {
pub fn new(query: &str) -> Self {
let mut is_default = false;
let mut is_tree = false;
let mut is_garden = false;
let mut is_group = false;
let mut include_gardens = true;
let mut include_groups = true;
let mut include_trees = true;
if syntax::is_garden(query) {
is_garden = true;
include_groups = false;
include_trees = false;
} else if syntax::is_group(query) {
is_group = true;
include_gardens = false;
include_trees = false;
} else if syntax::is_tree(query) {
is_tree = true;
include_gardens = false;
include_groups = false;
} else {
is_default = true;
}
let glob_pattern = syntax::trim(query);
let pattern = glob::Pattern::new(glob_pattern).unwrap_or_default();
TreeQuery {
query: query.into(),
is_default,
is_garden,
is_group,
is_tree,
include_gardens,
include_groups,
include_trees,
pattern,
}
}
}
#[derive(
Clone,
Debug,
Default,
PartialEq,
Eq,
strum_macros::EnumString,
strum_macros::Display,
strum_macros::EnumVariantNames,
)]
#[strum(ascii_case_insensitive, serialize_all = "kebab-case")]
pub enum ColorMode {
#[default]
Auto,
#[strum(
serialize = "1",
serialize = "on",
serialize = "true",
serialize = "always",
serialize = "y",
serialize = "yes"
)]
On,
#[strum(
serialize = "0",
serialize = "off",
serialize = "false",
serialize = "never",
serialize = "n",
serialize = "no"
)]
Off,
}
impl ColorMode {
pub fn parse_from_str(string: &str) -> Result<ColorMode, String> {
ColorMode::from_str(string).map_err(|_| format!("choices are {:?}", Self::VARIANTS))
}
pub fn is_enabled(&self) -> bool {
match self {
ColorMode::Auto => std::io::stdout().is_terminal(),
ColorMode::Off => false,
ColorMode::On => true,
}
}
pub fn update(&mut self) {
if *self == ColorMode::Auto {
if self.is_enabled() {
*self = ColorMode::On;
} else {
*self = ColorMode::Off;
}
}
if *self == ColorMode::Off {
yansi::Paint::disable();
}
}
}
pub type Color<T> = yansi::Paint<T>;
pub fn display_missing_tree(tree: &Tree, path: &str, verbose: u8) -> String {
if verbose > 0 {
format!(
"{} {} {} {}",
Color::black("#").bold(),
Color::black(&tree.name).bold(),
Color::black(&path).bold(),
Color::black("(skipped)").bold()
)
} else {
format!(
"{} {} {}",
Color::black("#").bold(),
Color::black(&tree.name).bold(),
Color::black("(skipped)").bold()
)
}
}
pub fn display_tree(tree: &Tree, path: &str, verbose: u8) -> String {
if verbose > 0 {
format!(
"{} {} {}",
Color::cyan("#"),
Color::blue(&tree.name).bold(),
Color::blue(&path)
)
} else {
format!("{} {}", Color::cyan("#"), Color::blue(&tree.name).bold())
}
}
pub fn print_tree(tree: &Tree, verbose: u8, quiet: bool) -> bool {
if let Ok(path) = tree.path_as_ref() {
if !std::path::PathBuf::from(&path).exists() {
if !quiet {
eprintln!("{}", display_missing_tree(tree, path, verbose));
}
return false;
}
print_tree_details(tree, verbose, quiet);
return true;
} else if !quiet {
eprintln!("{}", display_missing_tree(tree, "[invalid-path]", verbose));
}
false
}
pub fn print_tree_details(tree: &Tree, verbose: u8, quiet: bool) {
if !quiet {
if let Ok(path) = tree.path_as_ref() {
eprintln!("{}", display_tree(tree, path, verbose));
}
}
}
#[derive(Clone, Debug)]
pub struct ApplicationContext {
pub options: cli::MainOptions,
arena: RefCell<Arena<Configuration>>,
root_id: ConfigId,
}
impl_display!(ApplicationContext);
impl ApplicationContext {
pub fn new(options: cli::MainOptions) -> Self {
let mut arena = Arena::new();
let config = Configuration::new();
let root_id = arena.new_node(config);
let app_context = ApplicationContext {
arena: RefCell::new(arena),
root_id,
options,
};
let config = app_context.get_root_config_mut();
config.set_id(root_id);
app_context
}
pub fn from_options(options: &cli::MainOptions) -> Result<Self, errors::GardenError> {
let app_context = Self::new(options.clone());
let config_verbose = options.debug_level("config");
app_context.get_root_config_mut().update(
&app_context,
options.config.as_ref(),
options.root.as_ref(),
config_verbose,
None,
)?;
app_context.get_root_config_mut().update_options(options)?;
config::read_grafts(&app_context)?;
Ok(app_context)
}
pub fn from_path_and_root(
pathbuf: std::path::PathBuf,
root: Option<&std::path::PathBuf>,
) -> Result<Self, errors::GardenError> {
let options = cli::MainOptions::new();
let app_context = Self::new(options.clone());
let config_verbose = options.debug_level("config");
app_context.get_root_config_mut().update(
&app_context,
Some(&pathbuf),
root,
config_verbose,
None,
)?;
config::read_grafts(&app_context)?;
Ok(app_context)
}
pub fn from_path(pathbuf: std::path::PathBuf) -> Result<Self, errors::GardenError> {
if let Some(root_dir) = pathbuf.parent().map(std::path::Path::to_owned) {
Self::from_path_and_root(pathbuf, Some(&root_dir))
} else {
Self::from_path_and_root(pathbuf, None)
}
}
pub fn from_path_string(path: &str) -> Result<Self, errors::GardenError> {
Self::from_path(std::path::PathBuf::from(path))
}
pub fn from_string(string: &str) -> Result<Self, errors::GardenError> {
let options = cli::MainOptions::new();
let app_context = Self::new(options);
config::parse(&app_context, string, 0, app_context.get_root_config_mut())?;
config::read_grafts(&app_context)?;
Ok(app_context)
}
pub fn get_config(&self, id: ConfigId) -> &Configuration {
unsafe { (*self.arena.as_ptr()).get(id).unwrap().get() }
}
#[allow(clippy::mut_from_ref)]
pub fn get_config_mut(&self, id: ConfigId) -> &mut Configuration {
unsafe { (*self.arena.as_ptr()).get_mut(id).unwrap().get_mut() }
}
pub fn get_root_id(&self) -> ConfigId {
self.root_id
}
pub fn get_root_config(&self) -> &Configuration {
self.get_config(self.get_root_id())
}
pub fn get_root_config_mut(&self) -> &mut Configuration {
self.get_config_mut(self.get_root_id())
}
pub fn add_graft(&self, parent: ConfigId, config: Configuration) -> ConfigId {
let graft_id = self.arena.borrow_mut().new_node(config); parent.append(graft_id, &mut self.arena.borrow_mut());
self.get_config_mut(graft_id).set_id(graft_id);
graft_id
}
pub fn add_graft_config(
&self,
config_id: ConfigId,
graft_name: &str,
path: &std::path::Path,
root: Option<&std::path::PathBuf>,
) -> Result<(), errors::GardenError> {
let path = path.to_path_buf();
let config_verbose = self.options.debug_level("config");
let mut graft_config = Configuration::new();
graft_config.update(self, Some(&path), root, config_verbose, Some(config_id))?;
let graft_id = self.add_graft(config_id, graft_config);
if let Some(graft_config) = self.get_config_mut(config_id).grafts.get_mut(graft_name) {
graft_config.set_id(graft_id);
}
config::read_grafts_recursive(self, graft_id)?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GitTreeType {
Parent, Worktree(String), Tree, Bare, }
impl_display!(GitTreeType);
#[derive(Clone, Debug)]
pub struct GitTreeDetails {
pub branch: String,
pub tree_type: GitTreeType,
}
impl_display!(GitTreeDetails);