rfc822-like-0.2.3/.cargo_vcs_info.json0000644000000001360000000000100127660ustar { "git": { "sha1": "5a58927563ab0d9554e3cfe187b85deb71897a03" }, "path_in_vcs": "" }rfc822-like-0.2.3/.github/workflows/rust.yml000064400000000000000000000022611046102023000166740ustar 00000000000000name: Rust on: push: branches: [ master ] pull_request: branches: [ master ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - name: Update Rustup run: rustup self update - uses: actions/checkout@v2 - name: Setup rust-toolchain # This looks retarded but it's correct # see https://github.com/rust-lang/rustup/issues/2070#issuecomment-545096849 run: rustup show - name: Pin thiserror run: cargo update -p thiserror --precise 1.0.25 - name: Pin serde run: cargo update -p serde --precise 1.0.126 - name: Pin serde_derive run: cargo update -p serde_derive --precise 1.0.136 - name: Pin quote run: cargo update -p quote --precise 1.0.9 - name: Pin syn run: cargo update -p syn --precise 1.0.72 - name: Pin proc-macro2 run: cargo update -p proc-macro2 --precise 1.0.27 - name: Pin unicode-segmentation run: cargo update -p unicode-segmentation --precise 1.7.1 - name: Build run: cargo build --verbose - name: Run unit tests run: cargo +stable test --verbose - name: Clippy run: cargo +stable clippy -- -D clippy::all rfc822-like-0.2.3/.gitignore000064400000000000000000000000231046102023000135410ustar 00000000000000/target Cargo.lock rfc822-like-0.2.3/Cargo.toml0000644000000023450000000000100107700ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "rfc822-like" version = "0.2.3" authors = ["Martin Habovstiak "] description = "RFC822-like encoding used in control files implemented for serde" homepage = "https://github.com/Kixunil/rfc822-like" readme = "README.md" keywords = [ "rfc822", "debian", "serde", ] categories = [ "parser-implementations", "encoding", ] license = "MITNFA" repository = "https://github.com/Kixunil/rfc822-like" [dependencies.fmt2io] version = "1.0.0" [dependencies.serde] version = "1.0.126" [dependencies.thiserror] version = "1.0.25" [dependencies.unicode-segmentation] version = "1.7.1" [dev-dependencies.quickcheck] version = "1.0.3" [dev-dependencies.serde_derive] version = "1.0.126" [features] live_test = [] rfc822-like-0.2.3/Cargo.toml.orig000064400000000000000000000014511046102023000144460ustar 00000000000000[package] name = "rfc822-like" version = "0.2.3" authors = ["Martin Habovstiak "] edition = "2018" license = "MITNFA" description = "RFC822-like encoding used in control files implemented for serde" repository = "https://github.com/Kixunil/rfc822-like" homepage = "https://github.com/Kixunil/rfc822-like" keywords = ["rfc822", "debian", "serde"] readme = "README.md" categories = ["parser-implementations", "encoding"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] # Used for testing against Debian-based OS, do not depend on this! live_test = [] [dependencies] serde = "1.0.126" thiserror = "1.0.25" unicode-segmentation = "1.7.1" fmt2io = "1.0.0" [dev-dependencies] serde_derive = "1.0.126" quickcheck = "1.0.3" rfc822-like-0.2.3/README.md000064400000000000000000000043511046102023000130400ustar 00000000000000# Rust library for handling RFC822-like format used in Debian ## About This crate implements the file format inpired by RFC822 that is used in Debian packages. It is the format of the `debian/control` files in packages and `Packages` file of `apt`. It is called `rfc822-like` instead of just `rfc822` because Debian does not claim to implement RFC822 exactly and this crate is focused on working with Debian tools, not parsing exact RFC822 file format. Frankly, I didn't even bother to read RFC822 itself. If you need to strictly parse RFC822, I suggest you to fork this crate and fix whatever differences there are. I'm not interested in maintaining strict RFC822 crate, so don't send PRs for that but I'm willing to put common pieces into their own crate. If you're interested in this approach feel free to file a PR (or ask beforehand if you have questions). Note that this crate is currently not optimized for performance. There are multiple places where allocation could be avoided and other optimizations may be missing. It's absolutely fine for my own use cases, and probably will be for yours as well. If you need it to be faster or just want to have fun improving its performance I'll be happy to accept PRs. The API is currently not set in stone and may change over time. Basic steps to minimize the impact of changes were taken (e.g. encapsulation of `Error` type). ## Example Check the crate documentation for more examples and detailed explanation. ```rust use rfc822_like::de::Deserializer; use serde::Deserialize; let input = "Package: foo Description: The Foo Package: bar Description: The Bar "; let mut reader = input.as_bytes(); #[derive(Debug, Eq, PartialEq, serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { package: String, description: String, } let expected = vec![ Record { package: "foo".to_owned(), description:"The Foo".to_owned(), }, Record { package: "bar".to_owned(), description: "The Bar".to_owned(), }, ]; let deserialized = >::deserialize(Deserializer::new(&mut reader)).unwrap(); assert_eq!(deserialized, expected); ``` ## MSRV Whatever Rust version is available in the latest Debian stable, currently 1.41.1. ## License MITNFA rfc822-like-0.2.3/clippy.toml000064400000000000000000000000201046102023000137430ustar 00000000000000msrv = "1.41.1" rfc822-like-0.2.3/rust-toolchain000064400000000000000000000001731046102023000144550ustar 00000000000000[toolchain] channel = "1.41.1" components = ["clippy"] targets = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] rfc822-like-0.2.3/src/de/error.rs000064400000000000000000000033161046102023000144370ustar 00000000000000//! Error types related to deserialization of RFC822-like format. use std::fmt; use std::io; #[derive(Debug, thiserror::Error)] pub(crate) enum ErrorInner { #[error("{0}")] Custom(String), #[error("Line {0} doesn't contain a colon")] MissingColon(usize), #[error("I/O error")] IoError(#[from] io::Error), #[error("The deserialized type is ambiguous and must be explicitly specified. (RFC822 is NOT self-describing.)")] AmbiguousType, } impl serde::de::Error for Error { fn custom(msg: T) -> Self { ErrorInner::Custom(msg.to_string()).into() } } /// Error that can happen during deserialization. /// /// The error is currently encapsulated and not exposed because it's not yet certain what kind of /// information we will store in the error type. /// However, it does implement standard error-related traits and has a human-friendly `Display` /// implementation. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error(#[from] ErrorInner); /// Error returned when opening a file and subsequent deserialization fail. #[derive(Debug, thiserror::Error)] pub enum ReadFileError { /// Variant returned when a file couldn't be opened. #[error("failed to open file {path} for reading")] Open { /// Path to file that was accessed. path: std::path::PathBuf, /// The reason why opening failed. #[source] error: std::io::Error, }, /// Variant returned when read or deserialization fail. #[error("failed to load file {path}")] Load { /// Path to the file that could not be loaded. path: std::path::PathBuf, /// The reason why loading failed. #[source] error: Error, }, } rfc822-like-0.2.3/src/de/mod.rs000064400000000000000000000543301046102023000140670ustar 00000000000000//! # Deserialization of RFC822-like format //! //! This module contains types and methods used for deserialization of RFC822-like format. //! The current implementation is very basic. //! It lacks borrowing the contents of the string, etc. //! It is mainly meant for quick look at `apt` metadata, however if you have a use case that needs //! more than that I will be happy to accept a PR. use serde::de::{Visitor, MapAccess, SeqAccess, DeserializeSeed, IntoDeserializer}; use std::io; use error::ErrorInner; pub use error::Error; pub mod error; /// Deserializes a single record or multiple records separated by empty lines. /// /// Note that RFC822 is **not** self-describing, thus you must specify the type being deserialized. /// That means something like `serde_json::Value` can not be deserialized. /// /// The allowed types are: /// /// * map /// * struct /// * sequence of maps with str-deserializable keys /// * sequence of structs /// /// Further, values of maps and types of fields of structs must be either deserializable from `str` /// or sequence of `str`. /// /// # Example /// /// ``` /// use rfc822_like::de::Deserializer; /// use serde::Deserialize; /// /// let input = "Package: foo /// Description: The Foo /// /// Package: bar /// Description: The Bar /// "; /// /// let mut reader = input.as_bytes(); /// /// #[derive(Debug, Eq, PartialEq, serde_derive::Deserialize)] /// #[serde(rename_all = "PascalCase")] /// struct Record { /// package: String, /// description: String, /// } /// /// let expected = vec![ /// Record { /// package: "foo".to_owned(), /// description:"The Foo".to_owned(), /// }, /// Record { /// package: "bar".to_owned(), /// description: "The Bar".to_owned(), /// }, /// ]; /// /// let deserialized = >::deserialize(Deserializer::new(&mut reader)).unwrap(); /// assert_eq!(deserialized, expected); /// ``` /// /// Additionally, sequences of strings in fields are supported: /// /// ``` /// use rfc822_like::de::Deserializer; /// use serde::Deserialize; /// /// let input = "Depends: bitcoind, python (>= 3.0.0)\n"; /// let mut reader = input.as_bytes(); /// /// #[derive(Debug, Eq, PartialEq, serde_derive::Deserialize)] /// #[serde(rename_all = "PascalCase")] /// struct Package { /// depends: Vec, /// } /// /// let expected = Package { /// depends: vec!["bitcoind".to_owned(), "python (>= 3.0.0)".to_owned()] /// }; /// /// let deserialized = Package::deserialize(Deserializer::new(&mut reader)).unwrap(); /// assert_eq!(deserialized, expected); /// ``` pub struct Deserializer { state: DeserializerState, } impl Deserializer { /// Creates a `Deserializer` from buffered reader. pub fn new(reader: R) -> Self { Deserializer { state: DeserializerState::new(reader), } } } impl<'de, R: io::BufRead> serde::Deserializer<'de> for Deserializer { type Error = Error; fn deserialize_any>(self, _visitor: V) -> Result { Err(ErrorInner::AmbiguousType.into()) } fn deserialize_seq>(mut self, visitor: V) -> Result { visitor.visit_seq(Seq(&mut self.state)) } fn deserialize_map>(mut self, visitor: V) -> Result { visitor.visit_map(&mut self.state) } fn deserialize_struct>(mut self, _name: &'static str, _fields: &'static [&'static str], visitor: V) -> Result { visitor.visit_map(&mut self.state) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct tuple tuple_struct enum identifier ignored_any } } struct Seq<'a, R: io::BufRead>(&'a mut DeserializerState); impl<'a, 'de, R: io::BufRead> SeqAccess<'de> for Seq<'a, R> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de> { if self.0.eof { return Ok(None); } match seed.deserialize(SingleRecordDeserializer::new(self.0)) { Ok(value) => Ok(Some(value)), Err(_) if self.0.empty => Ok(None), Err(error) => Err(error), } } } struct SingleRecordDeserializer<'a, R: io::BufRead> { state: &'a mut DeserializerState, } impl<'a, R: io::BufRead> SingleRecordDeserializer<'a, R> { fn new(state: &'a mut DeserializerState) -> Self { SingleRecordDeserializer { state, } } } struct DeserializerState { reader: R, buf: String, line: usize, eof: bool, empty: bool, } impl DeserializerState { fn new(reader: R) -> Self { DeserializerState { reader, buf: String::new(), line: 0, eof: false, empty: true, } } fn get_key(&mut self) -> Result, Error> { if self.buf.is_empty() { match self.reader.read_line(&mut self.buf).map_err(ErrorInner::from)? { 0 => { self.eof = true; return Ok(None) }, // just \n 1 => { self.buf.clear(); self.empty = true; self.line += 1; return Ok(None); }, _ => self.line += 1, } } if self.buf == "\n" { self.buf.clear(); self.empty = true; return Ok(None); } match self.buf.find(':') { Some(pos) => { self.empty = false; Ok(Some(&self.buf[..pos])) }, None => { Err(ErrorInner::MissingColon(self.line).into()) }, } } fn get_value(&mut self) -> Result<(&str, usize), Error> { let mut pos = self.buf.len(); loop { let amount = self.reader.read_line(&mut self.buf).map_err(ErrorInner::from)?; if amount == 0 || !(self.buf[pos..].starts_with(' ') || self.buf[pos..].starts_with('\t')) { break; } pos += amount; } let begin = self.buf.find(':').expect("The caller didn't handle the error") + 1; Ok((self.buf[begin..pos].trim(), pos)) } fn clear_buf(&mut self, pos: usize) { self.buf.replace_range(0..pos, ""); } } impl<'a, 'de, R: io::BufRead> MapAccess<'de> for &'a mut DeserializerState { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> where K: DeserializeSeed<'de> { self .get_key()? .map(move |key| seed.deserialize(KeyDeserializer(key))) .transpose() } fn next_value_seed(&mut self, seed: V) -> Result where V: DeserializeSeed<'de> { let (value, pos) = self .get_value()?; let result = seed.deserialize(ValueDeserializer(value)); self.clear_buf(pos); result } } impl<'a, 'de, R: io::BufRead> serde::Deserializer<'de> for SingleRecordDeserializer<'a, R> { type Error = Error; fn deserialize_any(mut self, visitor: V) -> Result where V: Visitor<'de> { visitor.visit_map(&mut self.state) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } struct KeyDeserializer<'a>(&'a str); impl<'a, 'de> serde::Deserializer<'de> for KeyDeserializer<'a> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de> { visitor.visit_str(self.0) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } struct ValueDeserializer<'a>(&'a str); impl<'a, 'de> serde::Deserializer<'de> for ValueDeserializer<'a> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de> { self.deserialize_str(visitor) } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de> { if self.0.contains("\n ") { let mut string = String::with_capacity(self.0.len()); let mut iter = self.0.split('\n'); string.push_str(iter.next().expect("split didn't return any item")); for line in iter { string.push('\n'); if line != " ." { string.push_str(line.trim_start()); } } visitor.visit_string(string) } else { visitor.visit_str(self.0) } } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de> { // Possible optimization: we could instead have a visitor that points to appropriate // position inside the buffer and removes the beginning if called here, or turns it into a // slice if called above. self.deserialize_str(visitor) } fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de> { visitor.visit_seq(StrSeq(self.0.split(','))) } fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de> { visitor.visit_some(self) } fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { visitor.visit_enum(self.0.into_deserializer()) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char bytes byte_buf unit unit_struct newtype_struct tuple tuple_struct map struct identifier ignored_any } } struct StrDeserializer<'a>(&'a str); impl<'a, 'de> serde::Deserializer<'de> for StrDeserializer<'a> { type Error = Error; fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de> { visitor.visit_str(self.0) } serde::forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier ignored_any } } struct StrSeq<'a>(std::str::Split<'a, char>); impl<'a, 'de> SeqAccess<'de> for StrSeq<'a> { type Error = Error; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> where T: DeserializeSeed<'de> { self.0.next().map(|item| seed.deserialize(StrDeserializer(item.trim()))).transpose() } // fn size_hint(&self) -> Option { ... } not specialized for split } #[cfg(test)] mod tests { use serde::Deserialize; #[test] fn test_single() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name: bitcoin" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); } #[test] fn test_two_fields() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, summary: String, } let mut input = b"Name: bitcoin\nSummary: Magic Internet Money" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); assert_eq!(packages[0].summary, "Magic Internet Money"); } #[test] fn test_two_packages() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name: bitcoin\n\nName: lightning" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 2); assert_eq!(packages[0].name, "bitcoin"); assert_eq!(packages[1].name, "lightning"); } #[test] fn test_two_packages_two_fields() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, summary: String, } let mut input = b"Name: bitcoin\nSummary: Magic Internet Money\n\nName: lightning\nSummary: The payment rail" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 2); assert_eq!(packages[0].name, "bitcoin"); assert_eq!(packages[0].summary, "Magic Internet Money"); assert_eq!(packages[1].name, "lightning"); assert_eq!(packages[1].summary, "The payment rail"); } #[test] fn test_single_newline_end() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name: bitcoin\n" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); } #[test] fn test_two_packages_newline_end() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name: bitcoin\n\nName: lightning\n" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 2); assert_eq!(packages[0].name, "bitcoin"); assert_eq!(packages[1].name, "lightning"); } #[test] fn test_two_packages_two_fields_newline_end() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, summary: String, } let mut input = b"Name: bitcoin\nSummary: Magic Internet Money\n\nName: lightning\nSummary: The payment rail\n" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 2); assert_eq!(packages[0].name, "bitcoin"); assert_eq!(packages[0].summary, "Magic Internet Money"); assert_eq!(packages[1].name, "lightning"); assert_eq!(packages[1].summary, "The payment rail"); } #[test] fn test_single_package_double_newline_end() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name: bitcoin\n\n" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); } #[test] fn test_no_space() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name:bitcoin" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); } #[test] fn test_val_on_new_line() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name:\n bitcoin" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name, "bitcoin"); } #[test] fn test_seq_single() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: Vec, } let mut input = b"Name: bitcoin" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name.len(), 1); assert_eq!(packages[0].name[0], "bitcoin"); } #[test] fn test_seq_two() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: Vec, } let mut input = b"Name: bitcoin,lightning" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let packages = >::deserialize(deserializer).unwrap(); assert_eq!(packages.len(), 1); assert_eq!(packages[0].name.len(), 2); assert_eq!(packages[0].name[0], "bitcoin"); assert_eq!(packages[0].name[1], "lightning"); } #[test] fn test_extra_fields() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, } let mut input = b"Name:\n bitcoin\nVersion: 0.21.1" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let package = Record::deserialize(deserializer).unwrap(); assert_eq!(package.name, "bitcoin"); } #[test] fn test_multiline() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { description: String, } let mut input = b"Description:\n A very nice package\n This package is very nice\n because it has a multi-line\n description." as &[u8]; let deserializer = super::Deserializer::new(&mut input); let package = Record::deserialize(deserializer).unwrap(); assert_eq!(package.description, "A very nice package\nThis package is very nice\nbecause it has a multi-line\ndescription."); } #[test] fn test_multiparagraph() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { description: String, } let mut input = b"Description:\n A very nice package\n This package is very nice\n because it has a multi-line\n description.\n .\n It also has another paragraph in\n the description.\n .\n And another one." as &[u8]; let deserializer = super::Deserializer::new(&mut input); let package = Record::deserialize(deserializer).unwrap(); assert_eq!(package.description, "A very nice package\nThis package is very nice\nbecause it has a multi-line\ndescription.\n\nIt also has another paragraph in\nthe description.\n\nAnd another one."); } #[test] #[cfg(feature = "live_test")] fn live() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { #[allow(dead_code)] package: String, #[allow(dead_code)] description: String, } let dir = std::fs::read_dir("/var/lib/apt/lists").unwrap(); for entry in dir { let entry = entry.unwrap(); match entry.path().to_str() { None => continue, Some(path) if !path.ends_with("_Records") => continue, Some(_) => (), } let file = std::fs::File::open(entry.path()).unwrap(); let deserializer = super::Deserializer::new(std::io::BufReader::new(file)); >::deserialize(deserializer).unwrap_or_else(|error| panic!("Failed to parse {}: {:?}", entry.path().display(), error)); } } #[test] fn test_option_none() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, description: Option, } let mut input = b"Name: bitcoin" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let package = Record::deserialize(deserializer).unwrap(); assert_eq!(package.name, "bitcoin"); assert!(package.description.is_none()); } #[test] fn test_option_some() { #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Record { name: String, description: Option, } let mut input = b"Name: bitcoin\nDescription: The Internet of Money" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let package = Record::deserialize(deserializer).unwrap(); assert_eq!(package.name, "bitcoin"); assert_eq!(package.description, Some("The Internet of Money".to_owned())); } #[test] fn test_deserialize_unit_variant() { #[derive(serde_derive::Deserialize, PartialEq, Eq, Debug)] #[serde(rename_all = "snake_case")] enum Foo { Bar, } #[derive(serde_derive::Deserialize)] #[serde(rename_all = "PascalCase")] struct Baz { foo: Foo, } let mut input = b"Foo: bar\n" as &[u8]; let deserializer = super::Deserializer::new(&mut input); let baz = Baz::deserialize(deserializer).unwrap(); assert_eq!(baz.foo, Foo::Bar); } } rfc822-like-0.2.3/src/lib.rs000064400000000000000000000172021046102023000134630ustar 00000000000000//! A library for handling RFC822-like format used in Debian //! //! This crate implements the file format inpired by RFC822 that is used in Debian packages. //! It is the format of the `debian/control` files in packages and `Packages` file of `apt`. //! It is called `rfc822-like` instead of just `rfc822` because Debian does not claim to implement RFC822 exactly //! and this crate is focused on working with Debian tools, not parsing exact RFC822 file format. //! Frankly, I didn't even bother to read RFC822 itself. //! //! If you need to strictly parse RFC822, I suggest you to fork this crate and fix whatever differences there are. //! I'm not interested in maintaining strict RFC822 crate, so don't send PRs for that but I'm willing to put //! common pieces into their own crate. //! If you're interested in this approach feel free to file a PR (or ask beforehand if you have questions). //! //! Note that this crate is currently not optimized for performance. //! There are multiple places where allocation could be avoided and other optimizations may be missing. //! It's absolutely fine for my own use cases, and probably will be for yours as well. //! If you need it to be faster or just want to have fun improving its performance I'll be happy to accept PRs. //! //! The API is currently not set in stone and may change over time. //! Basic steps to minimize the impact of changes were taken (e.g. encapsulation of `Error` type). //! The crate also currently lacks serialization but it will be implemented eventually. //! Feel free to send PRs! //! //! Check [`Deserializer`] type for deserialization API reference and examples. //! Check [`Serializer`] type for serialization API reference and examples. #![deny(missing_docs)] pub mod de; pub mod ser; pub use de::Deserializer; pub use ser::Serializer; use serde::{Serialize, Deserialize}; use std::{io, fmt}; use std::path::{Path, PathBuf}; use de::error::ReadFileError; /// Deserialize a value from a reader. /// /// This deserializes `T` using data returned by `reader`. pub fn from_reader Deserialize<'a>, R: io::BufRead>(reader: R) -> Result { T::deserialize(Deserializer::new(reader)) } /// Reads the file and deserializes the value from it. /// /// This is a convenience function for opening the file, making `BufReader` and using it in /// `from_reader`. It's most useful when dealing with Debian control files stored in the /// system/source code. /// /// Note that instead of [`std::io::Error`] this returns [`ReadFileError`] which carries /// information about path so that the error message is more useful. pub fn from_file Deserialize<'a>, P: AsRef + Into>(path: P) -> Result { let file = match std::fs::File::open(&path) { Ok(file) => file, Err(error) => return Err(ReadFileError::Open { path: path.into(), error, }) }; let reader = io::BufReader::new(file); T::deserialize(Deserializer::new(reader)).map_err(|error| ReadFileError::Load { path: path.into(), error, }) } /// Deserializes a value from bytes that are *not* guaranteed to be UTF-8. /// /// Non-UTF8 data will obviously still fail but you don't have to do the check explicitly. pub fn from_bytes<'a, T: Deserialize<'a>>(mut bytes: &'a [u8]) -> Result { T::deserialize(Deserializer::new(&mut bytes)) } /// Deserializes a value from a string. pub fn from_str<'a, T: Deserialize<'a>>(s: &'a str) -> Result { from_bytes(s.as_bytes()) } /// Writes the `value` to [`std::fmt::Write`]r. /// /// This is useful if you want a guarantee that the value written is UTF-8 encoded. pub fn to_fmt_writer(writer: W, value: &T) -> Result<(), ser::Error> { value.serialize(Serializer::new(writer)) } /// Writes the `value` to [`std::io::Write`]r. /// /// The `Write` trait from `std::io` is more common than `fmt` so a convenience function is /// provided that writes to `std::io` instead. This is mainly useful for writing into files. Note /// however that this function doesn't perform any buffering so you need to take care of that! pub fn to_writer(writer: W, value: &T) -> Result<(), ser::Error> { fmt2io::write(writer, |writer| to_fmt_writer(writer, value).map(Ok).or_else(ser::Error::into_fmt)) .map_err(ser::error::ErrorInternal::IoWriteFailed)? } /// Serializes the `value` into memory. /// /// This allocates the string and writes the value into it. It may cause multiple reallocations so /// it's better to write to writers directly if possible. pub fn to_string(value: &T) -> Result { let mut result = String::new(); to_fmt_writer(&mut result, value)?; Ok(result) } #[cfg(test)] mod tests { use quickcheck::{quickcheck, TestResult}; use std::collections::HashMap; quickcheck! { fn reversible_map_string_serialization(map: HashMap) -> TestResult { for (key, value) in &map { if key.is_empty() || key.contains(&[':', '\n', '\0'] as &[_]) || key.trim() != key || value.trim() != value || value.contains('\0') { return TestResult::discard(); } if let Some(_) = value.split('\n').find(|line| line.trim() != *line || *line == ".") { return TestResult::discard(); } } let s = super::to_string(&map).unwrap(); let deserialized = super::from_str::>(&s).unwrap(); TestResult::from_bool(deserialized == map) } #[cfg(rfc822_like_test_reversible_map_vec_serialization)] fn reversible_map_vec_serialization(map: HashMap>) -> TestResult { for (key, value) in &map { if key.is_empty() || key.contains(&[':', '\n', '\0'] as &[_]) || key.trim() != key || value.is_empty() { return TestResult::discard(); } for item in value { if item.trim() != item || item.contains(&[',', '\n'] as &[_]) { return TestResult::discard(); } } } let s = super::to_string(&map).unwrap(); let deserialized = super::from_str::>>(&s).unwrap(); TestResult::from_bool(deserialized == map) } } #[test] fn empty_val() { let mut map = HashMap::new(); map.insert("X".to_owned(), String::new()); let s = super::to_string(&map).unwrap(); let deserialized = super::from_str::>(&s).unwrap(); assert_eq!(deserialized, map); } #[test] fn funny_value() { let mut map = HashMap::new(); map.insert("\u{1}".to_owned(), "\u{1}\n\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}\u{1}".to_owned()); let s = super::to_string(&map).unwrap(); let deserialized = super::from_str::>(&s).unwrap(); assert_eq!(deserialized, map); } #[test] fn multi_line() { let mut map = HashMap::new(); map.insert("X".to_owned(), "a\nb\nc\nd".to_owned()); let s = super::to_string(&map).unwrap(); let deserialized = super::from_str::>(&s).unwrap(); assert_eq!(deserialized, map); } } rfc822-like-0.2.3/src/ser/error.rs000064400000000000000000000032071046102023000146370ustar 00000000000000//! Error types related to serialization. /// Error returned when serializing fails. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error { internal: ErrorInternal, } impl From for Error { fn from(value: ErrorInternal) -> Self { Error { internal: value, } } } #[derive(Debug, thiserror::Error)] #[error("Unsupported data type {unsupported}")] pub(crate) enum ErrorInternal { #[error("unsupported data type {0}")] Unsupported(&'static str), #[error("{0}")] Custom(String), #[error("invalid char {c} in key '{key}' at position {pos}")] InvalidKeyChar { key: String, c: char, pos: usize }, #[error("empty key is not allowed")] EmptyKey, #[error("failed to write")] FmtWriteFailed, #[error("failed to write")] IoWriteFailed(#[from] std::io::Error), } impl Error { pub(crate) fn unsupported_data_type(type_name: &'static str) -> Self { let type_name = if type_name.starts_with("serialize_") { &type_name[10..] } else { type_name }; ErrorInternal::Unsupported(type_name).into() } pub(crate) fn failed_write(_: std::fmt::Error) -> Self { ErrorInternal::FmtWriteFailed.into() } pub(crate) fn into_fmt(self) -> Result, std::fmt::Error> { if let ErrorInternal::FmtWriteFailed = self.internal { Err(std::fmt::Error) } else { Ok(Err(self)) } } } impl serde::ser::Error for Error { fn custom(msg: T) -> Self { ErrorInternal::Custom(msg.to_string()).into() } } rfc822-like-0.2.3/src/ser/mod.rs000064400000000000000000001112061046102023000142640ustar 00000000000000//! # Serialization of RFC822-like format //! //! This module contains types and methods used for serialization of RFC822-like format. //! The current implementation is very basic. //! It lacks borrowing the contents of the string, reformatting paragraphs, etc. //! It is mainly meant for serializing Debian `control` files, however if you have a use //! case that needs more than that I will be happy to accept a PR. use std::fmt::Write; use std::fmt; use serde::ser; use unicode_segmentation::UnicodeSegmentation; use std::borrow::Cow; pub use error::Error; pub mod error; /// Convenience function serializing into `fmt::Writer` pub fn to_fmt_writer(writer: W, value: T) -> Result<(), Error> { value.serialize(Serializer::new(writer)) } macro_rules! unsupported_types { ($(fn $fn_name:ident$(<$($gen:ident),*>)?(self $(, $arg:ident: $arg_type:ty)*) -> Result<$ret:ty> $(where $type:ty: ?Sized + Serialize)?;)*) => { $( fn $fn_name$(<$($gen),*>)?(self $(, $arg: $arg_type)*) -> Result<$ret, Self::Error> $(where $type: ?Sized + serde::Serialize)? { $( let _ = $arg; )* Err(Error::unsupported_data_type(stringify!($fn_name))) } )* } } /// Serializer backed by `fmt::Writer` pub struct Serializer { writer: Writer, wrap_long_lines: bool, } impl Serializer where W: Write { /// Constructs the serializer pub fn new(writer: W) -> Self { Serializer { writer, wrap_long_lines: false, } } /// Causes lines longer than 80 characters to be wrapped on word boundaries. pub fn wrap_long_lines(mut self, wrap: bool) -> Self { self.wrap_long_lines = wrap; self } } impl serde::Serializer for Serializer where W: Write { type Ok = (); type Error = Error; type SerializeSeq = SeqSerializer; type SerializeTuple = ser::Impossible; type SerializeTupleStruct = ser::Impossible; type SerializeTupleVariant = ser::Impossible; type SerializeMap = MapSerializer; type SerializeStruct = StructSerializer; type SerializeStructVariant = ser::Impossible; fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { Ok(StructSerializer { writer: self.writer, wrap_long_lines: self.wrap_long_lines, }) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + ser::Serialize { value.serialize(self) } fn serialize_map(self, _len: Option) -> Result { Ok(MapSerializer { writer: self.writer, field_name: None, wrap_long_lines: self.wrap_long_lines, }) } fn serialize_seq(self, _len: Option) -> Result { Ok(SeqSerializer { output: self.writer, is_empty: true, wrap_long_lines: self.wrap_long_lines, }) } unsupported_types! { fn serialize_bool(self, v: bool) -> Result<()>; fn serialize_i8(self, v: i8) -> Result<()>; fn serialize_i16(self, v: i16) -> Result<()>; fn serialize_i32(self, v: i32) -> Result<()>; fn serialize_i64(self, v: i64) -> Result<()>; fn serialize_u8(self, v: u8) -> Result<()>; fn serialize_u16(self, v: u16) -> Result<()>; fn serialize_u32(self, v: u32) -> Result<()>; fn serialize_u64(self, v: u64) -> Result<()>; fn serialize_f32(self, v: f32) -> Result<()>; fn serialize_f64(self, v: f64) -> Result<()>; fn serialize_char(self, v: char) -> Result<()>; fn serialize_str(self, v: &str) -> Result<()>; fn serialize_bytes(self, v: &[u8]) -> Result<()>; fn serialize_none(self) -> Result<()>; fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_unit(self) -> Result<()>; fn serialize_unit_struct(self, name: &'static str) -> Result<()>; fn serialize_unit_variant(self, name: &'static str, variant_index: u32, variant: &'static str) -> Result<()>; fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_tuple(self, len: usize) -> Result; fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result; fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; } } struct NonSeqSerializer { writer: Writer, wrap_long_lines: bool, } impl serde::Serializer for NonSeqSerializer where W: Write { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible; type SerializeTuple = ser::Impossible; type SerializeTupleStruct = ser::Impossible; type SerializeTupleVariant = ser::Impossible; type SerializeMap = MapSerializer; type SerializeStruct = StructSerializer; type SerializeStructVariant = ser::Impossible; fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { Ok(StructSerializer { writer: self.writer, wrap_long_lines: self.wrap_long_lines, }) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + ser::Serialize { value.serialize(self) } fn serialize_map(self, _len: Option) -> Result { Ok(MapSerializer { writer: self.writer, field_name: None, wrap_long_lines: self.wrap_long_lines, }) } unsupported_types! { fn serialize_bool(self, v: bool) -> Result<()>; fn serialize_i8(self, v: i8) -> Result<()>; fn serialize_i16(self, v: i16) -> Result<()>; fn serialize_i32(self, v: i32) -> Result<()>; fn serialize_i64(self, v: i64) -> Result<()>; fn serialize_u8(self, v: u8) -> Result<()>; fn serialize_u16(self, v: u16) -> Result<()>; fn serialize_u32(self, v: u32) -> Result<()>; fn serialize_u64(self, v: u64) -> Result<()>; fn serialize_f32(self, v: f32) -> Result<()>; fn serialize_f64(self, v: f64) -> Result<()>; fn serialize_char(self, v: char) -> Result<()>; fn serialize_str(self, v: &str) -> Result<()>; fn serialize_bytes(self, v: &[u8]) -> Result<()>; fn serialize_none(self) -> Result<()>; fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_unit(self) -> Result<()>; fn serialize_unit_struct(self, name: &'static str) -> Result<()>; fn serialize_unit_variant(self, name: &'static str, variant_index: u32, variant: &'static str) -> Result<()>; fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_seq(self, len: Option) -> Result; fn serialize_tuple(self, len: usize) -> Result; fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result; fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; } } /// Serializer used for serializing sequences of records. /// /// This type is internal and should not be used directly. If you need to refer to it it's best to use /// `Serializer::SerializeSeq`. pub struct SeqSerializer { output: Writer, wrap_long_lines: bool, is_empty: bool, } impl ser::SerializeSeq for SeqSerializer where W: Write { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ser::Serialize + ?Sized { if !self.is_empty { writeln!(self.output).map_err(Error::failed_write)?; } self.is_empty = false; value.serialize(NonSeqSerializer { writer: &mut self.output, wrap_long_lines: self.wrap_long_lines }) } fn end(self) -> Result { Ok(()) } } /// Internal serializer for structs pub struct StructSerializer { writer: Writer, wrap_long_lines: bool, } impl ser::SerializeStruct for StructSerializer { type Ok = (); type Error = Error; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result where T: ?Sized + ser::Serialize { value.serialize(FieldSerializer { field_name: key.into(), output: &mut self.writer, wrap_long_lines: self.wrap_long_lines, })?; Ok(()) } fn end(self) -> Result { Ok(()) } } /// Internal serializer for maps // Can't use non-static lifetime because of lack of GAT pub struct MapSerializer { writer: Writer, field_name: Option>, wrap_long_lines: bool, } impl ser::SerializeMap for MapSerializer { type Ok = (); type Error = Error; fn serialize_key(&mut self, value: &T) -> Result where T: ?Sized + ser::Serialize { value.serialize(KeySerializer { key: &mut self.field_name, })?; Ok(()) } fn serialize_value(&mut self, value: &T) -> Result where T: ?Sized + ser::Serialize { value.serialize(FieldSerializer { field_name: self.field_name.take().expect("serialize_value() called before serialize_key()"), output: &mut self.writer, wrap_long_lines: self.wrap_long_lines, })?; Ok(()) } fn end(self) -> Result { Ok(()) } } fn check_and_write_key(mut output: impl Write, key: &str) -> Result<(), Error> { if key.is_empty() { return Err(error::ErrorInternal::EmptyKey.into()); } if let Some(pos) = key.find(&[':', '\n'] as &[char]) { let c = key[pos..].chars().next().expect("char found at the end - WTF"); return Err(error::ErrorInternal::InvalidKeyChar { key: key.to_owned(), c, pos, }.into()); } write!(output, "{}: ", key).map_err(Error::failed_write) } struct KeySerializer<'a> { key: &'a mut Option>, } impl<'a> serde::Serializer for KeySerializer<'a> { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible; type SerializeTuple = ser::Impossible; type SerializeTupleStruct = ser::Impossible; type SerializeTupleVariant = ser::Impossible; type SerializeMap = ser::Impossible; type SerializeStruct = ser::Impossible; type SerializeStructVariant = ser::Impossible; fn serialize_str(self, value: &str) -> Result { *self.key = Some(value.to_owned().into()); Ok(()) } unsupported_types! { fn serialize_bool(self, v: bool) -> Result<()>; fn serialize_i8(self, v: i8) -> Result<()>; fn serialize_i16(self, v: i16) -> Result<()>; fn serialize_i32(self, v: i32) -> Result<()>; fn serialize_i64(self, v: i64) -> Result<()>; fn serialize_u8(self, v: u8) -> Result<()>; fn serialize_u16(self, v: u16) -> Result<()>; fn serialize_u32(self, v: u32) -> Result<()>; fn serialize_u64(self, v: u64) -> Result<()>; fn serialize_f32(self, v: f32) -> Result<()>; fn serialize_f64(self, v: f64) -> Result<()>; fn serialize_char(self, v: char) -> Result<()>; fn serialize_bytes(self, v: &[u8]) -> Result<()>; fn serialize_unit(self) -> Result<()>; fn serialize_unit_struct(self, name: &'static str) -> Result<()>; fn serialize_unit_variant(self, name: &'static str, variant_index: u32, variant: &'static str) -> Result<()>; fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_tuple(self, len: usize) -> Result; fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result; fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_map(self, len: Option) -> Result; fn serialize_struct(self, name: &'static str, len: usize) -> Result; fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_none(self) -> Result<()>; fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_seq(self, _len: Option) -> Result; fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; } } #[derive(Copy, Clone)] enum FieldWriterState { FirstLine, Neutral, EndedWithNewline, } struct FieldWriter { output: Writer, wrap_long_lines: bool, state: FieldWriterState, } impl FieldWriter { fn new(output: W, wrap_long_lines: bool) -> Self { FieldWriter { output, wrap_long_lines, state: FieldWriterState::FirstLine, } } fn finish(&mut self) -> fmt::Result { if let FieldWriterState::EndedWithNewline = self.state { Ok(()) } else { self.output.write_str("\n") } } } impl Write for FieldWriter { fn write_str(&mut self, s: &str) -> fmt::Result { if s.is_empty() { return Ok(()) } let mut iter = s.split('\n'); let line = iter.next().expect("split() returned an empty iterator"); match self.state { // We must not wrap the first line FieldWriterState::FirstLine => self.output.write_str(line)?, FieldWriterState::EndedWithNewline if line.is_empty() => self.output.write_str(".")?, FieldWriterState::EndedWithNewline | FieldWriterState::Neutral if self.wrap_long_lines => write_wraped(&mut self.output, line)?, FieldWriterState::EndedWithNewline | FieldWriterState::Neutral => self.output.write_str(line)?, } let mut contained_newline = false; let mut iter = iter.peekable(); while let Some(line) = iter.next() { contained_newline = true; self.output.write_str("\n ")?; if line.is_empty() { // if it's last we don't know what follows if iter.peek().is_some() { self.output.write_str(".")?; } } else if self.wrap_long_lines { write_wraped(&mut self.output, line)?; } else { self.output.write_str(line)?; } } match (self.state, contained_newline) { (FieldWriterState::FirstLine, false) => (), _ if s.ends_with('\n') => self.state = FieldWriterState::EndedWithNewline, _ => self.state = FieldWriterState::Neutral, } Ok(()) } } struct FieldSerializer { field_name: Cow<'static, str>, output: Writer, wrap_long_lines: bool, } fn write_wraped(mut out: W, line: &str) -> std::fmt::Result { let mut written = 1; for word in line.split_word_bounds() { let word_len = word.graphemes(true).count(); if written + word_len > 80 { out.write_str("\n ")?; written = 1; } if !(word.trim().is_empty() && written == 1) { out.write_str(word)?; written += word_len; } } Ok(()) } impl serde::Serializer for FieldSerializer where W: Write { type Ok = (); type Error = Error; type SerializeSeq = SubSeqSerializer; type SerializeTuple = ser::Impossible; type SerializeTupleStruct = ser::Impossible; type SerializeTupleVariant = ser::Impossible; type SerializeMap = ser::Impossible; type SerializeStruct = ser::Impossible; type SerializeStructVariant = ser::Impossible; fn collect_str(mut self, value: &T) -> Result { check_and_write_key(&mut self.output, &self.field_name)?; let mut writer = FieldWriter::new(&mut self.output, self.wrap_long_lines); (move || { write!(writer, "{}", value)?; writer.finish() })().map_err(Error::failed_write) } fn serialize_str(self, value: &str) -> Result { self.collect_str(value) } fn serialize_none(self) -> Result { Ok(()) } fn serialize_some(self, value: &T) -> Result where T: ?Sized + ser::Serialize { value.serialize(self) } fn serialize_seq(self, _len: Option) -> Result { Ok(SubSeqSerializer { output: self.output, state: SubSeqSerializerState::Empty { field_name: self.field_name, }, }) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + ser::Serialize { value.serialize(self) } fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<(), Self::Error> { self.serialize_str(variant) } unsupported_types! { fn serialize_bool(self, v: bool) -> Result<()>; fn serialize_i8(self, v: i8) -> Result<()>; fn serialize_i16(self, v: i16) -> Result<()>; fn serialize_i32(self, v: i32) -> Result<()>; fn serialize_i64(self, v: i64) -> Result<()>; fn serialize_u8(self, v: u8) -> Result<()>; fn serialize_u16(self, v: u16) -> Result<()>; fn serialize_u32(self, v: u32) -> Result<()>; fn serialize_u64(self, v: u64) -> Result<()>; fn serialize_f32(self, v: f32) -> Result<()>; fn serialize_f64(self, v: f64) -> Result<()>; fn serialize_char(self, v: char) -> Result<()>; fn serialize_bytes(self, v: &[u8]) -> Result<()>; fn serialize_unit(self) -> Result<()>; fn serialize_unit_struct(self, name: &'static str) -> Result<()>; fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_tuple(self, len: usize) -> Result; fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result; fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_map(self, len: Option) -> Result; fn serialize_struct(self, name: &'static str, len: usize) -> Result; fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; } } #[derive(Clone)] enum SubSeqSerializerState { Empty { field_name: Cow<'static, str>, }, NonEmpty { indent: usize, }, } struct SubSeqSerializer { output: Writer, state: SubSeqSerializerState, } impl ser::SerializeSeq for SubSeqSerializer where W: Write { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> where T: ser::Serialize + ?Sized { use SubSeqSerializerState::*; (|| -> Result<_, _> { match &self.state { Empty { field_name, } => { write!(self.output, "{}: ", field_name)?; let indent = field_name.graphemes(true).count() + 2; self.state = NonEmpty { indent, }; }, NonEmpty { indent, } => { self.output.write_str(",\n")?; for _ in 0..*indent { self.output.write_char(' ')?; } } } Ok(()) })().map_err(Error::failed_write)?; value.serialize(StringSerializer(&mut self.output)) } fn end(mut self) -> Result { match self.state { SubSeqSerializerState::NonEmpty { .. } => self.output.write_char('\n'), SubSeqSerializerState::Empty { .. } => Ok(()) }.map_err(Error::failed_write) } } struct StringSerializer(Writer); impl serde::Serializer for StringSerializer where W: Write { type Ok = (); type Error = Error; type SerializeSeq = ser::Impossible; type SerializeTuple = ser::Impossible; type SerializeTupleStruct = ser::Impossible; type SerializeTupleVariant = ser::Impossible; type SerializeMap = ser::Impossible; type SerializeStruct = ser::Impossible; type SerializeStructVariant = ser::Impossible; fn serialize_str(mut self, value: &str) -> Result { self.0.write_str(value).map_err(Error::failed_write) } fn collect_str(mut self, value: &T) -> Result where T: ?Sized + std::fmt::Display { write!(self.0, "{}", value).map_err(Error::failed_write) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<(), Self::Error> where T: ?Sized + ser::Serialize { value.serialize(self) } fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<(), Self::Error> { self.serialize_str(variant) } unsupported_types! { fn serialize_bool(self, v: bool) -> Result<()>; fn serialize_i8(self, v: i8) -> Result<()>; fn serialize_i16(self, v: i16) -> Result<()>; fn serialize_i32(self, v: i32) -> Result<()>; fn serialize_i64(self, v: i64) -> Result<()>; fn serialize_u8(self, v: u8) -> Result<()>; fn serialize_u16(self, v: u16) -> Result<()>; fn serialize_u32(self, v: u32) -> Result<()>; fn serialize_u64(self, v: u64) -> Result<()>; fn serialize_f32(self, v: f32) -> Result<()>; fn serialize_f64(self, v: f64) -> Result<()>; fn serialize_char(self, v: char) -> Result<()>; fn serialize_bytes(self, v: &[u8]) -> Result<()>; fn serialize_none(self) -> Result<()>; fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_unit(self) -> Result<()>; fn serialize_unit_struct(self, name: &'static str) -> Result<()>; fn serialize_newtype_variant(self, name: &'static str, variant_index: u32, variant: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize; fn serialize_seq(self, len: Option) -> Result; fn serialize_tuple(self, len: usize) -> Result; fn serialize_tuple_struct(self, name: &'static str, len: usize) -> Result; fn serialize_tuple_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; fn serialize_map(self, len: Option) -> Result; fn serialize_struct(self, name: &'static str, len: usize) -> Result; fn serialize_struct_variant(self, name: &'static str, variant_index: u32, variant: &'static str, len: usize) -> Result; } } #[cfg(test)] mod tests { // We want writes to be nice and guarantee whole string is written in a single call, // so we need to ignore this nitpicky lint. #![allow(clippy::write_with_newline)] use super::Serializer; use serde::Serialize; use serde::Serializer as SerdeSerializer; use std::fmt::Write; #[test] fn error() { assert!(Serializer::new(String::new()).serialize_str("foo").is_err()); } #[test] fn empty() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo {} let mut out = String::new(); Foo {}.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, ""); } #[test] fn single() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, } let mut out = String::new(); Foo { bar: "baz" }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: baz\n"); } #[test] fn two() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, baz: &'static str, } let mut out = String::new(); Foo { bar: "bar value", baz: "baz value" }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: bar value\nBaz: baz value\n"); } #[test] fn long_begin() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, } let mut out = String::new(); Foo { bar: "Insanely long string meant for testing, that will be over eighty characters long, I believe." }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: Insanely long string meant for testing, that will be over eighty characters long, I believe.\n"); } #[test] fn long_body() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, } let mut out = String::new(); Foo { bar: "Begin\nInsanely long string meant for testing, that will be over eighty characters long, I believe." } .serialize(Serializer::new(&mut out).wrap_long_lines(true)).expect("Failed to serialize"); assert_eq!(out, "Bar: Begin\n Insanely long string meant for testing, that will be over eighty characters \n long, I believe.\n"); } #[test] fn multiline() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, } let mut out = String::new(); Foo { bar: "first line\nsecond line" }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: first line\n second line\n"); } #[test] fn empty_line() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, } let mut out = String::new(); Foo { bar: "begin\nfirst line\n\nsecond line" }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: begin\n first line\n .\n second line\n"); } #[test] fn none() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: Option<&'static str>, } let mut out = String::new(); Foo { bar: None }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, ""); } #[test] fn some() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: Option<&'static str>, } let mut out = String::new(); Foo { bar: Some("baz") }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: baz\n"); } #[test] fn seq_empty() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static [&'static str], } let mut out = String::new(); Foo { bar: &[] }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, ""); } #[test] fn seq_single() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static [&'static str], } let mut out = String::new(); Foo { bar: &["baz"] }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: baz\n"); } #[test] fn seq_two() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static [&'static str], } let mut out = String::new(); Foo { bar: &["baz", "bitcoin"] }.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: baz,\n bitcoin\n"); } #[test] fn struct_seq() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Foo { bar: &'static str, baz: &'static str, } let mut out = String::new(); vec![Foo { bar: "bar value 1", baz: "baz value 1" }, Foo { bar: "bar value 2", baz: "baz value 2" }] .serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Bar: bar value 1\nBaz: baz value 1\n\nBar: bar value 2\nBaz: baz value 2\n"); } #[test] fn field_writer_empty() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "").unwrap(); writer.finish().unwrap(); assert_eq!(output, "\n"); } #[test] fn field_writer_no_newline() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi\n"); } #[test] fn field_writer_single_newline() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi\nnakamoto").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi\n nakamoto\n"); } #[test] fn field_writer_two_newlines() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi\nnakamoto\nbitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi\n nakamoto\n bitcoin\n"); } #[test] fn field_writer_split_first_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi").unwrap(); write!(writer, " nakamoto").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n"); } #[test] fn field_writer_split_before_first_line_end() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi").unwrap(); write!(writer, "\nnakamoto").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi\n nakamoto\n"); } #[test] fn field_writer_split_after_first_line_end() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi\n").unwrap(); write!(writer, "nakamoto").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi\n nakamoto\n"); } #[test] fn field_writer_split_second_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto\ninvented").unwrap(); write!(writer, " bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n invented bitcoin\n"); } #[test] fn field_writer_empty_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto\n\ninvented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_split_before_empty_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto").unwrap(); write!(writer, "\n\ninvented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_split_in_empty_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto\n").unwrap(); write!(writer, "\ninvented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_split_after_empty_line() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto\n\n").unwrap(); write!(writer, "invented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_split_empty_line_twice1() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto\n").unwrap(); write!(writer, "\n").unwrap(); write!(writer, "invented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_split_empty_line_twice2() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto").unwrap(); write!(writer, "\n").unwrap(); write!(writer, "\ninvented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn field_writer_multi_split_empty_line_three_times() { let mut output = String::new(); let mut writer = super::FieldWriter::new(&mut output, false); write!(writer, "satoshi nakamoto").unwrap(); write!(writer, "\n").unwrap(); write!(writer, "\n").unwrap(); write!(writer, "invented bitcoin").unwrap(); writer.finish().unwrap(); assert_eq!(output, "satoshi nakamoto\n .\n invented bitcoin\n"); } #[test] fn serialize_unit_variant() { #[derive(serde_derive::Serialize)] #[serde(rename_all = "snake_case")] enum Foo { Bar, } #[derive(serde_derive::Serialize)] #[serde(rename_all = "PascalCase")] struct Baz { foo: Foo, } let mut out = String::new(); let baz = Baz { foo: Foo::Bar, }; baz.serialize(Serializer::new(&mut out)).expect("Failed to serialize"); assert_eq!(out, "Foo: bar\n"); } }