use clap::Subcommand;
use serde::Serialize;
use std::path::PathBuf;
use crate::output::{self, TextFormat};
use altium_format::ops::prjpcb;
#[derive(Subcommand)]
pub enum PrjPcbCommands {
Overview {
path: PathBuf,
},
Info {
path: PathBuf,
},
Documents {
path: PathBuf,
#[arg(short = 't', long)]
doc_type: Option<String>,
},
Create {
path: PathBuf,
#[arg(short, long)]
name: Option<String>,
#[arg(short = 't', long)]
template: Option<PathBuf>,
},
AddDocument {
path: PathBuf,
document: String,
},
RemoveDocument {
path: PathBuf,
document: String,
},
Parameters {
path: PathBuf,
},
SetParameter {
path: PathBuf,
name: String,
value: String,
},
RemoveParameter {
path: PathBuf,
name: String,
},
Netlist {
path: PathBuf,
},
Components {
path: PathBuf,
},
Bom {
path: PathBuf,
#[arg(short, long)]
grouped: bool,
},
ImportToPcb {
path: PathBuf,
#[arg(short, long)]
pcb: Option<String>,
#[arg(long)]
dry_run: bool,
},
SyncToPcb {
path: PathBuf,
#[arg(short, long)]
pcb: Option<String>,
#[arg(long)]
dry_run: bool,
},
DiffSchPcb {
path: PathBuf,
#[arg(short, long)]
pcb: Option<String>,
},
Validate {
path: PathBuf,
#[arg(long)]
check_files: bool,
},
Json {
path: PathBuf,
#[arg(long)]
full: bool,
#[arg(long)]
pretty: bool,
},
}
pub fn run(cmd: &PrjPcbCommands, format: &str) -> Result<(), Box<dyn std::error::Error>> {
match cmd {
PrjPcbCommands::Overview { path } => {
let result = prjpcb::cmd_overview(path)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Info { path } => {
let result = prjpcb::cmd_info(path)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Documents { path, doc_type } => {
let result = prjpcb::cmd_documents(path, doc_type.clone())?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Create { path, name, template } => {
let result = prjpcb::cmd_create(path, name.clone(), template.clone())?;
println!("{}", result);
}
PrjPcbCommands::AddDocument { path, document } => {
let result = prjpcb::cmd_add_document(path, document)?;
println!("{}", result);
}
PrjPcbCommands::RemoveDocument { path, document } => {
let result = prjpcb::cmd_remove_document(path, document)?;
println!("{}", result);
}
PrjPcbCommands::Parameters { path } => {
let result = prjpcb::cmd_parameters(path)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::SetParameter { path, name, value } => {
let result = prjpcb::cmd_set_parameter(path, name, value)?;
println!("{}", result);
}
PrjPcbCommands::RemoveParameter { path, name } => {
let result = prjpcb::cmd_remove_parameter(path, name)?;
println!("{}", result);
}
PrjPcbCommands::Netlist { path } => {
let result = prjpcb::cmd_netlist(path)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Components { path } => {
let result = prjpcb::cmd_components(path)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Bom { path, grouped } => {
let result = prjpcb::cmd_bom(path, *grouped)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::ImportToPcb { path, pcb, dry_run } => {
let result = prjpcb::cmd_import_to_pcb(path, pcb.clone(), *dry_run)?;
println!("{}", result);
}
PrjPcbCommands::SyncToPcb { path, pcb, dry_run } => {
let result = prjpcb::cmd_sync_to_pcb(path, pcb.clone(), *dry_run)?;
println!("{}", result);
}
PrjPcbCommands::DiffSchPcb { path, pcb } => {
let result = prjpcb::cmd_diff_sch_pcb(path, pcb.clone())?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Validate { path, check_files } => {
let result = prjpcb::cmd_validate(path, *check_files)?;
output::print(&TextWrapper(result), format)?;
}
PrjPcbCommands::Json { path, full, pretty } => {
let result = prjpcb::cmd_json(path, *full, *pretty)?;
let json_str = serde_json::to_string_pretty(&result)?;
println!("{}", json_str);
}
}
Ok(())
}
#[derive(Serialize)]
#[serde(transparent)]
struct TextWrapper<T>(T);
impl<T: Serialize> TextFormat for TextWrapper<T> {
fn format_text(&self) -> String {
if let Ok(value) = serde_json::to_value(&self.0) {
format_value(&value, 0)
} else {
"Error formatting output".to_string()
}
}
}
fn format_value(value: &serde_json::Value, indent: usize) -> String {
let prefix = " ".repeat(indent);
match value {
serde_json::Value::Object(map) => {
let mut out = String::new();
for (key, val) in map {
match val {
serde_json::Value::String(s) => {
out.push_str(&format!("{}{}: {}\n", prefix, key, s));
}
serde_json::Value::Number(n) => {
out.push_str(&format!("{}{}: {}\n", prefix, key, n));
}
serde_json::Value::Bool(b) => {
out.push_str(&format!("{}{}: {}\n", prefix, key, b));
}
serde_json::Value::Null => {
out.push_str(&format!("{}{}: null\n", prefix, key));
}
serde_json::Value::Array(arr) => {
if arr.is_empty() {
out.push_str(&format!("{}{}: []\n", prefix, key));
} else {
out.push_str(&format!("{}{}:\n", prefix, key));
for item in arr {
out.push_str(&format_value(item, indent + 1));
out.push('\n');
}
}
}
serde_json::Value::Object(_) => {
out.push_str(&format!("{}{}:\n", prefix, key));
out.push_str(&format_value(val, indent + 1));
}
}
}
out
}
serde_json::Value::Array(arr) => {
let mut out = String::new();
for (i, item) in arr.iter().enumerate() {
out.push_str(&format!("{}[{}]\n", prefix, i));
out.push_str(&format_value(item, indent + 1));
}
out
}
serde_json::Value::String(s) => format!("{}{}\n", prefix, s),
serde_json::Value::Number(n) => format!("{}{}\n", prefix, n),
serde_json::Value::Bool(b) => format!("{}{}\n", prefix, b),
serde_json::Value::Null => format!("{}null\n", prefix),
}
}