Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/commands/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ use anyhow::Result;
pub struct CommandContext {
/// The loaded configuration
pub config: Config,
/// Optional tag filter for repositories
pub tag: Option<String>,
/// Tag filters for repositories (can include multiple tags)
pub tag: Vec<String>,
/// Tags to exclude from repositories
pub exclude_tag: Vec<String>,
/// Whether to execute operations in parallel
pub parallel: bool,
/// Optional list of specific repository names to operate on
Expand Down
77 changes: 46 additions & 31 deletions src/commands/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,31 @@ pub struct CloneCommand;
#[async_trait]
impl Command for CloneCommand {
async fn execute(&self, context: &CommandContext) -> Result<()> {
let repositories = context
.config
.filter_repositories(context.tag.as_deref(), context.repos.as_deref());
let repositories = context.config.filter_repositories(
&context.tag,
&context.exclude_tag,
context.repos.as_deref(),
);

if repositories.is_empty() {
let filter_desc = match (&context.tag, &context.repos) {
(Some(tag), Some(repos)) => format!("tag '{tag}' and repositories {repos:?}"),
(Some(tag), None) => format!("tag '{tag}'"),
(None, Some(repos)) => format!("repositories {repos:?}"),
(None, None) => "no repositories found".to_string(),
let mut filter_parts = Vec::new();

if !context.tag.is_empty() {
filter_parts.push(format!("tags {:?}", context.tag));
}
if !context.exclude_tag.is_empty() {
filter_parts.push(format!("excluding tags {:?}", context.exclude_tag));
}
if let Some(repos) = &context.repos {
filter_parts.push(format!("repositories {:?}", repos));
}

let filter_desc = if filter_parts.is_empty() {
"no repositories found".to_string()
} else {
filter_parts.join(" and ")
};

println!(
"{}",
format!("No repositories found with {filter_desc}").yellow()
Expand Down Expand Up @@ -141,15 +155,16 @@ mod tests {
}

/// Helper to create CommandContext for testing
fn create_command_context(
fn create_context(
config: Config,
tag: Option<String>,
tag: Vec<String>,
repos: Option<Vec<String>>,
parallel: bool,
) -> CommandContext {
CommandContext {
config,
tag,
exclude_tag: Vec::new(),
repos,
parallel,
}
Expand All @@ -161,7 +176,7 @@ mod tests {
let command = CloneCommand;

// Test with tag that doesn't match any repository
let context = create_command_context(config, Some("nonexistent".to_string()), None, false);
let context = create_context(config, vec!["nonexistent".to_string()], None, false);

let result = command.execute(&context).await;
assert!(result.is_ok());
Expand All @@ -174,7 +189,7 @@ mod tests {
let command = CloneCommand;

// Test with tag that matches some repositories
let context = create_command_context(config, Some("frontend".to_string()), None, false);
let context = create_context(config, vec!["frontend".to_string()], None, false);

let result = command.execute(&context).await;
// This will likely fail because we're trying to actually clone repos,
Expand All @@ -188,9 +203,9 @@ mod tests {
let command = CloneCommand;

// Test with specific repository names
let context = create_command_context(
let context = create_context(
config,
None,
vec![],
Some(vec!["test-repo-1".to_string(), "test-repo-2".to_string()]),
false,
);
Expand All @@ -207,9 +222,9 @@ mod tests {
let command = CloneCommand;

// Test with both tag and repository filters
let context = create_command_context(
let context = create_context(
config,
Some("frontend".to_string()),
vec!["frontend".to_string()],
Some(vec!["test-repo-1".to_string()]),
false,
);
Expand All @@ -224,7 +239,7 @@ mod tests {
let command = CloneCommand;

// Test parallel execution mode
let context = create_command_context(config, Some("frontend".to_string()), None, true);
let context = create_context(config, vec!["frontend".to_string()], None, true);

let result = command.execute(&context).await;
// Should test parallel execution path
Expand All @@ -237,7 +252,7 @@ mod tests {
let command = CloneCommand;

// Test sequential execution mode
let context = create_command_context(config, Some("backend".to_string()), None, false);
let context = create_context(config, vec!["backend".to_string()], None, false);

let result = command.execute(&context).await;
// Should test sequential execution path
Expand All @@ -250,9 +265,9 @@ mod tests {
let command = CloneCommand;

// Test with repository names that don't exist
let context = create_command_context(
let context = create_context(
config,
None,
vec![],
Some(vec!["nonexistent-repo".to_string()]),
false,
);
Expand All @@ -267,7 +282,7 @@ mod tests {
let command = CloneCommand;

// Test with no filters (should try to clone all repositories)
let context = create_command_context(config, None, None, false);
let context = create_context(config, vec![], None, false);

let result = command.execute(&context).await;
// This will likely fail because we're trying to clone real repos,
Expand All @@ -289,7 +304,7 @@ mod tests {
};

let command = CloneCommand;
let context = create_command_context(config, None, None, false);
let context = create_context(config, vec![], None, false);

let result = command.execute(&context).await;
// Should fail because all clone operations fail
Expand All @@ -305,7 +320,7 @@ mod tests {
let config = create_test_config();
let command = CloneCommand;

let context = create_command_context(config, None, None, false);
let context = create_context(config, vec![], None, false);

let result = command.execute(&context).await;
// The result depends on actual git operations, but we're testing the logic paths
Expand All @@ -332,7 +347,7 @@ mod tests {
};

let command = CloneCommand;
let context = create_command_context(config, None, None, true); // Parallel execution
let context = create_context(config, vec![], None, true); // Parallel execution

let result = command.execute(&context).await;
// Should fail due to invalid repositories, but tests parallel error handling
Expand All @@ -347,24 +362,24 @@ mod tests {
// Test different filter combination scenarios

// Tag only
let context = create_command_context(config.clone(), Some("rust".to_string()), None, false);
let context = create_context(config.clone(), vec!["rust".to_string()], None, false);
let result = command.execute(&context).await;
assert!(result.is_err() || result.is_ok());

// Repos only
let context = create_command_context(
let context = create_context(
config.clone(),
None,
vec![],
Some(vec!["test-repo-3".to_string()]),
false,
);
let result = command.execute(&context).await;
assert!(result.is_err() || result.is_ok());

// Both tag and repos
let context = create_command_context(
let context = create_context(
config,
Some("frontend".to_string()),
vec!["frontend".to_string()],
Some(vec!["test-repo-1".to_string(), "test-repo-3".to_string()]),
false,
);
Expand All @@ -380,7 +395,7 @@ mod tests {
};

let command = CloneCommand;
let context = create_command_context(config, None, None, false);
let context = create_context(config, vec![], None, false);

let result = command.execute(&context).await;
assert!(result.is_ok()); // Should succeed with no repositories message
Expand All @@ -394,7 +409,7 @@ mod tests {
let command = CloneCommand;

// Use parallel execution to test task error handling paths
let context = create_command_context(config, Some("backend".to_string()), None, true);
let context = create_context(config, vec!["backend".to_string()], None, true);

let result = command.execute(&context).await;
// Tests the parallel task error handling code paths
Expand Down
12 changes: 8 additions & 4 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ mod tests {
config: Config {
repositories: vec![],
},
tag: None,
tag: vec![],
exclude_tag: vec![],
repos: None,
parallel: false,
};
Expand Down Expand Up @@ -211,7 +212,8 @@ mod tests {
config: Config {
repositories: vec![],
},
tag: None,
tag: vec![],
exclude_tag: vec![],
repos: None,
parallel: false,
};
Expand Down Expand Up @@ -270,7 +272,8 @@ mod tests {
config: Config {
repositories: vec![],
},
tag: None,
tag: vec![],
exclude_tag: vec![],
repos: None,
parallel: false,
};
Expand Down Expand Up @@ -306,7 +309,8 @@ mod tests {
config: Config {
repositories: vec![],
},
tag: None,
tag: vec![],
exclude_tag: vec![],
repos: None,
parallel: false,
};
Expand Down
30 changes: 22 additions & 8 deletions src/commands/pr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,31 @@ pub struct PrCommand {
#[async_trait]
impl Command for PrCommand {
async fn execute(&self, context: &CommandContext) -> Result<()> {
let repositories = context
.config
.filter_repositories(context.tag.as_deref(), context.repos.as_deref());
let repositories = context.config.filter_repositories(
&context.tag,
&context.exclude_tag,
context.repos.as_deref(),
);

if repositories.is_empty() {
let filter_desc = match (&context.tag, &context.repos) {
(Some(tag), Some(repos)) => format!("tag '{tag}' and repositories {repos:?}"),
(Some(tag), None) => format!("tag '{tag}'"),
(None, Some(repos)) => format!("repositories {repos:?}"),
(None, None) => "no repositories found".to_string(),
let mut filter_parts = Vec::new();

if !context.tag.is_empty() {
filter_parts.push(format!("tags {:?}", context.tag));
}
if !context.exclude_tag.is_empty() {
filter_parts.push(format!("excluding tags {:?}", context.exclude_tag));
}
if let Some(repos) = &context.repos {
filter_parts.push(format!("repositories {:?}", repos));
}

let filter_desc = if filter_parts.is_empty() {
"no repositories found".to_string()
} else {
filter_parts.join(" and ")
};

println!(
"{}",
format!("No repositories found with {filter_desc}").yellow()
Expand Down
Loading
Loading