-
Notifications
You must be signed in to change notification settings - Fork 0
/
inline-maps.rs
102 lines (90 loc) · 2.98 KB
/
inline-maps.rs
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
//! IDM can't do inline map values out of the box, let's fix that!
use std::collections::BTreeMap;
use idm::{from_str, to_string, AsVec, UnderlineSpaces};
use serde::{
de::{self, Deserialize, DeserializeOwned},
ser::{self, Serialize},
};
/// Wrapper that merges two word-like elements into a single word-like element
/// with a connecting colon.
///
/// ```
/// use idm::ColonPair;
///
/// assert_eq!(
/// idm::from_str::<ColonPair<String, String>>("a:b").unwrap(),
/// ColonPair("a".to_string(), "b".to_string())
/// );
/// assert_eq!(
/// idm::to_string(&ColonPair("x".to_string(), "y".to_string())).unwrap(),
/// "x:y"
/// );
/// ```
#[derive(Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct ColonPair<T, U>(pub T, pub U);
impl<T, U> ColonPair<T, U> {}
impl<'de, T: DeserializeOwned, U: DeserializeOwned> Deserialize<'de>
for ColonPair<T, U>
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let ret = String::deserialize(deserializer)?;
if let Some((head, tail)) = ret.split_once(':') {
let head = from_str(head).map_err(de::Error::custom)?;
let tail = from_str(tail).map_err(de::Error::custom)?;
Ok(ColonPair(head, tail))
} else {
Err(de::Error::custom("No colon found in value"))
}
}
}
impl<T: Serialize, U: Serialize> Serialize for ColonPair<T, U> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let head = to_string(&self.0).map_err(ser::Error::custom)?;
let tail = to_string(&self.1).map_err(ser::Error::custom)?;
if head.chars().any(|c| idm::is_whitespace(c) || c == ':')
|| tail.chars().any(idm::is_whitespace)
{
return Err(ser::Error::custom(
"ColonPair: halves are not words or head contains a colon",
));
}
format!("{}:{}", head, tail).serialize(serializer)
}
}
impl<T, U> From<(T, U)> for ColonPair<T, U> {
fn from((t, u): (T, U)) -> Self {
ColonPair(t, u)
}
}
#[allow(clippy::from_over_into)]
impl<T, U> Into<(T, U)> for ColonPair<T, U> {
fn into(self) -> (T, U) {
(self.0, self.1)
}
}
fn main() {
const BIBLIOGRAPHY: &str = "\
title:Structure_and_Interpretation_of_Computer_Programs isbn:0262010771
title:A_Mathematical_Theory_of_Communication doi:10.1002/j.1538-7305.1948.tb01338.x\n";
type InlineMap = AsVec<
BTreeMap<String, UnderlineSpaces<String>>,
(String, UnderlineSpaces<String>),
ColonPair<String, UnderlineSpaces<String>>,
>;
let library = idm::from_str::<Vec<InlineMap>>(BIBLIOGRAPHY).unwrap();
for work in &library {
println!("{}", *work["title"]);
for (key, value) in &**work {
if key == "title" {
continue;
}
println!(" :{} {}", key, **value);
}
}
}