use anyhow::Result;
use clap::{Parser, Subcommand};
use std::path::PathBuf;
use tracing::info;
mod client;
mod commands;
mod config;
mod output;
mod util;
use commands::{article, auth, meta, realtime, snapshot};
use config::Config;
use output::OutputFormat;
#[derive(Parser)]
#[command(name = "wme")]
#[command(about = "CLI for Wikimedia Enterprise API")]
#[command(version)]
#[command(propagate_version = true)]
struct Cli {
#[arg(long, global = true, env = "WME_TOKEN")]
token: Option<String>,
#[arg(short, long, global = true, default_value = "json")]
output: OutputFormat,
#[arg(long, global = true)]
fields: Option<String>,
#[arg(long, global = true)]
filter: Vec<String>,
#[arg(long, global = true)]
limit: Option<usize>,
#[arg(long, global = true, env = "WME_CONFIG")]
config: Option<PathBuf>,
#[arg(short, long, global = true)]
verbose: bool,
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Auth {
#[command(subcommand)]
command: AuthCommands,
},
Meta {
#[command(subcommand)]
command: MetaCommands,
},
Article {
#[command(subcommand)]
command: ArticleCommands,
},
Snapshot {
#[command(subcommand)]
command: SnapshotCommands,
},
Realtime {
#[command(subcommand)]
command: RealtimeCommands,
},
}
#[derive(Subcommand)]
enum AuthCommands {
Login {
#[arg(short, long, env = "WME_USERNAME")]
username: Option<String>,
#[arg(short, long, env = "WME_PASSWORD")]
password: Option<String>,
},
Refresh {
#[arg(short, long, env = "WME_USERNAME")]
username: Option<String>,
#[arg(long)]
refresh_token: Option<String>,
},
Revoke {
#[arg(long)]
refresh_token: Option<String>,
},
ForgotPassword {
#[arg(long)]
username: String,
},
ForgotPasswordConfirm {
#[arg(long)]
username: String,
#[arg(long)]
password: String,
#[arg(long)]
code: String,
},
ChangePassword {
#[arg(long)]
previous_password: String,
#[arg(long)]
proposed_password: String,
},
SetNewPassword {
#[arg(long)]
username: String,
#[arg(long)]
session: String,
#[arg(long)]
new_password: String,
},
}
#[derive(Subcommand)]
enum MetaCommands {
ProjectCodes,
#[command(name = "project-codes-get")]
ProjectCodesGet {
#[arg(value_name = "CODE")]
code: String,
},
Languages,
#[command(name = "languages-get")]
LanguagesGet {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
},
Projects,
#[command(name = "projects-get")]
ProjectsGet {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
},
Namespaces,
#[command(name = "namespaces-get")]
NamespacesGet {
#[arg(value_name = "ID")]
id: u32,
},
}
#[derive(Subcommand)]
enum ArticleCommands {
Get {
#[arg(value_name = "NAME")]
name: String,
},
Structured {
#[arg(value_name = "NAME")]
name: String,
},
}
#[derive(Subcommand)]
enum SnapshotCommands {
List,
Info {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
},
Download {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
#[arg(short = 'f', long = "output-file")]
output_file: Option<PathBuf>,
#[arg(long)]
range: Option<String>,
},
Chunks {
#[command(subcommand)]
command: SnapshotChunkCommands,
},
Structured {
#[command(subcommand)]
command: StructuredSnapshotCommands,
},
}
#[derive(Subcommand)]
enum SnapshotChunkCommands {
List {
#[arg(value_name = "SNAPSHOT_ID")]
snapshot_id: String,
},
Info {
#[arg(value_name = "SNAPSHOT_ID")]
snapshot_id: String,
#[arg(value_name = "CHUNK_ID")]
chunk_id: String,
},
Download {
#[arg(value_name = "SNAPSHOT_ID")]
snapshot_id: String,
#[arg(value_name = "CHUNK_ID")]
chunk_id: String,
#[arg(short = 'f', long = "output-file")]
output_file: Option<PathBuf>,
#[arg(long)]
range: Option<String>,
},
}
#[derive(Subcommand)]
enum StructuredSnapshotCommands {
List,
Info {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
},
Download {
#[arg(value_name = "IDENTIFIER")]
identifier: String,
#[arg(short = 'f', long = "output-file")]
output_file: Option<PathBuf>,
#[arg(long)]
range: Option<String>,
},
}
#[derive(Subcommand)]
enum RealtimeCommands {
Stream {
#[arg(long)]
since: Option<String>,
#[arg(long)]
parts: Vec<u32>,
#[arg(long)]
offsets: Option<String>,
#[arg(long = "since-per-partition")]
since_per_partition: Option<String>,
#[arg(long)]
ndjson: bool,
},
Batches {
#[command(subcommand)]
command: RealtimeBatchCommands,
},
}
#[derive(Subcommand)]
enum RealtimeBatchCommands {
List {
#[arg(value_name = "DATE")]
date: String,
#[arg(value_name = "HOUR")]
hour: String,
},
Info {
#[arg(value_name = "DATE")]
date: String,
#[arg(value_name = "HOUR")]
hour: String,
#[arg(value_name = "IDENTIFIER")]
identifier: String,
},
Download {
#[arg(value_name = "DATE")]
date: String,
#[arg(value_name = "HOUR")]
hour: String,
#[arg(value_name = "IDENTIFIER")]
identifier: String,
#[arg(short = 'f', long = "output-file")]
output_file: Option<PathBuf>,
#[arg(long)]
range: Option<String>,
},
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
let level = if cli.verbose { "debug" } else { "warn" };
tracing_subscriber::fmt()
.with_env_filter(format!("wme_cli={level},warn"))
.init();
let config = Config::load(cli.config.as_deref())?;
let global_opts = commands::GlobalOpts {
token: cli.token,
output: cli.output,
fields: cli.fields,
filter: cli.filter,
limit: cli.limit,
config,
};
info!("Starting wme CLI");
match cli.command {
Commands::Auth { command } => match command {
AuthCommands::Login { username, password } => {
auth::login(username, password, &global_opts).await?;
}
AuthCommands::Refresh {
username,
refresh_token,
} => {
auth::refresh(username, refresh_token, &global_opts).await?;
}
AuthCommands::Revoke { refresh_token } => {
auth::revoke(refresh_token, &global_opts).await?;
}
AuthCommands::ForgotPassword { username } => {
auth::forgot_password(&username, &global_opts).await?;
}
AuthCommands::ForgotPasswordConfirm {
username,
password,
code,
} => {
auth::forgot_password_confirm(&username, &password, &code, &global_opts).await?;
}
AuthCommands::ChangePassword {
previous_password,
proposed_password,
} => {
auth::change_password(&previous_password, &proposed_password, &global_opts).await?;
}
AuthCommands::SetNewPassword {
username,
session,
new_password,
} => {
auth::set_new_password(&username, &session, &new_password, &global_opts).await?;
}
},
Commands::Meta { command } => match command {
MetaCommands::ProjectCodes => {
meta::project_codes(&global_opts).await?;
}
MetaCommands::ProjectCodesGet { code } => {
meta::project_codes_get(&code, &global_opts).await?;
}
MetaCommands::Languages => {
meta::languages(&global_opts).await?;
}
MetaCommands::LanguagesGet { identifier } => {
meta::languages_get(&identifier, &global_opts).await?;
}
MetaCommands::Projects => {
meta::projects(&global_opts).await?;
}
MetaCommands::ProjectsGet { identifier } => {
meta::projects_get(&identifier, &global_opts).await?;
}
MetaCommands::Namespaces => {
meta::namespaces(&global_opts).await?;
}
MetaCommands::NamespacesGet { id } => {
meta::namespaces_get(id, &global_opts).await?;
}
},
Commands::Article { command } => match command {
ArticleCommands::Get { name } => {
article::get(&name, &global_opts).await?;
}
ArticleCommands::Structured { name } => {
article::structured(&name, &global_opts).await?;
}
},
Commands::Snapshot { command } => match command {
SnapshotCommands::List => {
snapshot::list(&global_opts).await?;
}
SnapshotCommands::Info { identifier } => {
snapshot::info(&identifier, &global_opts).await?;
}
SnapshotCommands::Download {
identifier,
output_file,
range,
} => {
snapshot::download(&identifier, output_file, range, &global_opts).await?;
}
SnapshotCommands::Chunks { command } => match command {
SnapshotChunkCommands::List { snapshot_id } => {
snapshot::chunks_list(&snapshot_id, &global_opts).await?;
}
SnapshotChunkCommands::Info {
snapshot_id,
chunk_id,
} => {
snapshot::chunks_info(&snapshot_id, &chunk_id, &global_opts).await?;
}
SnapshotChunkCommands::Download {
snapshot_id,
chunk_id,
output_file,
range,
} => {
snapshot::chunks_download(
&snapshot_id,
&chunk_id,
output_file,
range,
&global_opts,
)
.await?;
}
},
SnapshotCommands::Structured { command } => match command {
StructuredSnapshotCommands::List => {
snapshot::structured_list(&global_opts).await?;
}
StructuredSnapshotCommands::Info { identifier } => {
snapshot::structured_info(&identifier, &global_opts).await?;
}
StructuredSnapshotCommands::Download {
identifier,
output_file,
range,
} => {
snapshot::structured_download(&identifier, output_file, range, &global_opts)
.await?;
}
},
},
Commands::Realtime { command } => match command {
RealtimeCommands::Stream {
since,
parts,
offsets,
since_per_partition,
ndjson,
} => {
realtime::stream(
since,
parts,
offsets,
since_per_partition,
ndjson,
&global_opts,
)
.await?;
}
RealtimeCommands::Batches { command } => match command {
RealtimeBatchCommands::List { date, hour } => {
realtime::batches_list(&date, &hour, &global_opts).await?;
}
RealtimeBatchCommands::Info {
date,
hour,
identifier,
} => {
realtime::batches_info(&date, &hour, &identifier, &global_opts).await?;
}
RealtimeBatchCommands::Download {
date,
hour,
identifier,
output_file,
range,
} => {
realtime::batches_download(
&date,
&hour,
&identifier,
output_file,
range,
&global_opts,
)
.await?;
}
},
},
}
Ok(())
}