aboutsummaryrefslogtreecommitdiff
path: root/src/text_format/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/text_format/mod.rs')
-rw-r--r--src/text_format/mod.rs300
1 files changed, 15 insertions, 285 deletions
diff --git a/src/text_format/mod.rs b/src/text_format/mod.rs
index 2fa9a16..4ae5372 100644
--- a/src/text_format/mod.rs
+++ b/src/text_format/mod.rs
@@ -1,4 +1,4 @@
-//! Protobuf "text format" implementation.
+//! # Protobuf "text format" implementation.
//!
//! Text format message look like this:
//!
@@ -18,290 +18,20 @@
//! This format is not specified, but it is implemented by all official
//! protobuf implementations, including `protoc` command which can decode
//! and encode messages using text format.
+//!
+//! # JSON
+//!
+//! rust-protobuf also supports JSON printing and parsing.
+//! It is implemented in
+//! [`protobuf-json-mapping` crate](https://docs.rs/protobuf-json-mapping/%3E=3.0.0-alpha).
-use std;
-use std::fmt;
-use std::fmt::Write;
-
-use crate::message::Message;
-use crate::reflect::ReflectFieldRef;
-use crate::reflect::ReflectValueRef;
-
+mod parse;
mod print;
-// Used by text format parser and by pure-rust codegen parsed
-// this it is public but hidden module.
-// https://github.com/rust-lang/rust/issues/44663
-#[doc(hidden)]
-pub mod lexer;
-
-use self::print::print_str_to;
-#[doc(hidden)]
-pub use self::print::quote_bytes_to;
-#[doc(hidden)]
-pub use self::print::quote_escape_bytes;
-use crate::text_format::print::quote_escape_bytes_to;
-
-#[doc(hidden)]
-pub fn unescape_string(string: &str) -> Vec<u8> {
- fn parse_if_digit(chars: &mut std::str::Chars) -> u8 {
- let mut copy = chars.clone();
- let f = match copy.next() {
- None => return 0,
- Some(f) => f,
- };
- let d = match f {
- '0'..='9' => (f as u8 - b'0'),
- _ => return 0,
- };
- *chars = copy;
- d
- }
-
- fn parse_hex_digit(chars: &mut std::str::Chars) -> u8 {
- match chars.next().unwrap() {
- c @ '0'..='9' => (c as u8) - b'0',
- c @ 'a'..='f' => (c as u8) - b'a' + 10,
- c @ 'A'..='F' => (c as u8) - b'A' + 10,
- _ => panic!("incorrect hex escape"),
- }
- }
-
- fn parse_escape_rem(chars: &mut std::str::Chars) -> u8 {
- let n = chars.next().unwrap();
- match n {
- 'a' => return b'\x07',
- 'b' => return b'\x08',
- 'f' => return b'\x0c',
- 'n' => return b'\n',
- 'r' => return b'\r',
- 't' => return b'\t',
- 'v' => return b'\x0b',
- '"' => return b'"',
- '\'' => return b'\'',
- '0'..='9' => {
- let d1 = n as u8 - b'0';
- let d2 = parse_if_digit(chars);
- let d3 = parse_if_digit(chars);
- return (d1 * 64 + d2 * 8 + d3) as u8;
- }
- 'x' => {
- let d1 = parse_hex_digit(chars);
- let d2 = parse_hex_digit(chars);
- return d1 * 16 + d2;
- }
- c => return c as u8, // TODO: validate ASCII
- };
- }
-
- let mut chars = string.chars();
- let mut r = Vec::new();
-
- loop {
- let f = match chars.next() {
- None => return r,
- Some(f) => f,
- };
-
- if f == '\\' {
- r.push(parse_escape_rem(&mut chars));
- } else {
- r.push(f as u8); // TODO: escape UTF-8
- }
- }
-}
-
-fn do_indent(buf: &mut String, pretty: bool, indent: usize) {
- if pretty && indent > 0 {
- for _ in 0..indent {
- buf.push_str(" ");
- }
- }
-}
-
-fn print_start_field(
- buf: &mut String,
- pretty: bool,
- indent: usize,
- first: &mut bool,
- field_name: &str,
-) {
- if !*first && !pretty {
- buf.push_str(" ");
- }
- do_indent(buf, pretty, indent);
- *first = false;
- buf.push_str(field_name);
-}
-
-fn print_end_field(buf: &mut String, pretty: bool) {
- if pretty {
- buf.push_str("\n");
- }
-}
-
-fn print_field(
- buf: &mut String,
- pretty: bool,
- indent: usize,
- first: &mut bool,
- field_name: &str,
- value: ReflectValueRef,
-) {
- print_start_field(buf, pretty, indent, first, field_name);
-
- match value {
- ReflectValueRef::Message(m) => {
- buf.push_str(" {");
- if pretty {
- buf.push_str("\n");
- }
- print_to_internal(m, buf, pretty, indent + 1);
- do_indent(buf, pretty, indent);
- buf.push_str("}");
- }
- ReflectValueRef::Enum(e) => {
- buf.push_str(": ");
- buf.push_str(e.name());
- }
- ReflectValueRef::String(s) => {
- buf.push_str(": ");
- print_str_to(s, buf);
- }
- ReflectValueRef::Bytes(b) => {
- buf.push_str(": ");
- quote_escape_bytes_to(b, buf);
- }
- ReflectValueRef::I32(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::I64(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::U32(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::U64(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::Bool(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::F32(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- ReflectValueRef::F64(v) => {
- write!(buf, ": {}", v).unwrap();
- }
- }
-
- print_end_field(buf, pretty);
-}
-
-fn print_to_internal(m: &dyn Message, buf: &mut String, pretty: bool, indent: usize) {
- let d = m.descriptor();
- let mut first = true;
- for f in d.fields() {
- match f.get_reflect(m) {
- ReflectFieldRef::Map(map) => {
- for (k, v) in map {
- print_start_field(buf, pretty, indent, &mut first, f.name());
- buf.push_str(" {");
- if pretty {
- buf.push_str("\n");
- }
-
- let mut entry_first = true;
-
- print_field(buf, pretty, indent + 1, &mut entry_first, "key", k.as_ref());
- print_field(
- buf,
- pretty,
- indent + 1,
- &mut entry_first,
- "value",
- v.as_ref(),
- );
- do_indent(buf, pretty, indent);
- buf.push_str("}");
- print_end_field(buf, pretty);
- }
- }
- ReflectFieldRef::Repeated(repeated) => {
- // TODO: do not print zeros for v3
- for v in repeated {
- print_field(buf, pretty, indent, &mut first, f.name(), v.as_ref());
- }
- }
- ReflectFieldRef::Optional(optional) => {
- if let Some(v) = optional {
- print_field(buf, pretty, indent, &mut first, f.name(), v);
- }
- }
- }
- }
-
- // TODO: unknown fields
-}
-
-/// Text-format
-pub fn print_to(m: &dyn Message, buf: &mut String) {
- print_to_internal(m, buf, false, 0)
-}
-
-fn print_to_string_internal(m: &dyn Message, pretty: bool) -> String {
- let mut r = String::new();
- print_to_internal(m, &mut r, pretty, 0);
- r.to_string()
-}
-
-/// Text-format
-pub fn print_to_string(m: &dyn Message) -> String {
- print_to_string_internal(m, false)
-}
-
-/// Text-format to `fmt::Formatter`.
-pub fn fmt(m: &dyn Message, f: &mut fmt::Formatter) -> fmt::Result {
- let pretty = f.alternate();
- f.write_str(&print_to_string_internal(m, pretty))
-}
-
-#[cfg(test)]
-mod test {
-
- fn escape(data: &[u8]) -> String {
- let mut s = String::with_capacity(data.len() * 4);
- super::quote_bytes_to(data, &mut s);
- s
- }
-
- fn test_escape_unescape(text: &str, escaped: &str) {
- assert_eq!(text.as_bytes(), &super::unescape_string(escaped)[..]);
- assert_eq!(escaped, &escape(text.as_bytes())[..]);
- }
-
- #[test]
- fn test_print_to_bytes() {
- assert_eq!("ab", escape(b"ab"));
- assert_eq!("a\\\\023", escape(b"a\\023"));
- assert_eq!("a\\r\\n\\t \\'\\\"\\\\", escape(b"a\r\n\t '\"\\"));
- assert_eq!("\\344\\275\\240\\345\\245\\275", escape("你好".as_bytes()));
- }
-
- #[test]
- fn test_unescape_string() {
- test_escape_unescape("", "");
- test_escape_unescape("aa", "aa");
- test_escape_unescape("\n", "\\n");
- test_escape_unescape("\r", "\\r");
- test_escape_unescape("\t", "\\t");
- test_escape_unescape("你好", "\\344\\275\\240\\345\\245\\275");
- // hex
- assert_eq!(b"aaa\x01bbb", &super::unescape_string("aaa\\x01bbb")[..]);
- assert_eq!(b"aaa\xcdbbb", &super::unescape_string("aaa\\xCDbbb")[..]);
- assert_eq!(b"aaa\xcdbbb", &super::unescape_string("aaa\\xCDbbb")[..]);
- // quotes
- assert_eq!(b"aaa\"bbb", &super::unescape_string("aaa\\\"bbb")[..]);
- assert_eq!(b"aaa\'bbb", &super::unescape_string("aaa\\\'bbb")[..]);
- }
-}
+pub use self::parse::merge_from_str;
+pub use self::parse::parse_from_str;
+pub use self::parse::ParseError;
+pub use self::print::fmt;
+pub use self::print::print_to;
+pub use self::print::print_to_string;
+pub use self::print::print_to_string_pretty;