use clap::ArgMatches;
use crate::infra::driven::fs::config::SourceConfig;
use crate::infra::driving::cli::context::Context;
use crate::infra::driving::cli::errors::{die1, CliError};
use crate::infra::driving::cli::theme;
use crate::infra::driving::cli::OutputFormat;
pub(in super::super) fn execute_source(
matches: &ArgMatches,
ctx: &Context<'_>,
source_cfg: &SourceConfig,
) {
match matches.subcommand() {
Some(("list", _sub)) => execute_list(ctx, source_cfg),
Some(("sync", sub)) => {
let dry_run = sub.get_flag("dry-run");
execute_sync(ctx, source_cfg, dry_run);
}
_ => {
die1(
CliError::new("unknown source subcommand").kind("validation"),
ctx.output_fmt,
);
}
}
}
fn resolve_token(source_cfg: &SourceConfig, output_fmt: OutputFormat) -> String {
match std::env::var(&source_cfg.token_env) {
Ok(t) if !t.is_empty() => t,
_ => {
die1(
CliError::new(format!(
"environment variable '{}' is not set or empty",
source_cfg.token_env
))
.kind("config"),
output_fmt,
);
}
}
}
fn make_gitlab_source<'a>(
client: &'a crate::infra::driven::gitlab::UreqClient,
source_cfg: &SourceConfig,
token: &str,
id_prefix: Option<&str>,
) -> crate::infra::driven::gitlab::GitLabSource<'a> {
let mut source = crate::infra::driven::gitlab::GitLabSource::new(
client,
&source_cfg.url,
&source_cfg.project,
token,
)
.with_status_map(source_cfg.status_map.clone());
if let Some(prefix) = id_prefix {
source = source.with_id_prefix(prefix);
}
source
}
fn execute_list(ctx: &Context<'_>, source_cfg: &SourceConfig) {
let token = resolve_token(source_cfg, ctx.output_fmt);
let http_client = crate::infra::driven::gitlab::UreqClient;
let source = make_gitlab_source(&http_client, source_cfg, &token, ctx.issues_id_prefix());
match crate::domain::usecases::source::list_source_issues(&source) {
Ok(issues) => {
if issues.is_empty() {
println!("No issues found in source '{}'.", source_cfg.name);
return;
}
if ctx.output_fmt.is_structured() {
use crate::infra::driving::cli::issue_view::IssueView;
let views: Vec<IssueView> = issues.iter().map(IssueView::from_issue).collect();
crate::infra::driving::cli::render_structured(&views, ctx.output_fmt);
return;
}
use crate::infra::driving::cli::table::{terminal_width, Cell, Table};
let mut table = Table::new(terminal_width());
for issue in &issues {
let cells = vec![
Cell::new(theme::id(&issue.id.to_string())),
Cell::new(theme::status(&issue.status.label, issue.status.category)),
Cell::new(issue.title.to_string()),
];
table.push(cells);
}
table.print();
}
Err(e) => {
die1(CliError::new(e.to_string()).kind("io"), ctx.output_fmt);
}
}
}
fn execute_sync(ctx: &Context<'_>, source_cfg: &SourceConfig, dry_run: bool) {
let token = resolve_token(source_cfg, ctx.output_fmt);
let http_client = crate::infra::driven::gitlab::UreqClient;
let source = make_gitlab_source(&http_client, source_cfg, &token, ctx.issues_id_prefix());
let repo = ctx.issue_repository();
let id_gen = ctx.issue_id_generator();
let options = crate::domain::usecases::source::SyncOptions { dry_run };
if dry_run {
println!("Dry run — no changes will be written.");
}
match crate::domain::usecases::source::sync_issues_with(&source, &repo, &id_gen, options) {
Ok(result) => {
for action in &result.actions {
match action {
crate::domain::usecases::source::SyncAction::Created { id } => {
println!(" created {id}");
}
crate::domain::usecases::source::SyncAction::Updated { id } => {
println!(" updated {id}");
}
crate::domain::usecases::source::SyncAction::Unchanged { id } => {
if ctx.output_fmt.is_structured() {
println!(" unchanged {id}");
}
}
}
}
let verb = if dry_run { "Dry run" } else { "Sync" };
println!(
"{verb} complete: {} created, {} updated, {} unchanged.",
result.created, result.updated, result.unchanged
);
}
Err(e) => {
die1(CliError::new(e.to_string()).kind("io"), ctx.output_fmt);
}
}
}