mod client;
mod commands;
mod config;
mod error;
mod logger;
mod models;
mod output;
mod params;
mod resolve;
mod validate;
use clap::{Parser, Subcommand};
use client::PrefectClient;
use config::Config;
use error::Result;
use std::time::Instant;
#[derive(Parser)]
#[command(name = "pfp", version, about = "Prefect CLI")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Ls {
#[arg(long)]
json: bool,
},
Run {
query: String,
#[arg(long)]
watch: bool,
#[arg(long = "set", num_args = 1)]
sets: Vec<String>,
#[arg(long = "params-file")]
params_file: Option<String>,
#[arg(long)]
json: bool,
},
Runs {
query: String,
#[arg(long)]
json: bool,
},
Logs {
flow_run_id: String,
#[arg(long)]
limit: Option<usize>,
#[arg(long, short = 'f')]
follow: bool,
#[arg(long)]
json: bool,
},
Pause {
query: String,
},
Resume {
query: String,
},
Cancel {
flow_run_id: String,
},
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
let params_payload: Option<Result<serde_json::Value>> = match &cli.command {
Commands::Run {
params_file: Some(path),
..
} => Some(commands::run::load_params_file(path)),
_ => None,
};
let (cmd_name, cmd_args) = describe_command(&cli.command, params_payload.as_ref());
let start = Instant::now();
let result = run(cli, params_payload).await;
let duration_ms = start.elapsed().as_millis() as u64;
logger::log_invocation(&cmd_name, cmd_args, &result, duration_ms);
if let Err(e) = result {
eprintln!("Error: {}", e);
std::process::exit(e.exit_code());
}
}
fn describe_command(
cmd: &Commands,
params_payload: Option<&Result<serde_json::Value>>,
) -> (String, serde_json::Value) {
match cmd {
Commands::Ls { json } => ("ls".into(), serde_json::json!({ "json": json })),
Commands::Run {
query,
watch,
sets,
params_file,
json,
} => {
let params_log = params_file.as_ref().map(|p| match params_payload {
Some(Ok(v)) => serde_json::json!({ "path": p, "payload": v }),
_ => serde_json::json!({ "path": p }),
});
(
"run".into(),
serde_json::json!({
"query": query,
"watch": watch,
"sets": sets,
"params_file": params_log,
"json": json,
}),
)
}
Commands::Runs { query, json } => (
"runs".into(),
serde_json::json!({ "query": query, "json": json }),
),
Commands::Logs {
flow_run_id,
limit,
follow,
json,
} => (
"logs".into(),
serde_json::json!({ "flow_run_id": flow_run_id, "limit": limit, "follow": follow, "json": json }),
),
Commands::Pause { query } => ("pause".into(), serde_json::json!({ "query": query })),
Commands::Resume { query } => ("resume".into(), serde_json::json!({ "query": query })),
Commands::Cancel { flow_run_id } => (
"cancel".into(),
serde_json::json!({ "flow_run_id": flow_run_id }),
),
}
}
async fn run(cli: Cli, params_payload: Option<Result<serde_json::Value>>) -> Result<()> {
match cli.command {
Commands::Ls { json } => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::ls::run(client, json).await
}
Commands::Run {
query,
watch,
sets,
json,
..
} => {
let params_base = params_payload.transpose()?;
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::run::run(client, query, watch, sets, params_base, json).await
}
Commands::Runs { query, json } => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::runs::run(client, query, json).await
}
Commands::Logs {
flow_run_id,
limit,
follow,
json,
} => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::logs::run(client, flow_run_id, limit, follow, json).await
}
Commands::Pause { query } => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::pause::run(client, query).await
}
Commands::Resume { query } => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::resume::run(client, query).await
}
Commands::Cancel { flow_run_id } => {
let config = Config::load()?;
let client = PrefectClient::new(config);
commands::cancel::run(client, flow_run_id).await
}
}
}