#![deny(rustdoc::broken_intra_doc_links, rustdoc::bare_urls, rust_2018_idioms)]
#![warn(
missing_debug_implementations,
clippy::explicit_iter_loop,
clippy::use_self,
clippy::clone_on_ref_ptr,
clippy::future_not_send
)]
use prost_types::FileDescriptorProto;
use std::io::{BufWriter, Error, Result, Write};
use std::path::PathBuf;
use crate::descriptor::{Descriptor, Package};
use crate::message::resolve_message;
use crate::{
generator::{generate_enum, generate_message},
resolver::Resolver,
};
mod descriptor;
mod escape;
mod generator;
mod message;
mod resolver;
#[derive(Debug, Default)]
pub struct Builder {
descriptors: descriptor::DescriptorSet,
exclude: Vec<String>,
out_dir: Option<PathBuf>,
extern_paths: Vec<(String, String)>,
retain_enum_prefix: bool,
ignore_unknown_fields: bool,
btree_map_paths: Vec<String>,
emit_fields: bool,
use_integers_for_enums: bool,
ignore_unknown_enum_variants: bool,
preserve_proto_field_names: bool,
}
impl Builder {
pub fn new() -> Self {
Self::default()
}
pub fn out_dir<P>(&mut self, path: P) -> &mut Self
where
P: Into<PathBuf>,
{
self.out_dir = Some(path.into());
self
}
pub fn register_descriptors(&mut self, descriptors: &[u8]) -> Result<&mut Self> {
self.descriptors.register_encoded(descriptors)?;
Ok(self)
}
pub fn register_file_descriptor(&mut self, file: FileDescriptorProto) -> &mut Self {
self.descriptors.register_file_descriptor(file);
self
}
pub fn exclude<S: Into<String>, I: IntoIterator<Item = S>>(
&mut self,
prefixes: I,
) -> &mut Self {
self.exclude.extend(prefixes.into_iter().map(Into::into));
self
}
pub fn retain_enum_prefix(&mut self) -> &mut Self {
self.retain_enum_prefix = true;
self
}
pub fn extern_path(
&mut self,
proto_path: impl Into<String>,
rust_path: impl Into<String>,
) -> &mut Self {
self.extern_paths
.push((proto_path.into(), rust_path.into()));
self
}
pub fn ignore_unknown_fields(&mut self) -> &mut Self {
self.ignore_unknown_fields = true;
self
}
pub fn btree_map<S: Into<String>, I: IntoIterator<Item = S>>(&mut self, paths: I) -> &mut Self {
self.btree_map_paths
.extend(paths.into_iter().map(Into::into));
self
}
pub fn emit_fields(&mut self) -> &mut Self {
self.emit_fields = true;
self
}
pub fn use_integers_for_enums(&mut self) -> &mut Self {
self.use_integers_for_enums = true;
self
}
pub fn ignore_unknown_enum_variants(&mut self) -> &mut Self {
self.ignore_unknown_enum_variants = true;
self
}
pub fn preserve_proto_field_names(&mut self) -> &mut Self {
self.preserve_proto_field_names = true;
self
}
pub fn build<S: AsRef<str>>(&mut self, prefixes: &[S]) -> Result<()> {
let mut output: PathBuf = self.out_dir.clone().map(Ok).unwrap_or_else(|| {
std::env::var_os("OUT_DIR")
.ok_or_else(|| Error::other("OUT_DIR environment variable is not set"))
.map(Into::into)
})?;
output.push("FILENAME");
let write_factory = move |package: &Package| {
output.set_file_name(format!("{}.serde.rs", package));
let file = std::fs::OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(&output)?;
Ok(BufWriter::new(file))
};
let writers = self.generate(prefixes, write_factory)?;
for (_, mut writer) in writers {
writer.flush()?;
}
Ok(())
}
pub fn generate<S: AsRef<str>, W: Write, F: FnMut(&Package) -> Result<W>>(
&self,
prefixes: &[S],
mut write_factory: F,
) -> Result<Vec<(Package, W)>> {
let iter = self.descriptors.iter().filter(move |(t, _)| {
let exclude = self
.exclude
.iter()
.any(|prefix| t.prefix_match(prefix.as_ref()).is_some());
let include = prefixes
.iter()
.any(|prefix| t.prefix_match(prefix.as_ref()).is_some());
include && !exclude
});
let mut ret: Vec<(Package, W)> = Vec::new();
for (type_path, descriptor) in iter {
let writer = match ret.last_mut() {
Some((package, writer)) if package == type_path.package() => writer,
_ => {
let package = type_path.package();
ret.push((package.clone(), write_factory(package)?));
&mut ret.last_mut().unwrap().1
}
};
let resolver = Resolver::new(
&self.extern_paths,
type_path.package(),
self.retain_enum_prefix,
);
match descriptor {
Descriptor::Enum(descriptor) => generate_enum(
&resolver,
type_path,
descriptor,
writer,
self.use_integers_for_enums,
self.ignore_unknown_enum_variants,
)?,
Descriptor::Message(descriptor) => {
if let Some(message) = resolve_message(&self.descriptors, descriptor) {
generate_message(
&resolver,
&message,
writer,
self.ignore_unknown_fields,
&self.btree_map_paths,
self.emit_fields,
self.preserve_proto_field_names,
)?
}
}
}
}
Ok(ret)
}
}