#![cfg_attr(docsrs, doc(cfg(feature = "serde")))]
use crate::control::RawParagraph;
use serde::de;
use std::{
io::{BufRead, BufReader, Read},
marker::PhantomData,
};
#[cfg(feature = "sequoia")]
use crate::control::openpgp::{self, OpenPgpValidatorError};
#[cfg(feature = "sequoia")]
use sequoia_openpgp::{Cert, packet::Signature};
#[cfg(feature = "sequoia")]
use std::path::Path;
mod outer;
mod paragraph;
#[derive(Debug)]
pub enum Error {
De(String),
EndOfFile,
InvalidNumber,
InvalidBool,
ParseError(crate::control::Error),
BadType,
Io(std::io::Error),
#[cfg(feature = "sequoia")]
OpenPgp(OpenPgpValidatorError),
InvalidText(std::str::Utf8Error),
}
crate::errors::error_enum!(Error);
impl From<std::io::Error> for Error {
fn from(ioe: std::io::Error) -> Self {
Self::Io(ioe)
}
}
impl de::Error for Error {
fn custom<T>(custom: T) -> Self
where
T: std::fmt::Display,
{
Self::De(custom.to_string())
}
}
pub fn from_reader<'a, 'de, T, ReadT>(input: &'a mut BufReader<ReadT>) -> Result<T, Error>
where
ReadT: Read,
T: de::Deserialize<'de>,
{
let mut buf = String::new();
loop {
match input.read_line(&mut buf)? {
0 => {
if buf.trim().is_empty() {
return Err(Error::EndOfFile);
}
return from_str(&buf);
}
1 => {
if buf.ends_with('\n') && !buf.trim().is_empty() {
return from_str(&buf);
}
}
_ => {}
}
}
}
struct ControlIterator<'a, 'de, T, ReadT> {
input: &'a mut BufReader<ReadT>,
_de: PhantomData<&'de ()>,
_t: PhantomData<T>,
}
impl<'de, T, ReadT> Iterator for ControlIterator<'_, 'de, T, ReadT>
where
ReadT: Read,
T: de::Deserialize<'de>,
{
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
match from_reader(self.input) {
Err(Error::EndOfFile) => None,
v => Some(v),
}
}
}
pub fn from_reader_iter<'a, 'de, T, ReadT>(
input: &'a mut BufReader<ReadT>,
) -> impl Iterator<Item = Result<T, Error>> + use<'a, 'de, T, ReadT>
where
ReadT: Read,
T: de::Deserialize<'de>,
{
ControlIterator {
input,
_de: PhantomData,
_t: PhantomData,
}
}
#[cfg(feature = "tokio")]
mod _tokio {
#![cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
use super::*;
use tokio::io::{AsyncBufReadExt, AsyncRead, BufReader};
pub async fn from_reader_async<'a, 'de, T, ReadT>(
input: &'a mut BufReader<ReadT>,
) -> Result<T, Error>
where
ReadT: AsyncRead,
ReadT: Unpin,
T: de::Deserialize<'de>,
{
let mut buf = String::new();
loop {
match input.read_line(&mut buf).await? {
0 => {
if buf.trim().is_empty() {
return Err(Error::EndOfFile);
}
return from_str(&buf);
}
1 => {
if buf.ends_with('\n') && !buf.trim().is_empty() {
return from_str(&buf);
}
}
_ => {}
}
}
}
pub struct AsyncControlIterator<'a, 'de, T, ReadT>
where
ReadT: AsyncRead,
ReadT: Unpin,
T: de::Deserialize<'de>,
{
input: &'a mut BufReader<ReadT>,
_de: PhantomData<&'de ()>,
_t: PhantomData<T>,
}
impl<'de, T, ReadT> AsyncControlIterator<'_, 'de, T, ReadT>
where
ReadT: AsyncRead,
ReadT: Unpin,
T: de::Deserialize<'de>,
{
pub async fn next(&mut self) -> Option<Result<T, Error>> {
match from_reader_async(self.input).await {
Err(Error::EndOfFile) => None,
v => Some(v),
}
}
}
pub fn from_reader_async_iter<'a, 'de, T, ReadT>(
input: &'a mut BufReader<ReadT>,
) -> AsyncControlIterator<'a, 'de, T, ReadT>
where
ReadT: AsyncRead,
ReadT: Unpin,
T: de::Deserialize<'de>,
{
AsyncControlIterator {
input,
_de: PhantomData,
_t: PhantomData,
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
use std::io::Cursor;
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct TestControl {
#[serde(rename = "Hello")]
hello: String,
}
#[tokio::test]
async fn test_from_reader_async() {
let mut reader = BufReader::new(Cursor::new(
"\
Hello: World
",
));
let test: TestControl = from_reader_async(&mut reader).await.unwrap();
assert_eq!(test.hello, "World");
}
#[tokio::test]
async fn test_from_reader_async_iter() {
let mut reader = BufReader::new(Cursor::new(
"\
Hello: World
Hello: Paul
Hello: You
Hello: Me
",
));
let mut iter = from_reader_async_iter(&mut reader);
let mut values = vec![];
while let Some(rv) = iter.next().await {
let rv: TestControl = rv.unwrap();
values.push(rv.hello);
}
assert_eq!(vec!["World", "Paul", "You", "Me"], values);
}
}
}
#[cfg(feature = "tokio")]
pub use _tokio::{AsyncControlIterator, from_reader_async, from_reader_async_iter};
#[cfg_attr(docsrs, doc(cfg(feature = "seqoia")))]
#[cfg(feature = "sequoia")]
pub fn from_clearsigned_str<'a, 'de, T>(
keyring: &Path,
input: &'a str,
) -> Result<(Vec<(Cert, Signature)>, T), Error>
where
T: de::Deserialize<'de>,
{
let (signatures, input) = openpgp::verify(keyring, input).map_err(Error::OpenPgp)?;
Ok((signatures, from_reader(&mut BufReader::new(input))?))
}
pub fn from_str<'a, 'de, T>(input: &'a str) -> Result<T, Error>
where
T: de::Deserialize<'de>,
{
let input = input.trim_start();
let rp = RawParagraph::parse(input).map_err(Error::ParseError)?;
from_raw_paragraph(&rp)
}
fn from_raw_paragraph<'a, 'de, T>(input: &'a RawParagraph) -> Result<T, Error>
where
T: de::Deserialize<'de>,
{
let iter = input
.fields
.iter()
.flat_map(|v| [v.key.as_str(), v.value.as_str()])
.peekable();
let mut deserializer = outer::Deserializer { iter };
T::deserialize(&mut deserializer)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::version::Version;
use serde::Deserialize;
use std::io::Cursor;
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct TestControlFile {
#[serde(rename = "Package")]
package: String,
#[serde(rename = "Foo")]
foo: String,
#[serde(rename = "True-False")]
true_false: bool,
#[serde(rename = "X-A-Number")]
a_number: usize,
#[serde(rename = "Ello")]
ello: Option<String>,
}
macro_rules! test_de_fails_with {
($name:ident, $type:ty) => {
#[test]
fn $name() {
let err = from_str::<$type>(
"\
Package: something
Foo: Bar
True-False: yes
",
)
.err()
.unwrap();
assert!(
matches!(err, Error::De(_)),
"expected {} got {}",
Error::BadType,
err
);
}
};
}
test_de_fails_with!(test_to_i8, i8);
test_de_fails_with!(test_to_i16, i16);
test_de_fails_with!(test_to_i32, i32);
test_de_fails_with!(test_to_i64, i64);
test_de_fails_with!(test_to_i128, i128);
test_de_fails_with!(test_to_u8, u8);
test_de_fails_with!(test_to_u16, u16);
test_de_fails_with!(test_to_u32, u32);
test_de_fails_with!(test_to_u64, u64);
test_de_fails_with!(test_to_u128, u128);
test_de_fails_with!(test_to_f32, f32);
test_de_fails_with!(test_to_f64, f64);
test_de_fails_with!(test_to_bool, bool);
test_de_fails_with!(test_to_vec, Vec<String>);
test_de_fails_with!(test_to_string, String);
#[test]
fn test_basic_types_option_some() {
let test: TestControlFile = from_str(
"\
Package: something
Foo: Bar
True-False: yes
X-A-Number: 10
",
)
.unwrap();
assert_eq!(test.package, "something");
assert_eq!(test.foo, "Bar");
assert!(test.true_false);
assert_eq!(test.a_number, 10);
assert!(test.ello.is_none());
}
#[test]
fn test_basic_type_option_none() {
let test: TestControlFile = from_str(
"\
Package: something
Foo: Bar
True-False: yes
X-A-Number: 10
Ello: something
",
)
.unwrap();
assert_eq!(test.package, "something");
assert_eq!(test.foo, "Bar");
assert!(test.true_false);
assert_eq!(test.a_number, 10);
assert!(test.ello.is_some());
}
#[test]
fn test_reader() {
assert!(from_reader::<TestControlFile, _>(&mut BufReader::new(Cursor::new(""))).is_err());
assert!(
from_reader::<TestControlFile, _>(&mut BufReader::new(Cursor::new(
"
"
)))
.is_err()
);
}
#[test]
fn test_reader_multi() {
let mut reader = BufReader::new(Cursor::new(
"\
Package: somethingelse
Foo: Foo
True-False: no
X-A-Number: 100000
Package: sth
Foo: Foo1
True-False: yes
X-A-Number: 10000
Package: else
Foo: Foo2
True-False: no
X-A-Number: 1000
Ello: Govnr
",
));
let test: TestControlFile = from_reader(&mut reader).unwrap();
assert_eq!(test.package, "somethingelse");
assert_eq!(test.foo, "Foo");
assert!(!test.true_false);
assert_eq!(test.a_number, 100000);
assert!(test.ello.is_none());
let test: TestControlFile = from_reader(&mut reader).unwrap();
assert_eq!(test.package, "sth");
assert_eq!(test.foo, "Foo1");
assert!(test.true_false);
assert_eq!(test.a_number, 10000);
assert!(test.ello.is_none());
let test: TestControlFile = from_reader(&mut reader).unwrap();
assert_eq!(test.package, "else");
assert_eq!(test.foo, "Foo2");
assert!(!test.true_false);
assert_eq!(test.a_number, 1000);
assert!(test.ello.is_some());
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct TestControlSecond {
#[serde(rename = "Neato")]
neato: String,
}
#[test]
fn test_reader_mixed() {
let mut reader = BufReader::new(Cursor::new(
"\
Package: somethingelse
Foo: Foo
True-False: no
X-A-Number: 100000
Neato: Here
",
));
let test: TestControlFile = from_reader(&mut reader).unwrap();
assert_eq!(test.package, "somethingelse");
assert_eq!(test.foo, "Foo");
assert!(!test.true_false);
assert_eq!(test.a_number, 100000);
assert!(test.ello.is_none());
let test: TestControlSecond = from_reader(&mut reader).unwrap();
assert_eq!(test.neato, "Here");
}
#[test]
fn test_nested_flatten() {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Inner {
#[serde(rename = "Testing")]
testing: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Outer {
#[serde(flatten)]
inner: Inner,
}
let test: Outer = from_str(
"\
Testing: One
",
)
.unwrap();
assert_eq!("One", test.inner.testing);
}
#[test]
fn test_nested_breaks() {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Inner {
#[serde(rename = "Deep")]
deep: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Outer {
#[serde(rename = "Inner")]
inner: Inner,
}
assert!(matches!(
from_str::<Outer>(
"\
Inner: One
Deep: 2
Testing2: 1
",
)
.err()
.unwrap(),
Error::BadType
))
}
#[test]
fn test_multiline() {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Multiline {
#[serde(rename = "Multiline")]
multiline: Vec<String>,
}
let ml: Multiline = from_str(
"\
Multiline:
Something
Here
And
Here
",
)
.unwrap();
assert_eq!(4, ml.multiline.len());
}
#[test]
fn test_multiline_with_initial_line() {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Multiline {
#[serde(rename = "Multiline")]
multiline: Vec<String>,
}
let ml: Multiline = from_str(
"\
Multiline: Oh hello there!
Something
Here
And
Here
",
)
.unwrap();
assert_eq!(4, ml.multiline.len());
}
#[test]
fn test_multiline_custom() {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Multiline {
#[serde(rename = "Multiline")]
multiline: Vec<Version>,
}
let ml: Multiline = from_str(
"\
Multiline:
1.0
1.1
1.2
1.3
1.4
",
)
.unwrap();
assert_eq!(
vec!["1.0", "1.1", "1.2", "1.3", "1.4",]
.into_iter()
.map(|v| v.parse::<Version>().unwrap())
.collect::<Vec<_>>(),
ml.multiline,
)
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct TestControl {
#[serde(rename = "Hello")]
hello: String,
}
#[test]
fn test_from_reader() {
let mut reader = BufReader::new(Cursor::new(
"\
Hello: World
",
));
let test: TestControl = from_reader(&mut reader).unwrap();
assert_eq!(test.hello, "World");
}
#[test]
fn test_from_reader_iter() {
let mut reader = BufReader::new(Cursor::new(
"\
Hello: World
Hello: Paul
Hello: You
Hello: Me
",
));
let values = from_reader_iter::<TestControl, _>(&mut reader)
.map(|v| v.unwrap().hello)
.collect::<Vec<_>>();
assert_eq!(vec!["World", "Paul", "You", "Me"], values);
}
}