netsky 0.1.6

netsky CLI: the viable system launcher and subcommand dispatcher
Documentation
//! `netsky setup <channel>` — first-run wizards for channel sources.
//!
//! Currently just `email`, which drives the Gmail OAuth installed-app flow
//! by calling into `netsky_io::sources::email::access_cli` directly (no
//! subprocess — `netsky` doesn't need to be on PATH).

use std::io::{self, BufRead, Write};

use netsky_io::EmailAccessCmd;
use netsky_io::sources::email::config::known_accounts;

use crate::cli::SetupCommand;

pub fn run(cmd: SetupCommand) -> netsky_core::Result<()> {
    match cmd {
        SetupCommand::Email { account } => email(account),
    }
}

fn email(account: Option<String>) -> netsky_core::Result<()> {
    let account = match account {
        Some(a) => a,
        None => prompt_for_account()?,
    };
    netsky_io::sources::email::access_cli(EmailAccessCmd::OauthInit { account })
        .map_err(|e| netsky_core::Error::Message(format!("oauth-init failed: {e}")))
}

fn prompt_for_account() -> netsky_core::Result<String> {
    let accounts = known_accounts();
    if accounts.is_empty() {
        return Err(netsky_core::Error::Invalid(
            "no email accounts configured (populate `[[channels.email.accounts]]` \
             in netsky.toml with at least one primary)"
                .into(),
        ));
    }
    println!("Which primary account do you want to authorise?");
    for (i, a) in accounts.iter().enumerate() {
        println!("  {}) {}", i + 1, a.primary);
    }
    print!("Enter number or address: ");
    io::stdout().flush().map_err(netsky_core::Error::Io)?;

    let mut line = String::new();
    io::stdin()
        .lock()
        .read_line(&mut line)
        .map_err(netsky_core::Error::Io)?;
    let input = line.trim();
    if input.is_empty() {
        return Err(netsky_core::Error::Invalid(
            "no account selected (empty input)".into(),
        ));
    }
    if let Ok(n) = input.parse::<usize>()
        && n >= 1
        && n <= accounts.len()
    {
        return Ok(accounts[n - 1].primary.clone());
    }
    Ok(input.to_string())
}