Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@

### Added

- New `encode_cache` option to enable caching of message field lengths during encoding
- Add `encode_cache` option to enable caching of message field lengths during encoding
- Add `compile_protos_with_config_files` to compile proto files with corresponding config files
- Add new unified error type `micropb_gen::Error`

### Changed

- Top-level APIs now return `micropb_gen::Error` instead of `io::ErrorNew`

## 0.5.1

Expand Down
131 changes: 131 additions & 0 deletions micropb-gen/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
use std::{fmt::Display, io};

/// Error encountered while processing proto files and generating Rust code
#[derive(Debug)]
pub enum Error {
/// IO error
Io(io::Error),
/// Protoc invocation error
Protoc(String),
/// Protobuf field error
Field {
/// Protobuf package
package: String,
/// Protobuf message
message: String,
/// Protobuf field
field: String,
/// Error text
text: String,
},
/// Protobuf message error
Message {
/// Protobuf package
package: String,
/// Protobuf message
message: String,
/// Error text
text: String,
},
/// Protobuf package error
Package {
/// Protobuf package
package: String,
/// Error text
text: String,
},
#[cfg(feature = "config-file")]
/// Config file parsing error
ConfigFile {
/// File name
file_name: std::path::PathBuf,
/// TOML parsing error
err: toml::de::Error,
},
}

/// Result alias
pub type Result<T> = std::result::Result<T, Error>;

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Io(error) => error.fmt(f),
Error::Protoc(error) => write!(f, "protoc failed: {error}"),
Error::Field {
package,
message,
field,
text,
} => {
let dot = if package.is_empty() { "" } else { "." };
write!(f, "({dot}{package}.{message}.{field}) {text}")
}
Error::Message {
package,
message,
text,
} => {
let dot = if package.is_empty() { "" } else { "." };
write!(f, "({dot}{package}.{message}) {text}")
}
Error::Package { package, text } => {
let dot = if package.is_empty() { "" } else { "." };
write!(f, "({dot}{package}) {text}")
}
#[cfg(feature = "config-file")]
Error::ConfigFile { file_name, err } => {
write!(f, "Failed to parse {}: {err}", file_name.display())
}
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(error) => Some(error),
#[cfg(feature = "config-file")]
Error::ConfigFile { err, .. } => Some(err),
Error::Field { .. }
| Error::Message { .. }
| Error::Package { .. }
| Error::Protoc(_) => None,
}
}
}

impl From<io::Error> for Error {
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}

pub(crate) fn field_error(
pkg: &str,
msg_name: &str,
field_name: &str,
err_text: impl Display,
) -> Error {
Error::Field {
package: pkg.to_owned(),
message: msg_name.to_owned(),
field: field_name.to_owned(),
text: err_text.to_string(),
}
}

pub(crate) fn msg_error(pkg: &str, msg_name: &str, err_text: impl Display) -> Error {
Error::Message {
package: pkg.to_owned(),
message: msg_name.to_owned(),
text: err_text.to_string(),
}
}

pub(crate) fn pkg_error(pkg: &str, err_text: impl Display) -> Error {
Error::Package {
package: pkg.to_owned(),
text: err_text.to_string(),
}
}
52 changes: 24 additions & 28 deletions micropb-gen/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{
cell::RefCell,
collections::BTreeMap,
fmt::Display,
io,
};

