use anyhow::{Context, Result};
use clap::Parser;
use heck::ToSnakeCase;
use std::{
fs,
io::{self, Write},
};
use zlink::idl::Interface;
use zlink_codegen::{format_code, generate_interface, generate_interfaces};
mod cli;
use cli::Args;
fn main() -> Result<()> {
let args = Args::parse();
let (files, output, multiple_files) = match args.command {
Some(cli::Command::Generate {
files,
output,
multiple_files,
}) => (files, output, multiple_files),
None => (args.files, args.output, args.multiple_files),
};
if files.is_empty() {
eprintln!("Error: No input files specified");
eprintln!("Usage: zlink-codegen <FILES>... [OPTIONS]");
std::process::exit(1);
}
let mut file_contents = Vec::new();
let mut interfaces = Vec::new();
for file_path in &files {
let content = fs::read_to_string(file_path)
.with_context(|| format!("Failed to read file: {}", file_path.display()))?;
file_contents.push(content);
}
for (i, file_path) in files.iter().enumerate() {
let interface = Interface::try_from(file_contents[i].as_str())
.with_context(|| format!("Failed to parse interface from: {}", file_path.display()))?;
interfaces.push(interface);
}
if let Some(output_path) = output {
let output = if interfaces.len() == 1 {
generate_interface(&interfaces[0]).with_context(|| {
format!(
"Failed to generate code for interface: {}",
interfaces[0].name()
)
})?
} else {
generate_interfaces(&interfaces)
.with_context(|| "Failed to generate code for interfaces".to_string())?
};
let formatted = format_code(&output)?;
fs::write(&output_path, formatted)
.with_context(|| format!("Failed to write output file: {}", output_path.display()))?;
println!("Generated code written to {}", output_path.display());
} else if multiple_files {
for interface in &interfaces {
let code = generate_interface(interface).with_context(|| {
format!(
"Failed to generate code for interface: {}",
interface.name()
)
})?;
let formatted = format_code(&code)?;
let filename = interface_to_filename(interface.name());
let output_path = format!("{}.rs", filename);
fs::write(&output_path, formatted)
.with_context(|| format!("Failed to write output file: {}", output_path))?;
println!(
"Generated code for `{}` written to {}",
interface.name(),
output_path
);
}
} else {
let output = if interfaces.len() == 1 {
generate_interface(&interfaces[0]).with_context(|| {
format!(
"Failed to generate code for interface: {}",
interfaces[0].name()
)
})?
} else {
generate_interfaces(&interfaces)
.with_context(|| "Failed to generate code for interfaces".to_string())?
};
let formatted = format_code(&output)?;
io::stdout().write_all(formatted.as_bytes())?;
}
Ok(())
}
fn interface_to_filename(interface_name: &str) -> String {
interface_name
.split('.')
.next_back()
.unwrap_or(interface_name)
.to_snake_case()
}