use clap::Parser;
use pisnge::parser::parse_pie_chart;
use pisnge::png::svg_to_png;
use pisnge::renderer::render_pie_chart_svg;
use std::fs;
use std::path::Path;
#[derive(Parser)]
#[command(name = "pisnge")]
#[command(about = "A Rust-based diagram rendering library inspired by Mermaid.js")]
struct Cli {
#[arg(short, long)]
input: String,
#[arg(short, long)]
output: String,
#[arg(short, long, value_parser = ["png", "svg"])]
format: Option<String>,
#[arg(short, long)]
verbose: bool,
#[arg(short, long, default_value = "800")]
width: u32,
#[arg(short = 'H', long, default_value = "600")]
height: u32,
#[arg(long, default_value = "Liberation Sans")]
font: String,
}
fn detect_format_from_extension(output_path: &str) -> Option<String> {
Path::new(output_path)
.extension()
.and_then(|ext| ext.to_str())
.map(|ext| ext.to_lowercase())
.and_then(|ext| match ext.as_str() {
"png" => Some("png".to_string()),
"svg" => Some("svg".to_string()),
_ => None,
})
}
fn main() {
let cli = Cli::parse();
println!("Pisnge - Diagram Renderer");
let output_format = match cli.format {
Some(format) => format,
None => match detect_format_from_extension(&cli.output) {
Some(format) => format,
None => {
eprintln!("Error: Could not detect output format from file extension '{}'. Please specify format using -f flag.",
Path::new(&cli.output).extension().and_then(|ext| ext.to_str()).unwrap_or("(none)"));
eprintln!("Supported formats: png, svg");
std::process::exit(1);
}
},
};
if cli.verbose {
println!("Input file: {}", cli.input);
println!("Output file: {}", cli.output);
println!("Output format: {}", output_format);
}
match fs::read_to_string(&cli.input) {
Ok(content) => {
let normalized_content = if content.ends_with('\n') {
content
} else {
format!("{}\n", content)
};
match parse_pie_chart(&normalized_content) {
Ok((_, pie_chart)) => {
if cli.verbose {
println!("\nParsed pie chart:");
println!(" Show data: {}", pie_chart.show_data);
if let Some(title) = &pie_chart.title {
println!(" Title: {}", title);
}
println!(" Data entries: {}", pie_chart.data.len());
for entry in &pie_chart.data {
println!(" \"{}\": {}", entry.label, entry.value);
}
}
match output_format.as_str() {
"svg" => {
let (svg_document, _) =
render_pie_chart_svg(&pie_chart, cli.width, cli.height, &cli.font);
match fs::write(&cli.output, svg_document.to_string()) {
Ok(_) => println!("SVG saved to: {}", cli.output),
Err(e) => {
eprintln!("Failed to write SVG file: {}", e);
std::process::exit(1);
}
}
}
"png" => {
let (svg_document, actual_height) =
render_pie_chart_svg(&pie_chart, cli.width, cli.height, &cli.font);
match svg_to_png(&svg_document.to_string(), cli.width, actual_height) {
Ok(png_data) => match fs::write(&cli.output, png_data) {
Ok(_) => println!("PNG saved to: {}", cli.output),
Err(e) => {
eprintln!("Failed to write PNG file: {}", e);
std::process::exit(1);
}
},
Err(e) => {
eprintln!("Failed to convert SVG to PNG: {}", e);
std::process::exit(1);
}
}
}
_ => {
eprintln!(
"Unsupported format: {}. Supported formats: png, svg",
output_format
);
std::process::exit(1);
}
}
}
Err(e) => {
eprintln!("Failed to parse pie chart: {:?}", e);
std::process::exit(1);
}
}
}
Err(e) => {
eprintln!("Failed to read input file: {}", e);
std::process::exit(1);
}
}
}