use super::errors;
use super::eval;
use super::model;
use super::path;
use super::query;
use super::syntax;
pub fn resolve_trees(config: &model::Configuration, query: &str) -> Vec<model::TreeContext> {
let mut result = Vec::new();
let tree_query = model::TreeQuery::new(query);
let pattern = &tree_query.pattern;
if tree_query.include_gardens {
result = garden_trees(config, pattern);
if !result.is_empty() {
return result;
}
}
if tree_query.include_groups {
for group in &config.groups {
if !pattern.matches(group.get_name()) {
continue;
}
result.append(&mut trees_from_group(config, None, group));
}
if !result.is_empty() {
return result;
}
}
if tree_query.include_trees {
result.append(&mut trees(config, pattern));
if !result.is_empty() {
return result;
}
}
if tree_query.is_default {
if let Some(ctx) = tree_from_path(config, &tree_query.query) {
result.push(ctx);
}
}
result
}
pub fn garden_trees(
config: &model::Configuration,
pattern: &glob::Pattern,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for garden in &config.gardens {
if !pattern.matches(garden.get_name()) {
continue;
}
result.append(&mut trees_from_garden(config, garden));
}
result
}
pub fn trees_from_garden(
config: &model::Configuration,
garden: &model::Garden,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for group in &garden.groups {
let pattern = match glob::Pattern::new(group) {
Ok(value) => value,
Err(_) => continue,
};
for cfg_group in &config.groups {
if !pattern.matches(cfg_group.get_name()) {
continue;
}
result.append(&mut trees_from_group(
config,
Some(garden.get_index()),
cfg_group,
));
}
}
for tree in &garden.trees {
result.append(&mut trees_from_pattern(
config,
tree,
Some(garden.get_index()),
None,
));
}
result
}
pub fn trees_from_group(
config: &model::Configuration,
garden: Option<model::GardenIndex>,
group: &model::Group,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for tree in &group.members {
result.append(&mut trees_from_pattern(
config,
tree,
garden,
Some(group.get_index()),
));
}
result
}
pub fn tree_from_name(
config: &model::Configuration,
tree: &str,
garden_idx: Option<model::GardenIndex>,
group_idx: Option<model::GroupIndex>,
) -> Option<model::TreeContext> {
for (tree_idx, cfg_tree) in config.trees.iter().enumerate() {
if cfg_tree.get_name() == tree {
return Some(model::TreeContext::new(
tree_idx,
config.get_id(),
garden_idx,
group_idx,
));
}
}
if let Some(ctx) = tree_from_path(config, tree) {
return Some(ctx);
}
None
}
pub fn trees_from_pattern(
config: &model::Configuration,
tree: &str,
garden_idx: Option<model::GardenIndex>,
group_idx: Option<model::GroupIndex>,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
let pattern = match glob::Pattern::new(tree) {
Ok(value) => value,
Err(_) => return result,
};
for (tree_idx, cfg_tree) in config.trees.iter().enumerate() {
if pattern.matches(cfg_tree.get_name()) {
result.push(model::TreeContext::new(
tree_idx,
config.get_id(),
garden_idx,
group_idx,
));
}
}
if result.is_empty() {
if let Some(ctx) = tree_from_path(config, tree) {
result.push(ctx);
}
}
result
}
pub fn tree_from_path(config: &model::Configuration, path: &str) -> Option<model::TreeContext> {
tree_from_pathbuf(config, &std::path::PathBuf::from(path))
}
pub fn tree_from_pathbuf(
config: &model::Configuration,
path: &std::path::Path,
) -> Option<model::TreeContext> {
let pathbuf = match path.canonicalize() {
Ok(canon) => canon,
Err(_) => return None,
};
for (idx, tree) in config.trees.iter().enumerate() {
let tree_path = match tree.path_as_ref() {
Ok(value) => value,
Err(_) => continue,
};
let tree_canon = match std::path::PathBuf::from(tree_path).canonicalize() {
Ok(value) => value,
Err(_) => continue,
};
if pathbuf == tree_canon {
return Some(model::TreeContext::new(
idx as model::TreeIndex,
config.get_id(),
None,
None,
));
}
}
None
}
pub fn tree_name_from_path(
config: &model::Configuration,
path: &std::path::Path,
) -> Option<String> {
tree_name_from_abspath(config, &path::abspath(path))
}
pub fn tree_name_from_abspath(
config: &model::Configuration,
path: &std::path::Path,
) -> Option<String> {
for tree in &config.trees {
if !tree.path_is_valid() {
continue;
}
let tree_path_str = match tree.path_as_ref() {
Ok(path_str) => path_str,
Err(_) => continue,
};
let tree_pathbuf = std::path::PathBuf::from(tree_path_str);
if let Ok(canon_path) = tree_pathbuf.canonicalize() {
if canon_path == path {
return Some(tree.get_name().to_string());
}
}
}
None
}
fn trees(config: &model::Configuration, pattern: &glob::Pattern) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for (tree_idx, tree) in config.trees.iter().enumerate() {
if pattern.matches(tree.get_name()) {
result.push(model::TreeContext::new(
tree_idx,
config.get_id(),
None,
None,
));
}
}
result
}
pub fn tree_context(
config: &model::Configuration,
tree: &str,
garden: Option<&str>,
) -> Result<model::TreeContext, errors::GardenError> {
let mut ctx = model::TreeContext::new(0, config.get_id(), None, None);
if let Some(context) = tree_from_name(config, tree, None, None) {
ctx.tree = context.tree;
} else {
return Err(errors::GardenError::TreeNotFound { tree: tree.into() });
}
if let Some(garden_name) = garden {
let pattern = glob::Pattern::new(garden_name).map_err(|_| {
errors::GardenError::GardenPatternError {
garden: garden_name.into(),
}
})?;
let contexts = query::garden_trees(config, &pattern);
if contexts.is_empty() {
return Err(errors::GardenError::GardenNotFound {
garden: garden_name.into(),
});
}
let mut found = false;
for current_ctx in &contexts {
if current_ctx.tree == ctx.tree {
ctx.garden = current_ctx.garden;
found = true;
break;
}
}
if !found {
return Err(errors::GardenError::InvalidGardenArgument {
tree: tree.into(),
garden: garden_name.into(),
});
}
}
Ok(ctx)
}
pub fn find_tree(
app: &model::ApplicationContext,
id: model::ConfigId,
tree: &str,
garden: Option<&str>,
) -> Result<model::TreeContext, errors::GardenError> {
{
let config = app.get_config(id);
if let Some(graft_name) = syntax::graft_basename(tree) {
if syntax::is_graft(tree) && config.contains_graft(&graft_name) {
let graft = config.get_graft(&graft_name)?;
let graft_config = app.get_config(graft.get_id().unwrap());
if let Some(next_graft) = syntax::trim_graft(tree) {
return tree_context(graft_config, &next_graft, garden);
}
}
}
}
let config = app.get_config(id);
tree_context(config, tree, garden)
}
pub fn shared_worktree_path(config: &model::Configuration, ctx: &model::TreeContext) -> String {
let tree = &config.trees[ctx.tree];
if tree.is_worktree {
let worktree = eval::tree_value(config, tree.worktree.get_expr(), ctx.tree, ctx.garden);
if let Some(parent_ctx) = query::tree_from_name(config, &worktree, ctx.garden, ctx.group) {
if let Ok(path) = config.trees[parent_ctx.tree].path_as_ref() {
return path.to_string();
}
}
}
if let Ok(path) = tree.path_as_ref() {
return path.to_string();
}
tree.get_name().to_string()
}