#![forbid(unsafe_code)]
use clap::Args;
use std::io::Read;
use std::path::PathBuf;
use crate::util::Verbosity;
use oxiproto_reflect::{DescriptorPool, DynamicMessage};
use prost::Message as _;
use prost_reflect::ReflectMessage;
#[derive(Args)]
pub struct ConvertArgs {
#[arg(required = true)]
pub protos: Vec<PathBuf>,
#[arg(short = 't', long)]
pub message_type: String,
#[arg(short = 'i', long)]
pub input: Option<PathBuf>,
#[arg(short = 'o', long)]
pub output: Option<PathBuf>,
#[arg(short = 'I', long)]
pub include: Vec<PathBuf>,
}
fn build_pool(
protos: &[PathBuf],
includes: &[PathBuf],
) -> Result<DescriptorPool, Box<dyn std::error::Error>> {
for proto in protos {
if !proto.exists() {
return Err(format!("proto file not found: {}", proto.display()).into());
}
}
let fds = oxiproto_build::compile_to_fds(protos, includes)?;
let fds_bytes = fds.encode_to_vec();
let pool = oxiproto_reflect::pool_from_fds_bytes(&fds_bytes)?;
Ok(pool)
}
fn read_input(input: &Option<PathBuf>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
match input {
Some(path) => Ok(std::fs::read(path)?),
None => {
let mut buf = Vec::new();
std::io::stdin().read_to_end(&mut buf)?;
Ok(buf)
}
}
}
fn write_output(output: &Option<PathBuf>, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
match output {
Some(path) => {
std::fs::write(path, data)?;
Ok(())
}
None => {
use std::io::Write;
std::io::stdout().write_all(data)?;
Ok(())
}
}
}
pub fn run_encode(
args: ConvertArgs,
verbosity: Verbosity,
) -> Result<(), Box<dyn std::error::Error>> {
let _ = verbosity; let pool = build_pool(&args.protos, &args.include)?;
let descriptor = pool
.get_message_by_name(&args.message_type)
.ok_or_else(|| format!("message type '{}' not found", args.message_type))?;
let input_bytes = read_input(&args.input)?;
let json_value: serde_json::Value = serde_json::from_slice(&input_bytes)?;
let codec = oxiproto_json::JsonCodec::default();
let msg = oxiproto_json::from_json(&json_value, &descriptor, &codec)?;
let encoded = msg.encode_to_vec();
write_output(&args.output, &encoded)?;
Ok(())
}
pub fn run_decode(
args: ConvertArgs,
verbosity: Verbosity,
) -> Result<(), Box<dyn std::error::Error>> {
let _ = verbosity; let pool = build_pool(&args.protos, &args.include)?;
let descriptor = pool
.get_message_by_name(&args.message_type)
.ok_or_else(|| format!("message type '{}' not found", args.message_type))?;
let input_bytes = read_input(&args.input)?;
let msg = DynamicMessage::decode(descriptor.clone(), input_bytes.as_slice())?;
let _ = msg.descriptor();
let codec = oxiproto_json::JsonCodec::default();
let json_value = oxiproto_json::to_json(&msg, &codec);
let json_string = serde_json::to_string_pretty(&json_value)?;
let mut output = json_string.into_bytes();
output.push(b'\n');
write_output(&args.output, &output)?;
Ok(())
}