1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
//! Parsing of protobuf source files.
//!
//! See the documentation for [`parse()`] for details.
#![warn(missing_debug_implementations, missing_docs)]
#![deny(unsafe_code)]
#![doc(html_root_url = "https://docs.rs/protox-parse/0.4.1/")]
use logos::Span;
use prost_types::FileDescriptorProto;
pub use self::error::ParseError;
mod ast;
mod case;
mod error;
mod generate;
mod lex;
mod parse;
mod tag;
#[cfg(test)]
mod tests;
const MAX_MESSAGE_FIELD_NUMBER: i32 = 536_870_911;
/// Parses a single protobuf source file into a [`FileDescriptorProto`](prost_types::FileDescriptorProto).
///
/// This function only looks at the syntax of the file, without resolving type names or reading
/// imported files.
///
/// # Examples
///
/// ```
/// # use protox_parse::parse;
/// # use prost_types::{DescriptorProto, FieldDescriptorProto, FileDescriptorProto, SourceCodeInfo, source_code_info::Location, field_descriptor_proto::Label};
/// #
/// let source = r#"
/// syntax = "proto3";
/// import "dep.proto";
///
/// message Foo {
/// Bar bar = 1;
/// }
/// "#;
/// let file_descriptor = parse("foo.proto", source).unwrap();
/// assert_eq!(file_descriptor, FileDescriptorProto {
/// name: Some("foo.proto".to_owned()),
/// syntax: Some("proto3".to_owned()),
/// dependency: vec!["dep.proto".to_owned()],
/// message_type: vec![DescriptorProto {
/// name: Some("Foo".to_owned()),
/// field: vec![FieldDescriptorProto {
/// label: Some(Label::Optional as _),
/// name: Some("bar".to_owned()),
/// number: Some(1),
/// type_name: Some("Bar".to_owned()),
/// ..Default::default()
/// }],
/// ..Default::default()
/// }],
/// source_code_info: Some(SourceCodeInfo {
/// /* ... */
/// # location: vec![
/// # Location { path: vec![], span: vec![1, 4, 6, 5], ..Default::default() },
/// # Location { path: vec![3, 0], span: vec![2, 4, 23], ..Default::default() },
/// # Location { path: vec![4, 0], span: vec![4, 4, 6, 5], ..Default::default() },
/// # Location { path: vec![4, 0, 1], span: vec![4, 12, 15], ..Default::default() },
/// # Location { path: vec![4, 0, 2, 0], span: vec![5, 8, 20], ..Default::default() },
/// # Location { path: vec![4, 0, 2, 0, 1], span: vec![5, 12, 15], ..Default::default() },
/// # Location { path: vec![4, 0, 2, 0, 3], span: vec![5, 18, 19], ..Default::default() },
/// # Location { path: vec![4, 0, 2, 0, 6], span: vec![5, 8, 11], ..Default::default() },
/// # Location { path: vec![12], span: vec![1, 4, 22], ..Default::default() },
/// # ],
/// }),
/// ..Default::default()
/// })
/// ```
pub fn parse(name: &str, source: &str) -> Result<FileDescriptorProto, ParseError> {
if source.len() > MAX_FILE_LEN {
return Err(ParseError::new(
vec![error::ParseErrorKind::FileTooLarge],
name,
String::default(),
));
}
let ast = parse::parse_file(source)
.map_err(|errors| ParseError::new(errors, name, source.to_owned()))?;
generate::generate_file(ast, name, source)
.map_err(|errors| ParseError::new(errors, name, source.to_owned()))
}
const MAX_FILE_LEN: usize = i32::MAX as usize;
fn index_to_i32(index: usize) -> i32 {
// We enforce that all files parsed are at most i32::MAX bytes long. Therefore the indices of any
// definitions in a single file must fit into an i32.
index.try_into().unwrap()
}
fn join_span(start: Span, end: Span) -> Span {
start.start..end.end
}