proto-file-parser 0.1.1

A Protocol Buffer Definition (.proto) parser that converts proto files to JSON format
Documentation
use proto_file_parser::Proto;
use std::path::PathBuf;
use std::env;

const HELP: &str = r#"
Usage: proto-file-parser <COMMAND> [OPTIONS]

Commands:
  parse <FILE>              Parse a protobuf file and output JSON
    Options:
      -o, --output <FILE>   Output file (optional, defaults to stdout)
      -p, --pretty         Pretty print the JSON output

  help                     Show grammar guide and usage information
  credits                  Show project credits and information

Examples:
  Parse a protobuf file:
    proto-file-parser parse input.proto

  Parse and save as pretty-printed JSON:
    proto-file-parser parse input.proto -p -o output.json

  Show credits:
    proto-file-parser credits

  Show help:
    proto-file-parser help
"#;

const CREDITS: &str = r#"
Proto File Parser v0.1.1

Author:
  Illia Antypenko <i.antypenko@ukma.edu.ua>

Repository:
  https://github.com/snakeaid/proto-file-parser

Built with:
  - Rust Programming Language
  - pest (Parser Generator)
  - serde (Serialization)

License: MIT
"#;

#[derive(Debug)]
enum Command {
    Parse {
        file: PathBuf,
        output: Option<PathBuf>,
        pretty: bool,
    },
    Help,
    Credits,
}

fn parse_args() -> Result<Command, String> {
    let args: Vec<String> = env::args().skip(1).collect();

    if args.is_empty() {
        return Err("No command provided. Use 'help' for usage information.".to_string());
    }

    match args[0].as_str() {
        "help" => Ok(Command::Help),
        "credits" => Ok(Command::Credits),
        "parse" => {
            if args.len() < 2 {
                return Err("No input file provided for parse command.".to_string());
            }
            let mut output = None;
            let mut pretty = false;
            let mut i = 2;
            while i < args.len() {
                match args[i].as_str() {
                    "-o" | "--output" => {
                        if i + 1 >= args.len() {
                            return Err("No output file provided after -o/--output".to_string());
                        }
                        output = Some(PathBuf::from(&args[i + 1]));
                        i += 2;
                    }
                    "-p" | "--pretty" => {
                        pretty = true;
                        i += 1;
                    }
                    _ => {
                        return Err(format!("Unknown option: {}", args[i]));
                    }
                }
            }
            Ok(Command::Parse {
                file: PathBuf::from(&args[1]),
                output,
                pretty,
            })
        }
        cmd => Err(format!("Unknown command: {}. Use 'help' for usage information.", cmd)),
    }
}

fn show_help() {
    println!("{}", HELP);
}

fn show_credits() {
    println!("{}", CREDITS);
}

fn main() {
    let command = match parse_args() {
        Ok(cmd) => cmd,
        Err(e) => {
            eprintln!("Error: {}", e);
            std::process::exit(1);
        }
    };

    match command {
        Command::Parse { file, output, pretty } => {
            match Proto::parse_file(file.to_str().unwrap()) {
                Ok(json) => {
                    let result = if pretty {
                        match serde_json::from_str::<serde_json::Value>(&json) {
                            Ok(value) => serde_json::to_string_pretty(&value).unwrap(),
                            Err(_) => json,
                        }
                    } else {
                        json
                    };

                    match output {
                        Some(path) => {
                            if let Err(e) = std::fs::write(path, result) {
                                eprintln!("Error writing to file: {}", e);
                                std::process::exit(1);
                            }
                        }
                        None => println!("{}", result),
                    }
                }
                Err(e) => {
                    eprintln!("Error parsing file: {}", e);
                    std::process::exit(1);
                }
            }
        }

        Command::Help => {
            show_help();
        }

        Command::Credits => {
            show_credits();
        }
    }
}