use super::errors;
use super::eval;
use super::model;
use super::path;
use super::query;
use super::syntax;
pub fn resolve_trees(
app_context: &model::ApplicationContext,
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(app_context, config, pattern);
if !result.is_empty() {
return result;
}
}
if tree_query.include_groups {
for (name, group) in &config.groups {
if !pattern.matches(name) {
continue;
}
result.append(&mut trees_from_group(app_context, config, None, group));
}
if !result.is_empty() {
return result;
}
}
if tree_query.include_trees {
if syntax::is_graft(query) {
if let Ok((graft_id, remainder)) = config.get_graft_id(query) {
result.append(&mut resolve_trees(
app_context,
app_context.get_config(graft_id),
remainder,
));
}
} else {
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(
app_context: &model::ApplicationContext,
config: &model::Configuration,
pattern: &glob::Pattern,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for (name, garden) in &config.gardens {
if !pattern.matches(name) {
continue;
}
result.append(&mut trees_from_garden(app_context, config, garden));
}
result
}
pub fn trees_from_garden(
app_context: &model::ApplicationContext,
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 (name, cfg_group) in &config.groups {
if !pattern.matches(name) {
continue;
}
result.append(&mut trees_from_group(
app_context,
config,
Some(garden.get_name()),
cfg_group,
));
}
}
for tree in &garden.trees {
result.append(&mut trees_from_pattern(
app_context,
config,
tree,
Some(garden.get_name()),
None,
));
}
result
}
pub fn trees_from_group(
app_context: &model::ApplicationContext,
config: &model::Configuration,
garden: Option<&model::GardenName>,
group: &model::Group,
) -> Vec<model::TreeContext> {
let mut result = Vec::new();
for tree in &group.members {
result.append(&mut trees_from_pattern(
app_context,
config,
tree,
garden,
Some(group.get_name()),
));
}
result
}
pub fn tree_from_name(
config: &model::Configuration,
tree_name: &str,
garden_name: Option<&model::GardenName>,
group: Option<&model::GardenName>,
) -> Option<model::TreeContext> {
if let Some(tree) = config.trees.get(tree_name) {
return Some(model::TreeContext::new(
tree.get_name(),
config.get_id(),
garden_name.cloned(),
group.cloned(),
));
}
if let Some(ctx) = tree_from_path(config, tree_name) {
return Some(ctx);
}
None
}
pub fn trees_from_pattern(
app_context: &model::ApplicationContext,
config: &model::Configuration,
tree: &str,
garden_name: Option<&model::GardenName>,
group: Option<&model::GroupName>,
) -> Vec<model::TreeContext> {
if syntax::is_graft(tree) {
if let Ok((graft_id, remainder)) = config.get_graft_id(tree) {
return trees_from_pattern(
app_context,
app_context.get_config(graft_id),
remainder,
garden_name,
group,
);
}
}
let mut result = Vec::new();
let pattern = match glob::Pattern::new(tree) {
Ok(value) => value,
Err(_) => return result,
};
for (tree_name, cfg_tree) in &config.trees {
if pattern.matches(tree_name) {
result.push(model::TreeContext::new(
cfg_tree.get_name(),
config.get_id(),
garden_name.cloned(),
group.cloned(),
));
}
}
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 (name, tree) in &config.trees {
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(name, 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.values() {
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_name, tree) in &config.trees {
if pattern.matches(tree_name) {
result.push(model::TreeContext::new(
tree.get_name(),
config.get_id(),
None,
None,
));
}
}
result
}
pub fn tree_context(
app_context: &model::ApplicationContext,
config: &model::Configuration,
tree: &str,
garden: Option<&str>,
) -> Result<model::TreeContext, errors::GardenError> {
let mut ctx = model::TreeContext::new("", 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(app_context, 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.clone();
found = true;
break;
}
}
if !found {
return Err(errors::GardenError::InvalidGardenArgument {
tree: tree.into(),
garden: garden_name.into(),
});
}
}
Ok(ctx)
}
pub fn find_tree(
app_context: &model::ApplicationContext,
id: model::ConfigId,
tree: &str,
garden: Option<&str>,
) -> Result<model::TreeContext, errors::GardenError> {
{
let config = app_context.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_id = graft
.get_id()
.ok_or(errors::GardenError::ConfigurationError(format!(
"invalid graft: {graft_name}"
)))?;
if let Some(next_graft) = syntax::trim_graft(tree) {
return find_tree(app_context, graft_id, &next_graft, garden);
}
}
}
}
let config = app_context.get_config(id);
tree_context(app_context, config, tree, garden)
}
pub fn shared_worktree_path(
app_context: &model::ApplicationContext,
config: &model::Configuration,
ctx: &model::TreeContext,
) -> String {
let config = match ctx.config {
Some(config_id) => app_context.get_config(config_id),
None => config,
};
let tree = match config.trees.get(&ctx.tree) {
Some(tree) => tree,
None => match app_context.get_root_config().trees.get(&ctx.tree) {
Some(tree) => tree,
None => return String::new(),
},
};
if tree.is_worktree {
let worktree = eval::tree_value(
app_context,
config,
tree.worktree.get_expr(),
&ctx.tree,
ctx.garden.as_ref(),
);
if let Some(parent_ctx) =
query::tree_from_name(config, &worktree, ctx.garden.as_ref(), ctx.group.as_ref())
{
if let Some(path) = config
.trees
.get(&parent_ctx.tree)
.and_then(|tree| tree.path_as_ref().ok())
{
return path.to_string();
}
}
}
if let Ok(path) = tree.path_as_ref() {
return path.to_string();
}
tree.get_name().to_string()
}