trello_to_deck/
lib.rs

1//
2use std::path::PathBuf;
3
4use clap::Parser;
5use color_eyre::eyre::eyre;
6use config::AppConfig;
7use kxio::{fs::FileSystem, kxeprintln as e, kxprintln as p, net::Net, print::Printer};
8use tokio::time::Instant;
9
10use crate::{nextcloud::client::DeckClient, trello::client::TrelloClient};
11
12use execute::Execute;
13
14mod api_result;
15mod check;
16mod config;
17mod conversion;
18mod execute;
19mod import;
20mod init;
21pub mod macros;
22mod nextcloud;
23mod rate_limit;
24mod template;
25mod trello;
26
27#[cfg(test)]
28mod tests;
29
30const NAME: &str = "trello-to-deck";
31
32#[derive(Parser, Debug)]
33#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
34pub struct Commands {
35    #[clap(long, action = clap::ArgAction::SetTrue)]
36    pub log: bool,
37    #[clap(long, action = clap::ArgAction::SetTrue)]
38    pub tokio_console: bool,
39    #[clap(subcommand)]
40    pub command: Command,
41}
42#[derive(Parser, Debug)]
43pub enum Command {
44    /// Initialize the configuration
45    #[command(about = "Initialize configuration")]
46    Init,
47
48    /// Check the configuration and connection
49    #[command(about = "Check configuration and connection")]
50    Check,
51
52    /// Import boards from Trello to Nextcloud Deck
53    #[command(about = "Import boards from Trello to Nextcloud Deck")]
54    Import,
55
56    /// Trello-specific commands
57    #[command(about = "Trello-specific commands")]
58    #[clap(subcommand)]
59    Trello(trello::TrelloCommand),
60
61    /// Nextcloud-specific commands
62    #[command(about = "Nextcloud-specific commands")]
63    #[clap(subcommand)]
64    Nextcloud(nextcloud::NextcloudCommand),
65}
66
67#[derive(Clone)]
68pub struct Ctx {
69    pub fs: FileSystem,
70    pub net: Net,
71    pub prt: Printer,
72}
73impl From<PathBuf> for Ctx {
74    fn from(base: PathBuf) -> Self {
75        Self {
76            fs: kxio::fs::new(base),
77            net: kxio::net::new(),
78            prt: kxio::print::standard(),
79        }
80    }
81}
82
83#[derive(Clone)]
84pub(crate) struct FullCtx {
85    pub fs: FileSystem,
86    pub net: Net,
87    pub prt: Printer,
88    pub cfg: AppConfig,
89}
90
91impl FullCtx {
92    pub(crate) fn now(&self) -> Instant {
93        Instant::now()
94    }
95}
96
97impl FullCtx {
98    pub(crate) const fn deck_client(&self) -> DeckClient {
99        DeckClient::new(self)
100    }
101
102    pub(crate) const fn trello_client(&self) -> TrelloClient {
103        TrelloClient::new(self)
104    }
105}
106
107#[cfg_attr(test, mutants::skip)]
108pub async fn run(ctx: &Ctx, commands: &Commands) -> color_eyre::Result<()> {
109    if commands.tokio_console {
110        use tracing_subscriber::prelude::*;
111        let console_layer = console_subscriber::spawn();
112        tracing_subscriber::registry()
113            .with(console_layer)
114            .with(tracing_subscriber::fmt::layer())
115            .init();
116    } else if commands.log {
117        tracing::subscriber::set_global_default(
118            tracing_subscriber::FmtSubscriber::builder()
119                .with_max_level(tracing::Level::TRACE)
120                .with_env_filter("trace,hyper_util=info,kxio=info")
121                .with_file(true)
122                .with_line_number(true)
123                .finish(),
124        )?;
125    }
126    tracing::info!("ready");
127
128    let cfg = AppConfig::load(ctx);
129    match cfg {
130        Err(err) => {
131            if matches!(commands.command, Command::Init) {
132                init::run(ctx)
133            } else {
134                Err(eyre!("Missing or invalid config: {err}"))
135            }
136        }
137        Ok(cfg) => {
138            commands
139                .command
140                .execute(&FullCtx {
141                    fs: ctx.fs.clone(),
142                    net: ctx.net.clone(),
143                    prt: ctx.prt.clone(),
144                    cfg,
145                })
146                .await
147        }
148    }
149}