use convert_case::{Case, Casing};
Expand All @@ -16,6 +15,7 @@ use crate::{
EncodeDecode, Generator, WarningCb,
config::Config,
descriptor::{DescriptorProto, EnumDescriptorProto, FileDescriptorProto, FileDescriptorSet},
error::pkg_error,
generator::{r#enum::Enum, graph::TypeGraph},
pathtree::{Node, PathTree},
split_pkg_name,
Expand Down Expand Up @@ -91,15 +91,6 @@ fn field_error_str(pkg: &str, msg_name: &str, field_name: &str, err_text: impl D
format!("({dot}{pkg}.{msg_name}.{field_name}) {err_text}")
}

fn field_error(pkg: &str, msg_name: &str, field_name: &str, err_text: impl Display) -> io::Error {
io::Error::other(field_error_str(pkg, msg_name, field_name, err_text))
}

fn msg_error(pkg: &str, msg_name: &str, err_text: impl Display) -> io::Error {
let dot = if pkg.is_empty() { "" } else { "." };
io::Error::other(format!("({dot}{pkg}.{msg_name}) {err_text}"))
}

pub(crate) enum EncodeFunc {
Sizeof(Ident),
Encode(Ident),
Expand Down Expand Up @@ -173,7 +164,7 @@ impl<'proto> Context<'proto> {
pub(crate) fn generate_fdset(
generator: Generator,
fdset: &'proto FileDescriptorSet,
) -> io::Result<TokenStream> {
) -> crate::Result<TokenStream> {
// Pre-generate the comment trees for every file
let mut comment_trees = vec![];
for file in &fdset.file {
Expand Down Expand Up @@ -222,23 +213,24 @@ impl<'proto> Context<'proto> {
Ok(module)
}

fn setup_file_context(&mut self, fdproto: &FileDescriptorProto) -> io::Result<()> {
self.syntax = match fdproto.syntax.as_str() {
"proto3" => Syntax::Proto3,
"proto2" | "" => Syntax::Proto2,
"editions" => return Err(io::Error::other("Protobuf Editions not supported")),
syntax => {
return Err(io::Error::other(format!(
"Unexpected Protobuf syntax specifier {syntax}"
)));
}
};
fn setup_file_context(&mut self, fdproto: &FileDescriptorProto) -> crate::Result<()> {
self.pkg_path = fdproto
.package()
.map(|s| split_pkg_name(s).map(ToOwned::to_owned).collect())
.unwrap_or_default();
self.pkg = fdproto.package().cloned().unwrap_or_default();
self.type_path = RefCell::new(vec![]);
self.syntax = match fdproto.syntax.as_str() {
"proto3" => Syntax::Proto3,
"proto2" | "" => Syntax::Proto2,
"editions" => return Err(pkg_error(&self.pkg, "Protobuf Editions not supported")),
syntax => {
return Err(pkg_error(
&self.pkg,
format!("Unexpected Protobuf syntax specifier {syntax}"),
));
}
};
Ok(())
}

Expand All @@ -247,7 +239,7 @@ impl<'proto> Context<'proto> {
config_tree: &PathTree<Box<Config>>,
comment_tree: &'proto PathTree<Comments, (i32, i32)>,
fdproto: &'proto FileDescriptorProto,
) -> io::Result<()> {
) -> crate::Result<()> {
self.setup_file_context(fdproto)?;

let root_node = &config_tree.root;
Expand Down Expand Up @@ -283,7 +275,7 @@ impl<'proto> Context<'proto> {
Ok(())
}

fn generate_fdproto(&mut self, fdproto: &FileDescriptorProto) -> io::Result<TokenStream> {
fn generate_fdproto(&mut self, fdproto: &FileDescriptorProto) -> crate::Result<TokenStream> {
self.setup_file_context(fdproto)?;

// Generate Rust code from message and enum types
Expand Down Expand Up @@ -320,7 +312,11 @@ impl<'proto> Context<'proto> {
e.generate_decl()
}

fn generate_msg_mod(&self, msg: &Message, proto: &DescriptorProto) -> io::Result<TokenStream> {
fn generate_msg_mod(
&self,
msg: &Message,
proto: &DescriptorProto,
) -> crate::Result<TokenStream> {
let msg_mod_name = resolve_path_elem(msg.name, self.params.suffixed_package_names);

self.type_path.borrow_mut().push(msg.name.to_owned());
Expand Down Expand Up @@ -371,7 +367,7 @@ impl<'proto> Context<'proto> {
proto: &'proto DescriptorProto,
msg_conf: CurrentConfig,
comment_node: Option<&'proto CommentNode>,
) -> io::Result<()> {
) -> crate::Result<()> {
let fq_name = self.fq_proto_name(&proto.name);
if self.params.extern_paths.contains_key(&fq_name) {
return Ok(());
Expand Down Expand Up @@ -412,7 +408,7 @@ impl<'proto> Context<'proto> {
proto: &'proto EnumDescriptorProto,
enum_conf: CurrentConfig,
comment_node: Option<&'proto CommentNode>,
) -> io::Result<()> {
) -> crate::Result<()> {
let fq_name = self.fq_proto_name(&proto.name);
if self.params.extern_paths.contains_key(&fq_name) {
return Ok(());
Expand All @@ -428,7 +424,7 @@ impl<'proto> Context<'proto> {
&self,
msg: Option<&Message>,
proto: &DescriptorProto,
) -> io::Result<TokenStream> {
) -> crate::Result<TokenStream> {
// None means message has been skipped
let Some(msg) = msg else { return Ok(quote! {}) };

Expand Down
7 changes: 3 additions & 4 deletions micropb-gen/src/generator/enum.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::io;

use micropb::size::sizeof_varint32;
use proc_macro2::{Literal, TokenStream};
use quote::quote;
Expand All @@ -9,7 +7,8 @@ use super::location::{CommentNode, Comments, get_comments, next_comment_node};
use crate::{
config::IntSize,
descriptor::EnumDescriptorProto,
generator::{Context, CurrentConfig, derive_enum_attr, location, msg_error, sanitized_ident},
error::msg_error,
generator::{Context, CurrentConfig, derive_enum_attr, location, sanitized_ident},
};

pub(crate) struct Variant<'proto> {
Expand All @@ -34,7 +33,7 @@ impl<'proto> Enum<'proto> {
ctx: &Context<'proto>,
enum_conf: &CurrentConfig,
comment_node: Option<&'proto CommentNode>,
) -> io::Result<Option<Self>> {
) -> crate::Result<Option<Self>> {
if enum_conf.config.skip.unwrap_or(false) {
return Ok(None);
}
Expand Down
20 changes: 11 additions & 9 deletions micropb-gen/src/generator/message.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use std::{collections::HashMap, io};
use std::collections::HashMap;

use proc_macro2::{Literal, Span, TokenStream};
use quote::{format_ident, quote};
use syn::Ident;

use crate::{
descriptor::DescriptorProto,
error::{field_error, msg_error},
generator::{
Context, EncodeFunc,
field::{CustomField, FieldType},
Expand All @@ -21,9 +22,7 @@ use crate::{
use super::{
CurrentConfig, derive_msg_attr,
field::Field,
field_error,
location::{CommentNode, Comments},
msg_error,
oneof::{Oneof, OneofField, OneofType},
sanitized_ident,
};
Expand Down Expand Up @@ -72,7 +71,7 @@ impl<'proto> Message<'proto> {
ctx: &Context<'proto>,
msg_conf: &CurrentConfig,
comment_node: Option<&'proto CommentNode>,
) -> io::Result<Option<Self>> {
) -> crate::Result<Option<Self>> {
if msg_conf.config.skip.unwrap_or(false) {
return Ok(None);
}
Expand Down Expand Up @@ -352,7 +351,7 @@ impl<'proto> Message<'proto> {
&self,
ctx: &Context<'proto>,
proto_default: bool,
) -> io::Result<TokenStream> {
) -> crate::Result<TokenStream> {
let msg_mod_name = resolve_path_elem(self.name, true);
let rust_name = &self.rust_name;
let lifetime = &self.lifetime;
Expand Down Expand Up @@ -431,7 +430,10 @@ impl<'proto> Message<'proto> {
}
}

pub(crate) fn generate_default_impl(&self, ctx: &Context<'proto>) -> io::Result<TokenStream> {
pub(crate) fn generate_default_impl(
&self,
ctx: &Context<'proto>,
) -> crate::Result<TokenStream> {
if !self.impl_default {
return Ok(quote! {});
}
Expand Down Expand Up @@ -538,7 +540,7 @@ impl<'proto> Message<'proto> {
}
}

pub(crate) fn generate_impl(&self, ctx: &Context<'proto>) -> Result<TokenStream, io::Error> {
pub(crate) fn generate_impl(&self, ctx: &Context<'proto>) -> crate::Result<TokenStream> {
if self.as_oneof_enum {
return Ok(quote! {});
}
Expand All @@ -563,7 +565,7 @@ impl<'proto> Message<'proto> {
pub(crate) fn generate_decode_trait(
&self,
ctx: &Context<'proto>,
) -> Result<TokenStream, io::Error> {
) -> crate::Result<TokenStream> {
let name = &self.rust_name;
let lifetime = &self.lifetime;
let tag = Ident::new("tag", Span::call_site());
Expand Down Expand Up @@ -640,7 +642,7 @@ impl<'proto> Message<'proto> {
Ok(tok)
}

pub(crate) fn generate_cache_decl(&self, ctx: &Context<'proto>) -> io::Result<TokenStream> {
pub(crate) fn generate_cache_decl(&self, ctx: &Context<'proto>) -> crate::Result<TokenStream> {
let fields = self
.fields
.iter()
Expand Down
Loading