diff options
Diffstat (limited to 'src/text_format/mod.rs')
-rw-r--r-- | src/text_format/mod.rs | 300 |
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; |