use clap::{Parser, Subcommand};
use vynco::{AuditorTenureParams, ChangeListParams, Client, CompanyListParams, VyncoError};
#[derive(Parser)]
#[command(name = "vynco")]
#[command(about = "VynCo Swiss Corporate Intelligence CLI — example for the vynco Rust SDK v2")]
struct Cli {
#[arg(long, env = "VYNCO_API_KEY")]
api_key: String,
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Health,
Companies {
#[arg(long)]
canton: Option<String>,
#[arg(long)]
search: Option<String>,
#[arg(long, default_value = "1")]
page: i64,
#[arg(long, default_value = "10")]
page_size: i64,
},
Company {
uid: String,
},
Count,
Events {
uid: String,
#[arg(long)]
limit: Option<u32>,
},
Screen {
name: String,
#[arg(long)]
uid: Option<String>,
},
Dashboard,
Auditors {
#[arg(long)]
min_years: Option<f64>,
#[arg(long)]
canton: Option<String>,
},
Risk {
uid: String,
},
Fingerprint {
uid: String,
},
Graph {
uid: String,
},
Dossier {
uid: String,
},
Compare {
uids: Vec<String>,
},
Credits,
Team,
Changes {
#[arg(long, default_value = "1")]
page: i64,
#[arg(long, default_value = "10")]
page_size: i64,
},
BoardMembers {
uid: String,
},
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
let client = match Client::builder(&cli.api_key).build() {
Ok(c) => c,
Err(e) => {
eprintln!("Failed to create client: {e}");
std::process::exit(1);
}
};
if let Err(e) = run(client, cli.command).await {
eprintln!("\nError: {e}");
match &e {
VyncoError::Authentication(_) => {
eprintln!("Hint: Check your VYNCO_API_KEY or pass --api-key");
}
VyncoError::InsufficientCredits(_) => {
eprintln!("Hint: Top up your credits at https://vynco.ch");
}
VyncoError::RateLimit(_) => {
eprintln!("Hint: You've hit the rate limit — wait a moment and retry");
}
_ => {}
}
std::process::exit(1);
}
}
async fn run(client: Client, command: Command) -> Result<(), VyncoError> {
match command {
Command::Health => {
let resp = client.health().check().await?;
println!("API Status: {}", resp.data.status);
println!("Database: {}", resp.data.database);
println!("Redis: {}", resp.data.redis);
println!("Version: {}", resp.data.version);
print_meta(&resp.meta);
}
Command::Companies {
canton,
search,
page,
page_size,
} => {
let params = CompanyListParams {
page: Some(page),
page_size: Some(page_size),
canton,
search,
..Default::default()
};
let resp = client.companies().list(¶ms).await?;
let total_pages = if resp.data.page_size > 0 {
(resp.data.total as f64 / resp.data.page_size as f64).ceil() as i64
} else {
1
};
println!(
"Companies: page {}/{} ({} total)\n",
resp.data.page, total_pages, resp.data.total,
);
for c in &resp.data.items {
println!(
" {:<18} {:<45} {:<6} {}",
c.uid,
c.name,
c.canton.as_deref().unwrap_or("-"),
c.status.as_deref().unwrap_or("-"),
);
}
print_meta(&resp.meta);
}
Command::Company { uid } => {
let resp = client.companies().get(&uid).await?;
let c = &resp.data;
println!("UID: {}", c.uid);
println!("Name: {}", c.name);
println!("Legal form: {}", c.legal_form.as_deref().unwrap_or("-"));
println!("Status: {}", c.status.as_deref().unwrap_or("-"));
println!("Canton: {}", c.canton.as_deref().unwrap_or("-"));
if let Some(ref cap) = c.share_capital {
println!("Share cap: {:.2}", cap);
}
if let Some(ref cat) = c.auditor_category {
println!("Auditor cat: {}", cat);
}
if let Some(ref d) = c.updated_at {
println!("Updated: {}", d);
}
print_meta(&resp.meta);
}
Command::Count => {
let resp = client.companies().count().await?;
println!("Company count: {}", resp.data.count);
print_meta(&resp.meta);
}
Command::Events { uid, limit } => {
let resp = client.companies().events(&uid, limit).await?;
println!("Events for {}: {} total\n", uid, resp.data.count);
for evt in &resp.data.events {
println!(
" [{:<10}] {:<30} {}",
evt.category, evt.ce_type, evt.summary,
);
}
print_meta(&resp.meta);
}
Command::Screen { name, uid } => {
let req = vynco::ScreeningRequest {
name: name.clone(),
uid,
sources: None,
};
let resp = client.screening().screen(&req).await?;
println!("Screening: \"{}\"", name);
println!("Risk level: {}", resp.data.risk_level);
println!("Hits: {}", resp.data.hit_count);
println!("Sources: {}", resp.data.sources_checked.join(", "));
if !resp.data.hits.is_empty() {
println!("\nMatches:");
for hit in &resp.data.hits {
println!(
" {} (score: {:.2}) — {} [{}]",
hit.matched_name,
hit.score,
hit.source,
hit.datasets.join(", "),
);
}
}
print_meta(&resp.meta);
}
Command::Dashboard => {
let resp = client.dashboard().get().await?;
let d = &resp.data.data;
println!("Total companies: {}", d.total_companies);
println!("With canton: {}", d.with_canton);
println!("With status: {}", d.with_status);
println!("With legal form: {}", d.with_legal_form);
println!("With capital: {}", d.with_capital);
println!("Completeness: {:.1}%", d.completeness_pct);
let t = &resp.data.auditor_tenures;
println!("\nAuditor tenures:");
println!(" Total: {}", t.total_tenures);
println!(" Long (7+yr): {}", t.long_tenures_7plus);
println!(" Avg years: {:.1}", t.avg_tenure_years);
println!(" Max years: {:.1}", t.max_tenure_years);
print_meta(&resp.meta);
}
Command::Auditors { min_years, canton } => {
let params = AuditorTenureParams {
min_years,
canton,
page: Some(1),
page_size: Some(20),
};
let resp = client.auditors().tenures(¶ms).await?;
let total_pages = if resp.data.page_size > 0 {
(resp.data.total as f64 / resp.data.page_size as f64).ceil() as i64
} else {
1
};
println!(
"Auditor tenures: page {}/{} ({} total)\n",
resp.data.page, total_pages, resp.data.total,
);
for t in &resp.data.items {
println!(
" {:<18} {:<35} {:<25} {:.1} yr",
t.company_uid,
t.company_name,
t.auditor_name,
t.tenure_years.unwrap_or(0.0),
);
}
print_meta(&resp.meta);
}
Command::Risk { uid } => {
let req = vynco::RiskScoreRequest { uid: uid.clone() };
let resp = client.ai().risk_score(&req).await?;
let r = &resp.data;
println!("Risk score for {} ({})", r.uid, r.company_name);
println!("Overall: {}/100", r.overall_score);
println!("Risk level: {}", r.risk_level);
if !r.breakdown.is_empty() {
println!("\nBreakdown:");
for f in &r.breakdown {
println!(
" {:<20} score: {:<4} weight: {:.2} {}",
f.factor, f.score, f.weight, f.description,
);
}
}
print_meta(&resp.meta);
}
Command::Fingerprint { uid } => {
let resp = client.companies().fingerprint(&uid).await?;
let f = &resp.data;
println!("Fingerprint for {} ({})", f.company_uid, f.name);
println!("Canton: {}", f.canton);
println!("Legal form: {}", f.legal_form);
println!("Board size: {}", f.board_size);
println!("Company age: {} years", f.company_age);
println!("Change freq: {} events", f.change_frequency);
if let Some(ref sector) = f.industry_sector {
println!("Sector: {}", sector);
}
if let Some(ref size) = f.size_category {
println!("Size: {}", size);
}
if let Some(employees) = f.employee_count_estimate {
println!("Employees: ~{}", employees);
}
if let Some(capital) = f.capital_amount {
println!(
"Capital: {:.0} {}",
capital,
f.capital_currency.as_deref().unwrap_or("CHF")
);
}
if let Some(ref tier) = f.auditor_tier {
println!("Auditor: {}", tier);
}
println!(
"Parent co: {}",
if f.has_parent_company { "yes" } else { "no" }
);
println!("Subsidiaries:{}", f.subsidiary_count);
print_meta(&resp.meta);
}
Command::Graph { uid } => {
let resp = client.graph().get(&uid).await?;
let g = &resp.data;
println!("Network graph for {}\n", uid);
println!("Nodes: {} Links: {}", g.nodes.len(), g.links.len());
if !g.nodes.is_empty() {
println!("\nSample nodes:");
for node in g.nodes.iter().take(10) {
println!(" [{:<8}] {:<40} {}", node.node_type, node.name, node.uid,);
}
if g.nodes.len() > 10 {
println!(" ... and {} more", g.nodes.len() - 10);
}
}
if !g.links.is_empty() {
println!("\nSample links:");
for link in g.links.iter().take(10) {
println!(" {} -> {} ({})", link.source, link.target, link.link_type,);
}
if g.links.len() > 10 {
println!(" ... and {} more", g.links.len() - 10);
}
}
print_meta(&resp.meta);
}
Command::Dossier { uid } => {
let req = vynco::CreateDossierRequest {
uid: uid.clone(),
level: Some("standard".into()),
};
let resp = client.dossiers().create(&req).await?;
let d = &resp.data;
println!("Dossier for {} ({})", d.company_uid, d.company_name);
println!("ID: {}", d.id);
println!("Level: {}", d.level);
println!("Sources: {}", d.sources.join(", "));
println!("Created: {}", d.created_at);
println!("\n{}", d.content);
print_meta(&resp.meta);
}
Command::Compare { uids } => {
if uids.len() < 2 {
eprintln!("Error: compare requires at least two UIDs");
std::process::exit(1);
}
let req = vynco::CompareRequest { uids };
let resp = client.companies().compare(&req).await?;
let c = &resp.data;
println!("Comparing {} companies:\n", c.uids.len());
for (i, name) in c.names.iter().enumerate() {
println!(" [{}] {} ({})", i + 1, name, c.uids[i]);
}
if !c.dimensions.is_empty() {
println!("\nDimensions:");
for dim in &c.dimensions {
let vals: Vec<&str> = dim
.values
.iter()
.map(|v| v.as_deref().unwrap_or("-"))
.collect();
println!(" {:<20} {}", dim.label, vals.join(" | "));
}
}
print_meta(&resp.meta);
}
Command::Credits => {
let resp = client.credits().balance().await?;
let b = &resp.data;
println!("Credit balance: {}", b.balance);
println!("Monthly credits: {}", b.monthly_credits);
println!("Used this month: {}", b.used_this_month);
println!("Tier: {}", b.tier);
println!("Overage rate: {}", b.overage_rate);
print_meta(&resp.meta);
}
Command::Team => {
let resp = client.teams().me().await?;
let t = &resp.data;
println!("Team ID: {}", t.id);
println!("Name: {}", t.name);
println!("Slug: {}", t.slug);
println!("Tier: {}", t.tier);
println!("Credit balance: {}", t.credit_balance);
println!("Monthly credits: {}", t.monthly_credits);
print_meta(&resp.meta);
}
Command::Changes { page, page_size } => {
let params = ChangeListParams {
page: Some(page),
page_size: Some(page_size),
..Default::default()
};
let resp = client.changes().list(¶ms).await?;
let total_pages = if resp.data.page_size > 0 {
(resp.data.total as f64 / resp.data.page_size as f64).ceil() as i64
} else {
1
};
println!(
"Changes: page {}/{} ({} total)\n",
resp.data.page, total_pages, resp.data.total,
);
for ch in &resp.data.items {
println!(
" {:<18} {:<12} {:<30} {}",
ch.company_uid,
ch.change_type,
ch.company_name.as_deref().unwrap_or("-"),
ch.detected_at,
);
}
print_meta(&resp.meta);
}
Command::BoardMembers { uid } => {
let resp = client.persons().board_members(&uid).await?;
println!("Board members for {}:\n", uid);
for m in &resp.data {
println!(
" {:<20} {:<20} {:<15} since {}",
m.first_name.as_deref().unwrap_or("-"),
m.last_name.as_deref().unwrap_or("-"),
m.role,
m.since.as_deref().unwrap_or("-"),
);
}
print_meta(&resp.meta);
}
}
Ok(())
}
fn print_meta(meta: &vynco::ResponseMeta) {
println!("\n--- Response metadata ---");
if let Some(ref id) = meta.request_id {
println!("Request ID: {id}");
}
if let Some(used) = meta.credits_used {
println!("Credits used: {used}");
}
if let Some(remaining) = meta.credits_remaining {
println!("Credits remaining: {remaining}");
}
if let Some(limit) = meta.rate_limit_limit {
println!("Rate limit: {limit}/min");
}
if let Some(remaining) = meta.rate_limit_remaining {
println!("Rate limit remaining:{remaining}");
}
if let Some(ref source) = meta.data_source {
println!("Data source: {source}");
}
}