use std::fs;
use std::io::{self, Read};
use std::path::{Path, PathBuf};
use std::time::Duration;
use anyhow::{Context, Result, anyhow, bail};
use clap::{Args, Parser, Subcommand};
use directories::ProjectDirs;
use reqwest::{Client, Method, Url};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use tokio::time::sleep;
const DEFAULT_BASE_URL: &str = "https://cpu.mattstuchlik.com";
const ALL_SYSTEMS_ID: &str = "all_systems";
const RATIO_PPM_SCALE: f64 = 1_000_000.0;
const USER_AGENT: &str = concat!("cpu-mode-cli/", env!("CARGO_PKG_VERSION"));
#[derive(Parser)]
#[command(name = "cpu-mode")]
#[command(about = "Command-line client for cpu.mode")]
struct Cli {
#[arg(long, global = true)]
raw: bool,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Auth(AuthArgs),
Challenges(ChallengesArgs),
Systems(SystemsArgs),
Leaderboard(LeaderboardArgs),
Submit(SubmitArgs),
Jobs(JobsArgs),
Users(UsersArgs),
Solutions(SolutionsArgs),
}
#[derive(Args)]
struct AuthArgs {
#[command(subcommand)]
command: AuthCommand,
}
#[derive(Subcommand)]
enum AuthCommand {
Login(AuthLoginArgs),
Status,
Logout,
}
#[derive(Args)]
struct AuthLoginArgs {
#[arg(long)]
no_store: bool,
}
#[derive(Args)]
struct ChallengesArgs {
#[command(subcommand)]
command: ChallengesCommand,
}
#[derive(Subcommand)]
enum ChallengesCommand {
List,
Show { challenge_id: String },
}
#[derive(Args)]
struct SystemsArgs {
#[command(subcommand)]
command: SystemsCommand,
}
#[derive(Subcommand)]
enum SystemsCommand {
List,
}
#[derive(Args)]
struct LeaderboardArgs {
challenge_id: String,
#[arg(long)]
system: Option<String>,
#[arg(long)]
all_systems: bool,
}
#[derive(Args)]
struct SubmitArgs {
challenge_id: String,
#[arg(long, value_parser = ["rust", "cpp", "asm"])]
lang: String,
#[arg(long)]
file: PathBuf,
#[arg(long)]
compiler: Option<String>,
#[arg(long)]
compiler_options: Option<String>,
#[arg(long)]
wait: bool,
#[arg(long, default_value_t = 1000)]
poll_interval_ms: u64,
}
#[derive(Args)]
struct JobsArgs {
#[command(subcommand)]
command: JobsCommand,
}
#[derive(Subcommand)]
enum JobsCommand {
Show {
job_id: String,
},
Watch {
job_id: String,
#[arg(long, default_value_t = 1000)]
poll_interval_ms: u64,
},
Queue {
#[arg(long, default_value_t = 10)]
limit: usize,
#[arg(long)]
cursor: Option<String>,
},
Profile {
job_id: String,
#[arg(short, long)]
output: Option<PathBuf>,
},
TopDown {
job_id: String,
},
}
#[derive(Args)]
struct UsersArgs {
#[command(subcommand)]
command: UsersCommand,
}
#[derive(Subcommand)]
enum UsersCommand {
Jobs {
user_id: String,
#[arg(long)]
challenge: Option<String>,
#[arg(long, default_value_t = 20)]
limit: usize,
#[arg(long)]
cursor: Option<String>,
},
}
#[derive(Args)]
struct SolutionsArgs {
#[command(subcommand)]
command: SolutionsCommand,
}
#[derive(Subcommand)]
enum SolutionsCommand {
Show {
solution_id: String,
},
Publish {
solution_id: String,
},
Unpublish {
solution_id: String,
},
Jobs {
solution_id: String,
#[arg(long, default_value_t = 20)]
limit: usize,
#[arg(long)]
cursor: Option<String>,
},
}
#[derive(Debug, Default, Deserialize, Serialize)]
struct StoredConfig {
token: Option<String>,
}
struct ConfigStore {
path: PathBuf,
config: StoredConfig,
}
impl ConfigStore {
fn load() -> Result<Self> {
let path = config_path()?;
let config = match fs::read_to_string(&path) {
Ok(contents) => toml::from_str(&contents)
.with_context(|| format!("parse config {}", path.display()))?,
Err(err) if err.kind() == io::ErrorKind::NotFound => StoredConfig::default(),
Err(err) => return Err(err).with_context(|| format!("read config {}", path.display())),
};
Ok(Self { path, config })
}
fn token(&self) -> Option<&str> {
self.config.token.as_deref()
}
fn set_token(&mut self, token: String) -> Result<()> {
self.config.token = Some(token);
self.save()
}
fn clear_token(&mut self) -> Result<()> {
self.config.token = None;
self.save()
}
fn save(&self) -> Result<()> {
let parent = self
.path
.parent()
.ok_or_else(|| anyhow!("config path has no parent: {}", self.path.display()))?;
fs::create_dir_all(parent)
.with_context(|| format!("create config dir {}", parent.display()))?;
let contents = toml::to_string_pretty(&self.config).context("serialize config")?;
write_private_file(&self.path, contents.as_bytes())
.with_context(|| format!("write config {}", self.path.display()))
}
}
fn config_path() -> Result<PathBuf> {
if let Some(path) = std::env::var_os("CPU_MODE_CONFIG") {
return Ok(PathBuf::from(path));
}
let dirs = ProjectDirs::from("com", "cpu-mode", "cpu-mode")
.ok_or_else(|| anyhow!("could not determine config directory"))?;
Ok(dirs.config_dir().join("config.toml"))
}
#[cfg(unix)]
fn write_private_file(path: &Path, contents: &[u8]) -> io::Result<()> {
use std::os::unix::fs::OpenOptionsExt;
let mut options = fs::OpenOptions::new();
options.create(true).truncate(true).write(true).mode(0o600);
std::io::Write::write_all(&mut options.open(path)?, contents)
}
#[cfg(not(unix))]
fn write_private_file(path: &Path, contents: &[u8]) -> io::Result<()> {
fs::write(path, contents)
}
struct ApiClient {
http: Client,
base_url: String,
token: Option<String>,
}
impl ApiClient {
fn new(base_url: String, token: Option<String>) -> Self {
Self {
http: Client::new(),
base_url: base_url.trim_end_matches('/').to_string(),
token,
}
}
async fn get(&self, path: &str, query: &[(&str, String)]) -> Result<Value> {
self.request(Method::GET, path, query, None).await
}
async fn get_text(&self, path: &str, query: &[(&str, String)]) -> Result<String> {
self.request_text(Method::GET, path, query, None).await
}
async fn post(&self, path: &str, body: Value) -> Result<Value> {
self.request(Method::POST, path, &[], Some(body)).await
}
async fn patch(&self, path: &str, body: Value) -> Result<Value> {
self.request(Method::PATCH, path, &[], Some(body)).await
}
async fn request(
&self,
method: Method,
path: &str,
query: &[(&str, String)],
body: Option<Value>,
) -> Result<Value> {
let text = self.request_text(method, path, query, body).await?;
parse_json_body(&text)
}
async fn request_text(
&self,
method: Method,
path: &str,
query: &[(&str, String)],
body: Option<Value>,
) -> Result<String> {
let mut url = self.url(path)?;
{
let mut pairs = url.query_pairs_mut();
for (key, value) in query {
pairs.append_pair(key, value);
}
}
let mut request = self
.http
.request(method, url)
.header("user-agent", USER_AGENT);
if let Some(token) = &self.token {
request = request.bearer_auth(token);
}
if let Some(body) = body {
request = request.json(&body);
}
let response = request.send().await.context("send request")?;
let status = response.status();
let text = response.text().await.context("read response body")?;
if !status.is_success() {
bail!("HTTP {status}: {text}");
}
Ok(text)
}
fn url(&self, path: &str) -> Result<Url> {
Url::parse(&format!("{}{}", self.base_url, path))
.with_context(|| format!("build URL for path {path}"))
}
}
fn parse_json_body(text: &str) -> Result<Value> {
if text.trim().is_empty() {
Ok(Value::Null)
} else {
serde_json::from_str(text).with_context(|| format!("parse JSON response: {text}"))
}
}
#[derive(Deserialize)]
struct AuthStartResponse {
login_id: String,
verification_uri: String,
user_code: String,
interval: Option<u64>,
expires_in: Option<u64>,
}
#[derive(Deserialize)]
struct AuthPollResponse {
status: String,
token: Option<String>,
user: Option<Value>,
interval: Option<u64>,
message: Option<String>,
}
#[tokio::main]
async fn main() -> Result<()> {
let cli = Cli::parse();
let mut store = ConfigStore::load()?;
let token = store.token().map(str::to_string);
let client = ApiClient::new(DEFAULT_BASE_URL.to_string(), token);
let output = OutputMode { raw: cli.raw };
match cli.command {
Command::Auth(args) => handle_auth(args, &client, &mut store, output).await,
Command::Challenges(args) => handle_challenges(args, &client, output).await,
Command::Systems(args) => handle_systems(args, &client, output).await,
Command::Leaderboard(args) => handle_leaderboard(args, &client, output).await,
Command::Submit(args) => handle_submit(args, &client, output).await,
Command::Jobs(args) => handle_jobs(args, &client, output).await,
Command::Users(args) => handle_users(args, &client, output).await,
Command::Solutions(args) => handle_solutions(args, &client, output).await,
}
}
#[derive(Clone, Copy)]
struct OutputMode {
raw: bool,
}
async fn handle_auth(
args: AuthArgs,
client: &ApiClient,
store: &mut ConfigStore,
output: OutputMode,
) -> Result<()> {
match args.command {
AuthCommand::Login(args) => auth_login(args, client, store, output).await,
AuthCommand::Status => {
let session = client.get("/auth/session", &[]).await?;
output.print(&session, print_auth_status)
}
AuthCommand::Logout => {
store.clear_token()?;
output.print(
&json!({"ok": true, "message": "local token removed"}),
print_logout,
)
}
}
}
async fn auth_login(
args: AuthLoginArgs,
client: &ApiClient,
store: &mut ConfigStore,
output: OutputMode,
) -> Result<()> {
let start: AuthStartResponse =
serde_json::from_value(client.post("/auth/cli/start", json!({})).await?)
.context("decode auth start response")?;
eprintln!(
"Open {} and enter code {}",
start.verification_uri, start.user_code
);
if let Some(expires_in) = start.expires_in {
eprintln!("This code expires in {expires_in} seconds.");
}
let mut interval = start.interval.unwrap_or(5).max(1);
loop {
sleep(Duration::from_secs(interval)).await;
let poll: AuthPollResponse = serde_json::from_value(
client
.post("/auth/cli/poll", json!({ "login_id": start.login_id }))
.await?,
)
.context("decode auth poll response")?;
match poll.status.as_str() {
"authorized" => {
let token = poll
.token
.ok_or_else(|| anyhow!("authorized response did not include token"))?;
if !args.no_store {
store.set_token(token.clone())?;
}
return output.print(
&json!({
"authenticated": true,
"stored": !args.no_store,
"user": poll.user,
"token": if args.no_store { Some(token) } else { None },
}),
print_auth_login,
);
}
"pending" => {}
"slow_down" => {
interval += 5;
}
"expired" => bail!(
"{}",
poll.message.unwrap_or_else(|| "login expired".to_string())
),
"denied" => bail!(
"{}",
poll.message.unwrap_or_else(|| "login denied".to_string())
),
other => bail!("unknown auth status {other}"),
}
if let Some(next_interval) = poll.interval {
interval = next_interval.max(1);
}
}
}
async fn handle_challenges(
args: ChallengesArgs,
client: &ApiClient,
output: OutputMode,
) -> Result<()> {
match args.command {
ChallengesCommand::List => {
let value = client.get("/api/challenges", &[]).await?;
output.print(&value, print_challenge_list)
}
ChallengesCommand::Show { challenge_id } => {
let value = client
.get(&format!("/api/challenges/{}", enc(&challenge_id)), &[])
.await?;
output.print(&value, print_challenge_detail)
}
}
}
async fn handle_systems(args: SystemsArgs, client: &ApiClient, output: OutputMode) -> Result<()> {
match args.command {
SystemsCommand::List => {
output.print(&client.get("/api/systems", &[]).await?, print_systems)
}
}
}
async fn handle_leaderboard(
args: LeaderboardArgs,
client: &ApiClient,
output: OutputMode,
) -> Result<()> {
let all_systems = args.all_systems || args.system.as_deref().is_some_and(is_all_systems_alias);
if all_systems {
if let Some(system) = args.system.as_deref() {
if args.all_systems && !is_all_systems_alias(system) {
bail!("--all-systems cannot be combined with --system {system}");
}
}
let value = client
.get(
&format!(
"/api/challenges/{}/all-systems-leaderboard",
enc(&args.challenge_id)
),
&[],
)
.await?;
return output.print(&value, print_all_systems_leaderboard);
}
let mut query = Vec::new();
if let Some(system) = args.system {
query.push(("system_id", system));
}
let value = client
.get(
&format!("/api/challenges/{}/leaderboard", enc(&args.challenge_id)),
&query,
)
.await?;
output.print(&value, print_leaderboard)
}
async fn handle_submit(args: SubmitArgs, client: &ApiClient, output: OutputMode) -> Result<()> {
let source = read_source(&args.file)?;
let mut body = json!({
"language": args.lang,
"source": source,
});
if let Some(compiler) = args.compiler {
body["compiler"] = Value::String(compiler);
}
if let Some(options) = args.compiler_options {
body["compiler_options"] = Value::String(options);
}
let submission = client
.post(
&format!("/api/challenges/{}/submissions", enc(&args.challenge_id)),
body,
)
.await?;
if !args.wait {
return output.print(&submission, print_submission);
}
let jobs = submission
.get("jobs")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("submission response did not include jobs array"))?;
let mut finished = Vec::new();
for job in jobs {
let job_id = job
.get("job_id")
.and_then(Value::as_str)
.ok_or_else(|| anyhow!("job entry did not include job_id"))?;
finished.push(watch_job(client, job_id, args.poll_interval_ms).await?);
}
output.print(
&json!({
"submission": submission,
"jobs": finished,
}),
print_submission_with_jobs,
)
}
async fn handle_jobs(args: JobsArgs, client: &ApiClient, output: OutputMode) -> Result<()> {
match args.command {
JobsCommand::Show { job_id } => output.print(&get_job(client, &job_id).await?, print_job),
JobsCommand::Watch {
job_id,
poll_interval_ms,
} => output.print(
&watch_job(client, &job_id, poll_interval_ms).await?,
print_job,
),
JobsCommand::Queue { limit, cursor } => {
let mut query = vec![("limit", limit.to_string())];
if let Some(cursor) = cursor {
query.push(("cursor", cursor));
}
output.print(
&client.get("/api/jobs/queue", &query).await?,
print_queue_jobs_page,
)
}
JobsCommand::Profile {
job_id,
output: path,
} => {
let profile = client
.get_text(&format!("/api/jobs/{}/profile", enc(&job_id)), &[])
.await?;
if output.raw {
if path.is_some() {
bail!("--output cannot be used with --raw");
}
print!("{profile}");
Ok(())
} else {
let path = path.unwrap_or_else(|| {
PathBuf::from(format!(
"{}.perf-annotate.txt",
safe_filename_component(&job_id)
))
});
fs::write(&path, profile.as_bytes())
.with_context(|| format!("write profile {}", path.display()))?;
println!(
"Downloaded profile to {} ({} bytes)",
path.display(),
profile.len()
);
Ok(())
}
}
JobsCommand::TopDown { job_id } => output.print(
&client
.get(&format!("/api/jobs/{}/top-down", enc(&job_id)), &[])
.await?,
print_top_down,
),
}
}
async fn handle_users(args: UsersArgs, client: &ApiClient, output: OutputMode) -> Result<()> {
match args.command {
UsersCommand::Jobs {
user_id,
challenge,
limit,
cursor,
} => {
let mut query = vec![("limit", limit.to_string())];
if let Some(challenge) = challenge {
query.push(("challenge_id", challenge));
}
if let Some(cursor) = cursor {
query.push(("cursor", cursor));
}
let value = client
.get(&format!("/api/users/{}/jobs", enc(&user_id)), &query)
.await?;
output.print(&value, print_jobs_page)
}
}
}
async fn handle_solutions(
args: SolutionsArgs,
client: &ApiClient,
output: OutputMode,
) -> Result<()> {
match args.command {
SolutionsCommand::Show { solution_id } => output.print(
&client
.get(&format!("/api/solutions/{}", enc(&solution_id)), &[])
.await?,
print_solution,
),
SolutionsCommand::Publish { solution_id } => output.print(
&client
.patch(
&format!("/api/solutions/{}", enc(&solution_id)),
json!({ "is_public": true }),
)
.await?,
print_solution_visibility,
),
SolutionsCommand::Unpublish { solution_id } => output.print(
&client
.patch(
&format!("/api/solutions/{}", enc(&solution_id)),
json!({ "is_public": false }),
)
.await?,
print_solution_visibility,
),
SolutionsCommand::Jobs {
solution_id,
limit,
cursor,
} => {
let mut query = vec![("limit", limit.to_string())];
if let Some(cursor) = cursor {
query.push(("cursor", cursor));
}
output.print(
&client
.get(
&format!("/api/solutions/{}/jobs", enc(&solution_id)),
&query,
)
.await?,
print_jobs_page,
)
}
}
}
async fn get_job(client: &ApiClient, job_id: &str) -> Result<Value> {
client.get(&format!("/api/jobs/{}", enc(job_id)), &[]).await
}
async fn watch_job(client: &ApiClient, job_id: &str, poll_interval_ms: u64) -> Result<Value> {
let interval = Duration::from_millis(poll_interval_ms.max(100));
loop {
let job = get_job(client, job_id).await?;
if job.get("status").and_then(Value::as_str) == Some("done") {
return Ok(job);
}
sleep(interval).await;
}
}
fn read_source(path: &Path) -> Result<String> {
if path == Path::new("-") {
let mut source = String::new();
io::stdin()
.read_to_string(&mut source)
.context("read source from stdin")?;
Ok(source)
} else {
fs::read_to_string(path).with_context(|| format!("read source {}", path.display()))
}
}
fn enc(value: &str) -> String {
urlencoding::encode(value).into_owned()
}
fn is_all_systems_alias(system: &str) -> bool {
matches!(system, ALL_SYSTEMS_ID | "all-systems" | "all")
}
impl OutputMode {
fn print(self, value: &Value, human: fn(&Value) -> Result<()>) -> Result<()> {
if self.raw {
print_json(value)
} else {
human(value)
}
}
}
fn print_json(value: &Value) -> Result<()> {
println!("{}", serde_json::to_string_pretty(value)?);
Ok(())
}
fn print_auth_status(value: &Value) -> Result<()> {
if value.get("authenticated").and_then(Value::as_bool) == Some(true) {
println!("Authenticated");
if let Some(user) = value.get("user") {
print_user_line(user);
}
} else {
println!("Not authenticated");
}
Ok(())
}
fn print_auth_login(value: &Value) -> Result<()> {
println!("Authenticated");
if let Some(user) = value.get("user") {
print_user_line(user);
}
if value.get("stored").and_then(Value::as_bool) == Some(true) {
println!("Token stored locally");
} else if let Some(token) = value.get("token").and_then(Value::as_str) {
println!("Token: {token}");
}
Ok(())
}
fn print_logout(_: &Value) -> Result<()> {
println!("Local token removed");
Ok(())
}
fn print_user_line(user: &Value) {
let name = first_str(user, &["display_name", "name", "login"]).unwrap_or("-");
let id = first_str(user, &["id", "user_id"]).unwrap_or("-");
println!("User: {name} ({id})");
}
fn print_challenge_list(value: &Value) -> Result<()> {
let challenges = value
.get("challenges")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("challenge list response did not include challenges array"))?;
let id_width = challenges
.iter()
.filter_map(|challenge| challenge.get("id").and_then(Value::as_str))
.map(str::len)
.chain(std::iter::once("ID".len()))
.max()
.unwrap_or("ID".len());
let title_width = challenges
.iter()
.filter_map(|challenge| challenge.get("title").and_then(Value::as_str))
.map(str::len)
.chain(std::iter::once("TITLE".len()))
.max()
.unwrap_or("TITLE".len());
println!("{:<id_width$} {:<title_width$} LANGUAGES", "ID", "TITLE");
println!("{:-<id_width$} {:-<title_width$} ---------", "", "");
for challenge in challenges {
let id = challenge.get("id").and_then(Value::as_str).unwrap_or("-");
let title = challenge
.get("title")
.and_then(Value::as_str)
.unwrap_or("-");
let languages = challenge
.get("languages")
.and_then(Value::as_array)
.map(|languages| {
languages
.iter()
.filter_map(Value::as_str)
.collect::<Vec<_>>()
.join(", ")
})
.filter(|languages| !languages.is_empty())
.unwrap_or_else(|| "-".to_string());
println!("{id:<id_width$} {title:<title_width$} {languages}");
}
Ok(())
}
fn print_challenge_detail(value: &Value) -> Result<()> {
let id = value.get("id").and_then(Value::as_str).unwrap_or("-");
let title = value.get("title").and_then(Value::as_str).unwrap_or("-");
println!("{title} ({id})");
if let Some(description) = value.get("description").and_then(Value::as_array) {
println!();
for line in description.iter().filter_map(Value::as_str) {
println!("{line}");
}
}
println!();
println!(
"Languages: {}",
string_list(value.get("languages")).unwrap_or_else(|| "-".to_string())
);
if let Some(compilers) = value.get("compilers").and_then(Value::as_array) {
let labels = compilers
.iter()
.filter_map(|compiler| {
let id = compiler.get("id").and_then(Value::as_str)?;
let label = compiler.get("label").and_then(Value::as_str).unwrap_or(id);
let language = compiler
.get("language")
.and_then(Value::as_str)
.unwrap_or("-");
Some(format!("{id} ({label}, {language})"))
})
.collect::<Vec<_>>();
if !labels.is_empty() {
println!("Compilers: {}", labels.join(", "));
}
}
if let Some(limits) = value.get("limits").and_then(Value::as_object) {
if let Some(source_bytes) = limits.get("source_bytes").and_then(Value::as_u64) {
println!("Source limit: {}", format_bytes(source_bytes));
}
if let Some(options_bytes) = limits.get("compiler_options_bytes").and_then(Value::as_u64) {
println!("Compiler options limit: {}", format_bytes(options_bytes));
}
}
if let Some(options) = value.get("compiler_options").and_then(Value::as_object) {
println!();
println!("Compiler defaults:");
if let Some(rust) = options.get("rust_default").and_then(Value::as_str) {
println!(" rust: {rust}");
}
if let Some(cpp) = options.get("cpp_default").and_then(Value::as_str) {
println!(" cpp: {cpp}");
}
if let Some(asm) = options.get("asm_default").and_then(Value::as_str) {
println!(" asm: {asm}");
}
}
if let Some(urls) = value.get("urls").and_then(Value::as_object) {
println!();
println!("API paths:");
for key in ["leaderboard", "record_history", "submissions"] {
if let Some(url) = urls.get(key).and_then(Value::as_str) {
println!(" {key}: {url}");
}
}
}
Ok(())
}
fn print_systems(value: &Value) -> Result<()> {
let systems = value
.as_array()
.ok_or_else(|| anyhow!("systems response was not an array"))?;
let rows = systems
.iter()
.map(|system| {
vec![
field_string(system, "id"),
field_string(system, "label"),
field_string(system, "uarch"),
field_u64(system, "cpu")
.map(|cpu| cpu.to_string())
.unwrap_or_else(|| "-".to_string()),
]
})
.collect::<Vec<_>>();
print_table(&["ID", "LABEL", "UARCH", "CPU"], &rows);
Ok(())
}
fn print_leaderboard(value: &Value) -> Result<()> {
let challenge = field_string(value, "challenge_id");
let system = field_string(value, "system_id");
println!("{challenge} / {system}");
let entries = value
.get("entries")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("leaderboard response did not include entries array"))?;
let open_entries = value
.get("open_entries")
.and_then(Value::as_array)
.map(Vec::as_slice)
.unwrap_or(&[]);
let mut display_entries = entries
.iter()
.map(|entry| (false, entry))
.chain(open_entries.iter().map(|entry| (true, entry)))
.collect::<Vec<_>>();
if display_entries.is_empty() {
println!("No leaderboard entries.");
return Ok(());
}
display_entries.sort_by(|(left_open, left), (right_open, right)| {
field_u64(left, "time_ns")
.unwrap_or(u64::MAX)
.cmp(&field_u64(right, "time_ns").unwrap_or(u64::MAX))
.then_with(|| field_string(left, "language").cmp(&field_string(right, "language")))
.then_with(|| field_string(left, "user_id").cmp(&field_string(right, "user_id")))
.then_with(|| left_open.cmp(right_open))
.then_with(|| field_string(left, "job_id").cmp(&field_string(right, "job_id")))
});
let rows = display_entries
.iter()
.map(|(is_open, entry)| {
vec![
if *is_open {
String::new()
} else {
field_u64(entry, "rank")
.map(|rank| rank.to_string())
.unwrap_or_else(|| "-".to_string())
},
field_string(entry, "user_display_name"),
if entry
.get("solution_is_public")
.and_then(Value::as_bool)
.unwrap_or(false)
{
"public".to_string()
} else {
"-".to_string()
},
field_string(entry, "language"),
ns_field(entry, "time_ns"),
field_u64(entry, "cycles")
.map(format_count)
.unwrap_or_else(|| "-".to_string()),
field_string(entry, "job_id"),
]
})
.collect::<Vec<_>>();
print_table(
&["RANK", "USER", "PUBLIC", "LANG", "TIME", "CYCLES", "JOB"],
&rows,
);
Ok(())
}
fn print_all_systems_leaderboard(value: &Value) -> Result<()> {
let challenge = field_string(value, "challenge_id");
let system = field_string(value, "system_id");
println!("{challenge} / {system}");
let entries = value
.get("entries")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("all-systems leaderboard response did not include entries array"))?;
if entries.is_empty() {
println!("No leaderboard entries.");
return Ok(());
}
let mut display_entries = entries.iter().collect::<Vec<_>>();
display_entries.sort_by(|left, right| {
field_u64(left, "rank")
.unwrap_or(u64::MAX)
.cmp(&field_u64(right, "rank").unwrap_or(u64::MAX))
.then_with(|| field_u64(left, "score_ppm").cmp(&field_u64(right, "score_ppm")))
.then_with(|| field_string(left, "language").cmp(&field_string(right, "language")))
.then_with(|| field_string(left, "user_id").cmp(&field_string(right, "user_id")))
.then_with(|| {
field_string(left, "solution_id").cmp(&field_string(right, "solution_id"))
})
});
let rows = display_entries
.iter()
.map(|entry| {
vec![
field_u64(entry, "rank")
.map(|rank| rank.to_string())
.unwrap_or_else(|| "-".to_string()),
field_string(entry, "user_display_name"),
if entry
.get("solution_is_public")
.and_then(Value::as_bool)
.unwrap_or(false)
{
"public".to_string()
} else {
"-".to_string()
},
field_string(entry, "language"),
field_u64(entry, "score_ppm")
.map(format_ratio_ppm)
.unwrap_or_else(|| "-".to_string()),
all_systems_runs_summary(entry),
field_string(entry, "solution_id"),
]
})
.collect::<Vec<_>>();
print_table(
&[
"RANK", "USER", "PUBLIC", "LANG", "GEOMEAN", "RUNS", "SOLUTION",
],
&rows,
);
Ok(())
}
fn print_submission(value: &Value) -> Result<()> {
println!("Solution: {}", field_string(value, "solution_id"));
println!("User: {}", field_string(value, "user_id"));
print_submission_jobs(value);
Ok(())
}
fn print_submission_with_jobs(value: &Value) -> Result<()> {
let submission = value
.get("submission")
.ok_or_else(|| anyhow!("wait response did not include submission"))?;
print_submission(submission)?;
println!();
println!("Finished jobs:");
let jobs = value
.get("jobs")
.and_then(Value::as_array)
.ok_or_else(|| anyhow!("wait response did not include jobs array"))?;
print_job_rows(jobs);
Ok(())
}
fn print_submission_jobs(value: &Value) {
let Some(jobs) = value.get("jobs").and_then(Value::as_array) else {
return;
};
if jobs.is_empty() {
println!("Jobs: none");
return;
}
let rows = jobs
.iter()
.map(|job| vec![field_string(job, "system_id"), field_string(job, "job_id")])
.collect::<Vec<_>>();
println!("Jobs:");
print_table(&["SYSTEM", "JOB"], &rows);
}
fn print_job(value: &Value) -> Result<()> {
println!("Job: {}", field_string(value, "id"));
println!("Challenge: {}", field_string(value, "challenge_id"));
println!("Solution: {}", field_string(value, "solution_id"));
println!("System: {}", field_string(value, "system_id"));
println!("Language: {}", field_string(value, "language"));
println!("Status: {}", field_string(value, "status"));
if let Some(time) = field_u64(value, "result_time_ns") {
println!("Time: {}", format_ns(time));
}
if let Some(time) = field_u64(value, "result_time_max_ns") {
println!("Max time: {}", format_ns(time));
}
if let Some(cycles) = field_u64(value, "result_cycles") {
println!("Cycles: {}", format_count(cycles));
}
if let Some(error) = value.get("result_error").and_then(Value::as_str) {
println!("Error: {error}");
}
if let Some(counters) = value.get("result_counters").and_then(Value::as_object) {
if !counters.is_empty() {
println!("Counters:");
for (name, count) in counters {
if let Some(count) = count.as_u64() {
println!(" {name}: {}", format_count(count));
}
}
}
}
Ok(())
}
fn print_top_down(value: &Value) -> Result<()> {
let analysis = value
.get("analysis")
.ok_or_else(|| anyhow!("top-down response did not include analysis"))?;
println!("Job: {}", field_string(value, "job_id"));
println!("System: {}", field_string(value, "system_id"));
println!("Uarch: {}", field_string(analysis, "uarch"));
if let Some(slots) = analysis.get("slots").and_then(Value::as_object) {
let raw = slots
.get("raw")
.and_then(Value::as_u64)
.map(format_count)
.unwrap_or_else(|| "-".to_string());
let source = slots.get("source").and_then(Value::as_str).unwrap_or("-");
println!("Slots: {raw} ({source})");
}
if let Some(notes) = string_list(analysis.get("notes")) {
println!("Notes: {notes}");
}
print_top_down_metric_table(
"Top level",
analysis.get("top_level"),
&[
("Retiring", "retiring"),
("Bad speculation", "bad_speculation"),
("Frontend bound", "frontend_bound"),
("Backend bound", "backend_bound"),
("Unclassified", "unclassified"),
],
);
print_top_down_metric_table(
"Frontend",
analysis.get("frontend"),
&[
("All frontend bound", "all"),
("Latency", "latency"),
("Bandwidth", "bandwidth"),
("Other", "other"),
],
);
print_top_down_metric_table(
"Bad speculation",
analysis.get("bad_speculation"),
&[
("All bad speculation", "all"),
("Branch mispredict", "branch_mispredict"),
("Machine clears", "machine_clears_slots"),
("Other", "other"),
],
);
print_top_down_metric_table(
"Backend",
analysis.get("backend"),
&[
("All backend bound", "all"),
("Memory bound", "memory_bound"),
("Core bound", "core_bound"),
("Alloc restrictions", "alloc_restrictions"),
("Scheduler", "non_memory_scheduler"),
("Register", "register"),
("Reorder buffer", "reorder_buffer"),
("Serialization", "serialization"),
("Other core", "other_core_bound"),
],
);
print_top_down_execution_summary(analysis.get("execution"));
Ok(())
}
fn print_top_down_metric_table(title: &str, section: Option<&Value>, metrics: &[(&str, &str)]) {
let section = section.and_then(Value::as_object);
let rows = metrics
.iter()
.map(|(label, key)| {
top_down_metric_row(label, section.and_then(|section| section.get(*key)))
})
.collect::<Vec<_>>();
println!();
println!("{title}:");
print_table(&["METRIC", "RAW SLOTS", "% SLOTS", "% PARENT"], &rows);
}
fn top_down_metric_row(label: &str, metric: Option<&Value>) -> Vec<String> {
vec![
label.to_string(),
top_down_raw(metric),
top_down_fraction(metric, "fraction_of_slots"),
top_down_fraction(metric, "fraction_of_parent"),
]
}
fn print_top_down_execution_summary(execution: Option<&Value>) {
let Some(execution) = execution else {
return;
};
let ipc = execution
.get("ipc")
.and_then(Value::as_f64)
.map(|ipc| format!("{ipc:.3}"))
.unwrap_or_else(|| "-".to_string());
let branch_mispredict = execution
.get("branch_mispredict_rate")
.and_then(|metric| metric.get("fraction"))
.and_then(Value::as_f64)
.map(format_percent)
.unwrap_or_else(|| "-".to_string());
println!();
println!("Execution:");
print_table(
&["METRIC", "VALUE"],
&[
vec!["IPC".to_string(), ipc],
vec!["Branch mispredict rate".to_string(), branch_mispredict],
],
);
}
fn print_jobs_page(value: &Value) -> Result<()> {
let jobs = value
.get("items")
.and_then(Value::as_array)
.or_else(|| value.as_array())
.ok_or_else(|| anyhow!("jobs response did not include items array"))?;
if jobs.is_empty() {
println!("No jobs.");
} else {
print_job_rows(jobs);
}
if let Some(cursor) = value.get("next_cursor").and_then(Value::as_str) {
println!();
println!("Next cursor: {cursor}");
}
Ok(())
}
fn print_queue_jobs_page(value: &Value) -> Result<()> {
let jobs = value
.get("items")
.and_then(Value::as_array)
.or_else(|| value.as_array())
.ok_or_else(|| anyhow!("jobs response did not include items array"))?;
if jobs.is_empty() {
println!("No jobs.");
} else {
print_queue_job_rows(jobs);
}
if let Some(cursor) = value.get("next_cursor").and_then(Value::as_str) {
println!();
println!("Next cursor: {cursor}");
}
Ok(())
}
fn print_job_rows(jobs: &[Value]) {
let rows = jobs
.iter()
.map(|job| {
vec![
field_string(job, "id"),
field_string(job, "challenge_id"),
field_string(job, "system_id"),
field_string(job, "language"),
field_string(job, "status"),
ns_field(job, "result_time_ns"),
]
})
.collect::<Vec<_>>();
print_table(
&["JOB", "CHALLENGE", "SYSTEM", "LANG", "STATUS", "TIME"],
&rows,
);
}
fn print_queue_job_rows(jobs: &[Value]) {
let rows = jobs
.iter()
.map(|job| {
vec![
first_str(job, &["user_display_name", "user_id"])
.unwrap_or("-")
.to_string(),
field_string(job, "challenge_id"),
field_string(job, "system_id"),
field_string(job, "language"),
field_string(job, "status"),
ns_field(job, "result_time_ns"),
]
})
.collect::<Vec<_>>();
print_table(
&["USER", "CHALLENGE", "SYSTEM", "LANG", "STATUS", "TIME"],
&rows,
);
}
fn print_solution(value: &Value) -> Result<()> {
println!("Solution: {}", field_string(value, "id"));
println!("Challenge: {}", field_string(value, "challenge_id"));
println!(
"User: {} ({})",
field_string(value, "user_display_name"),
field_string(value, "user_id")
);
println!("Language: {}", field_string(value, "language"));
if let Some(compiler) = value.get("compiler").and_then(Value::as_str) {
println!("Compiler: {compiler}");
}
println!(
"Visibility: {}",
if value
.get("is_public")
.and_then(Value::as_bool)
.unwrap_or(false)
{
"public"
} else {
"private"
}
);
if let Some(jobs_url) = value.get("jobs_url").and_then(Value::as_str) {
println!("Jobs: {jobs_url}");
}
let source_visible = value
.get("source_visible")
.and_then(Value::as_bool)
.unwrap_or(false);
if source_visible {
if let Some(options) = value.get("compiler_options").and_then(Value::as_str) {
println!();
println!("Compiler options:");
println!("{options}");
}
if let Some(source) = value.get("source").and_then(Value::as_str) {
println!();
println!("Source:");
println!("{source}");
}
} else {
println!("Source: hidden");
}
Ok(())
}
fn print_solution_visibility(value: &Value) -> Result<()> {
println!("Solution: {}", field_string(value, "solution_id"));
println!(
"Visibility: {}",
if value
.get("is_public")
.and_then(Value::as_bool)
.unwrap_or(false)
{
"public"
} else {
"private"
}
);
Ok(())
}
fn string_list(value: Option<&Value>) -> Option<String> {
value
.and_then(Value::as_array)
.map(|items| {
items
.iter()
.filter_map(Value::as_str)
.collect::<Vec<_>>()
.join(", ")
})
.filter(|items| !items.is_empty())
}
fn format_bytes(bytes: u64) -> String {
if bytes % 1024 == 0 {
format!("{} KiB", bytes / 1024)
} else {
format!("{bytes} bytes")
}
}
fn first_str<'a>(value: &'a Value, keys: &[&str]) -> Option<&'a str> {
keys.iter()
.find_map(|key| value.get(*key).and_then(Value::as_str))
}
fn field_string(value: &Value, key: &str) -> String {
value
.get(key)
.and_then(Value::as_str)
.unwrap_or("-")
.to_string()
}
fn field_u64(value: &Value, key: &str) -> Option<u64> {
value.get(key).and_then(Value::as_u64)
}
fn ns_field(value: &Value, key: &str) -> String {
field_u64(value, key)
.map(format_ns)
.unwrap_or_else(|| "-".to_string())
}
fn top_down_raw(metric: Option<&Value>) -> String {
metric
.and_then(|metric| metric.get("raw"))
.and_then(|raw| {
raw.as_i64()
.map(format_signed_count)
.or_else(|| raw.as_u64().map(format_count))
})
.unwrap_or_else(|| "-".to_string())
}
fn top_down_fraction(metric: Option<&Value>, key: &str) -> String {
metric
.and_then(|metric| metric.get(key))
.and_then(Value::as_f64)
.map(format_percent)
.unwrap_or_else(|| "-".to_string())
}
fn format_percent(fraction: f64) -> String {
format!("{:.2}%", fraction * 100.0)
}
fn format_ratio_ppm(ppm: u64) -> String {
format!("{:.3}x", ppm as f64 / RATIO_PPM_SCALE)
}
fn format_ns(ns: u64) -> String {
format!("{:.3} ms", ns as f64 / 1_000_000.0)
}
fn format_count(value: u64) -> String {
let digits = value.to_string();
let mut out = String::with_capacity(digits.len() + digits.len() / 3);
for (index, ch) in digits.chars().enumerate() {
let remaining = digits.len() - index;
if index > 0 && remaining % 3 == 0 {
out.push(',');
}
out.push(ch);
}
out
}
fn format_signed_count(value: i64) -> String {
if value < 0 {
format!("-{}", format_count(value.unsigned_abs()))
} else {
format_count(value as u64)
}
}
fn safe_filename_component(value: &str) -> String {
value
.chars()
.map(|ch| match ch {
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '_' | '.' => ch,
_ => '_',
})
.collect()
}
fn all_systems_runs_summary(entry: &Value) -> String {
entry
.get("runs")
.and_then(Value::as_array)
.map(|runs| {
runs.iter()
.map(|run| {
let system = run.get("system_id").and_then(Value::as_str).unwrap_or("-");
let ratio = field_u64(run, "ratio_ppm")
.map(format_ratio_ppm)
.unwrap_or_else(|| "-".to_string());
format!("{system}={ratio}")
})
.collect::<Vec<_>>()
.join(", ")
})
.filter(|summary| !summary.is_empty())
.unwrap_or_else(|| "-".to_string())
}
fn print_table(headers: &[&str], rows: &[Vec<String>]) {
let mut widths = headers
.iter()
.map(|header| header.len())
.collect::<Vec<_>>();
for row in rows {
for (index, cell) in row.iter().enumerate() {
if let Some(width) = widths.get_mut(index) {
*width = (*width).max(cell.len());
}
}
}
for (index, header) in headers.iter().enumerate() {
if index > 0 {
print!(" ");
}
print!("{header:<width$}", width = widths[index]);
}
println!();
for (index, width) in widths.iter().enumerate() {
if index > 0 {
print!(" ");
}
print!("{:-<width$}", "");
}
println!();
for row in rows {
for (index, width) in widths.iter().enumerate() {
if index > 0 {
print!(" ");
}
let cell = row.get(index).map(String::as_str).unwrap_or("-");
print!("{cell:<width$}");
}
println!();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_systems_aliases_are_accepted() {
assert!(is_all_systems_alias("all_systems"));
assert!(is_all_systems_alias("all-systems"));
assert!(is_all_systems_alias("all"));
assert!(!is_all_systems_alias("raptor_cove_p"));
}
#[test]
fn formats_ratio_ppm_as_slowdown() {
assert_eq!(format_ratio_ppm(1_000_000), "1.000x");
assert_eq!(format_ratio_ppm(1_091_039), "1.091x");
}
#[test]
fn summarizes_all_systems_runs() {
let entry = json!({
"runs": [
{"system_id": "raptor_cove_p", "ratio_ppm": 1_190_366},
{"system_id": "gracemont_e", "ratio_ppm": 1_000_000}
]
});
assert_eq!(
all_systems_runs_summary(&entry),
"raptor_cove_p=1.190x, gracemont_e=1.000x"
);
}
#[test]
fn formats_top_down_metric_rows() {
let metric = json!({
"raw": -24056,
"fraction_of_slots": -0.0001,
"fraction_of_parent": null
});
assert_eq!(
top_down_metric_row("Unclassified", Some(&metric)),
vec![
"Unclassified".to_string(),
"-24,056".to_string(),
"-0.01%".to_string(),
"-".to_string(),
]
);
}
}