1use std::str::FromStr;
2
3use anyhow::{Error, bail, format_err};
4use linearize::Linearize;
5use regex::Regex;
6use serde_derive::Serialize;
7
8use crate::url::Url;
9
10#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
11pub enum EnumeratedListType {
12 Arabic,
13 LowerAlpha,
14 UpperAlpha,
15 LowerRoman,
16 UpperRoman,
17}
18
19#[derive(Linearize, Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
20pub enum FootnoteType {
21 Number,
22 Symbol,
23}
24
25impl TryFrom<char> for FootnoteType {
26 type Error = ();
27
28 fn try_from(c: char) -> Result<Self, Self::Error> {
29 match c {
30 '#' => Ok(FootnoteType::Number),
31 '*' => Ok(FootnoteType::Symbol),
32 _ => Err(()),
33 }
34 }
35}
36
37#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
38pub enum FixedSpace {
39 Default,
40 #[default]
42 Preserve,
43}
44
45#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
46pub enum AlignH {
47 Left,
48 Center,
49 Right,
50}
51#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
52pub enum AlignHV {
53 Top,
54 Middle,
55 Bottom,
56 Left,
57 Center,
58 Right,
59}
60#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
61pub enum AlignV {
62 Top,
63 Middle,
64 Bottom,
65}
66
67#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
68pub enum TableAlignH {
69 Left,
70 Right,
71 Center,
72 Justify,
73 Char,
74}
75#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
76pub enum TableBorder {
77 Top,
78 Bottom,
79 TopBottom,
80 All,
81 Sides,
82 None,
83}
84
85#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
86pub struct ID(pub String);
87#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
88pub struct NameToken(pub String);
89
90#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)]
94pub struct TableGroupCols(pub usize);
95
96#[derive(Debug, PartialEq, Serialize, Clone)]
98pub enum Measure {
99 Em(f64),
101 Ex(f64),
102 Mm(f64),
103 Cm(f64),
104 In(f64),
105 Px(f64),
106 Pt(f64),
107 Pc(f64),
108}
109
110impl FromStr for AlignHV {
111 type Err = Error;
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 use self::AlignHV as A;
114 Ok(match s {
115 "top" => A::Top,
116 "middle" => A::Middle,
117 "bottom" => A::Bottom,
118 "left" => A::Left,
119 "center" => A::Center,
120 "right" => A::Right,
121 s => bail!("Invalid Alignment {}", s),
122 })
123 }
124}
125
126impl From<&str> for ID {
127 fn from(s: &str) -> Self {
128 ID(s.to_owned().replace(' ', "-"))
129 }
130}
131
132impl From<&str> for NameToken {
133 fn from(s: &str) -> Self {
134 NameToken(s.to_owned())
135 }
136}
137
138impl FromStr for Measure {
139 type Err = Error;
140 fn from_str(s: &str) -> Result<Self, Self::Err> {
141 use self::Measure as M;
142 let re =
143 Regex::new(r"(?P<float>\d+\.\d*|\.?\d+)\s*(?P<unit>em|ex|mm|cm|in|px|pt|pc)").unwrap();
144 let caps: regex::Captures = re
145 .captures(s)
146 .ok_or_else(|| format_err!("Invalid measure"))?;
147 let value: f64 = caps["float"].parse()?;
148 Ok(match &caps["unit"] {
149 "em" => M::Em(value),
150 "ex" => M::Ex(value),
151 "mm" => M::Mm(value),
152 "cm" => M::Cm(value),
153 "in" => M::In(value),
154 "px" => M::Px(value),
155 "pt" => M::Pt(value),
156 "pc" => M::Pc(value),
157 _ => unreachable!(),
158 })
159 }
160}
161
162#[cfg(test)]
163mod parse_tests {
164 use super::*;
165
166 #[test]
167 fn measure() {
168 let _a: Measure = "1.5em".parse().unwrap();
169 let _b: Measure = "20 mm".parse().unwrap();
170 let _c: Measure = ".5in".parse().unwrap();
171 let _d: Measure = "1.pc".parse().unwrap();
172 }
173}
174
175pub(crate) trait CanBeEmpty {
176 fn is_empty(&self) -> bool;
177}
178
179macro_rules! impl_cannot_be_empty {
185 ($t:ty) => {
186 impl CanBeEmpty for $t {
187 fn is_empty(&self) -> bool { false }
188 }
189 };
190 ($t:ty, $($ts:ty),*) => {
191 impl_cannot_be_empty!($t);
192 impl_cannot_be_empty!($($ts),*);
193 };
194}
195impl_cannot_be_empty!(Url);
196impl_cannot_be_empty!(TableGroupCols);
197
198impl<T> CanBeEmpty for Option<T> {
199 fn is_empty(&self) -> bool {
200 self.is_none()
201 }
202}
203
204impl<T> CanBeEmpty for Vec<T> {
205 fn is_empty(&self) -> bool {
206 self.is_empty()
207 }
208}
209
210impl CanBeEmpty for bool {
211 fn is_empty(&self) -> bool {
212 !self
213 }
214}
215
216impl CanBeEmpty for FixedSpace {
217 fn is_empty(&self) -> bool {
218 self == &FixedSpace::default()
219 }
220}