launchpadlib 0.5.7

Rust library for accessing Launchpad.net
Documentation
use clap::Parser;

#[derive(Parser)]
#[clap(name = "lp-set-dupe")]
struct Args {
    main_bug: u32,

    dupes: Vec<u32>,

    /// Launchpad instance to use (defaults to production)
    #[clap(short, long)]
    instance: Option<String>,

    /// Skip confirmation prompt
    #[clap(short, long)]
    force: bool,
}

pub const CONSUMER_KEY: &str = "lp-set-dupe";

fn main() {
    let args = Args::parse();

    let client =
        launchpadlib::blocking::Client::authenticated(args.instance.as_deref(), CONSUMER_KEY)
            .unwrap();

    let root = if let Some(host) = args.instance {
        let host = format!("api.{}", host);
        launchpadlib::blocking::v1_0::service_root_for_host(&client, &host)
    } else {
        launchpadlib::blocking::v1_0::service_root(&client)
    }
    .unwrap();

    let bugs = root.bugs().unwrap();

    let mut main_bug = bugs.get_by_id(&client, args.main_bug).unwrap();

    log::info!(
        "Marking bugs as duplicate of bug {} ({})",
        main_bug.id,
        main_bug.title
    );

    if let Some(new_main_dupe_of) = main_bug
        .duplicate_of()
        .as_ref()
        .map(|c| c.get(&client).unwrap())
    {
        log::error!(
            "Bug {} is already a duplicate of bug {} ({})",
            main_bug.id,
            new_main_dupe_of.id,
            new_main_dupe_of.title
        );
        println!(
            "Would you like to use {} as the new main bug instead? [y/N]",
            new_main_dupe_of.id
        );
        let mut input = String::new();
        std::io::stdin().read_line(&mut input).unwrap();

        match input.trim() {
            "y" | "Y" => {
                main_bug
                    .self_()
                    .unwrap()
                    .patch(
                        &client,
                        &launchpadlib::blocking::v1_0::BugDiff {
                            duplicate_of_link: new_main_dupe_of.self_link.clone(),
                            ..Default::default()
                        },
                    )
                    .unwrap();
                println!(
                    "Bug {} is now a duplicate of bug {} ({})",
                    main_bug.id, new_main_dupe_of.id, new_main_dupe_of.title
                );
                main_bug = new_main_dupe_of;
            }
            _ => {
                log::error!("Aborting");
                std::process::exit(1);
            }
        }
    }

    let mut bugs_to_process = args.dupes.clone();

    while let Some(bugno) = bugs_to_process.pop() {
        if let Ok(bug) = bugs.get_by_id(&client, bugno) {
            println!(
                "Marking bug {} as duplicate of bug {} ({})... ",
                bugno, bug.id, bug.title
            );
            if bug.duplicate_of().is_some() {
                log::error!("Bug {} is already a duplicate of another bug", bug.id);
                std::process::exit(1);
            }
            bug.self_()
                .unwrap()
                .patch(
                    &client,
                    &launchpadlib::blocking::v1_0::BugDiff {
                        duplicate_of_link: main_bug.self_link.clone(),
                        ..Default::default()
                    },
                )
                .unwrap();
            println!("done");
            bugs_to_process.extend(
                bug.duplicates(&client)
                    .unwrap()
                    .map(|bug| bug.unwrap().id as u32),
            );
        } else {
            log::error!("Bug {} not found", bugno);
            std::process::exit(1);
        }
    }
}