1use super::*;
6use crate::parsing::text_query::{parse_expr, Token, TokenIterator};
7use serde_derive::{Deserialize, Serialize};
8use std::convert::TryFrom;
9use std::ops::Deref;
10use std::rc::Rc;
11
12#[derive(Debug, Clone)]
13#[cfg_attr(test, derive(PartialEq))]
14pub enum DatePattern {
15 Literal(String),
16 Match(String),
17 Optional(Vec<DatePattern>),
18 Dash,
19 Colon,
20 Space,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
24#[serde(into = "String", try_from = "String")]
25pub struct ExprString(pub Expr);
26
27impl From<ExprString> for String {
28 fn from(value: ExprString) -> String {
29 format!("{}", value.0)
30 }
31}
32
33impl TryFrom<String> for ExprString {
34 type Error = String;
35
36 fn try_from(input: String) -> Result<Self, Self::Error> {
37 let mut iter = TokenIterator::new(&input).peekable();
38 let expr = parse_expr(&mut iter);
39 if let Some(Token::Eof) = iter.next() {
40 Ok(ExprString(expr))
41 } else {
42 Err("Expected EOF".to_owned())
43 }
44 }
45}
46
47impl Deref for ExprString {
48 type Target = Expr;
49
50 fn deref(&self) -> &Expr {
51 &self.0
52 }
53}
54
55#[derive(Debug, Serialize, Deserialize)]
56#[serde(rename_all = "camelCase")]
57pub struct Property {
58 pub name: String,
59 pub input: ExprString,
60 pub input_name: String,
61 pub output: ExprString,
62 pub output_name: String,
63 pub doc: Option<String>,
64}
65
66#[derive(Debug, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase")]
68#[serde(tag = "type")]
69pub enum Def {
70 BaseUnit {
71 #[serde(rename = "longName")]
72 long_name: Option<String>,
73 },
74 Prefix {
75 expr: ExprString,
76 #[serde(rename = "isLong")]
77 is_long: bool,
78 },
79 Unit {
80 expr: ExprString,
81 },
82 Quantity {
83 expr: ExprString,
84 },
85 Substance {
86 symbol: Option<String>,
87 properties: Vec<Property>,
88 },
89 Category {
90 #[serde(rename = "displayName")]
91 display_name: String,
92 },
93 Error {
94 message: String,
95 },
96}
97
98#[derive(Debug, Serialize, Deserialize)]
99pub struct DefEntry {
100 pub name: String,
101 #[serde(flatten)]
102 pub def: Rc<Def>,
103 pub doc: Option<String>,
104 pub category: Option<String>,
105}
106
107impl DefEntry {
108 pub fn new(name: &str, doc: Option<&str>, category: Option<&str>, def: Def) -> DefEntry {
109 DefEntry {
110 name: name.into(),
111 doc: doc.map(Into::into),
112 category: category.map(Into::into),
113 def: Rc::new(def),
114 }
115 }
116
117 pub fn new_unit(name: &str, doc: Option<&str>, category: Option<&str>, expr: Expr) -> DefEntry {
118 Self::new(
119 name,
120 doc,
121 category,
122 Def::Unit {
123 expr: ExprString(expr),
124 },
125 )
126 }
127}
128
129#[derive(Debug, Serialize, Deserialize)]
130#[serde(transparent)]
131pub struct Defs {
132 pub defs: Vec<DefEntry>,
133}
134
135impl fmt::Display for DatePattern {
136 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match *self {
138 DatePattern::Literal(ref l) => write!(fmt, "'{}'", l),
139 DatePattern::Match(ref n) => write!(fmt, "{}", n),
140 DatePattern::Optional(ref pats) => {
141 write!(fmt, "[")?;
142 for p in pats {
143 p.fmt(fmt)?;
144 }
145 write!(fmt, "]")
146 }
147 DatePattern::Dash => write!(fmt, "-"),
148 DatePattern::Colon => write!(fmt, ":"),
149 DatePattern::Space => write!(fmt, " "),
150 }
151 }
152}
153
154impl DatePattern {
155 pub fn show(pat: &[DatePattern]) -> String {
156 use std::io::Write;
157
158 let mut buf = vec![];
159 for p in pat {
160 write!(buf, "{}", p).unwrap();
161 }
162 String::from_utf8(buf).unwrap()
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::{DatePattern, ExprString};
169 use crate::ast::Expr;
170 use std::convert::TryFrom;
171
172 #[test]
173 fn test_try_from() {
174 assert_eq!(
175 ExprString::try_from("abc".to_owned()),
176 Ok(ExprString(Expr::new_unit("abc".to_owned())))
177 );
178 assert_eq!(
179 ExprString::try_from("".to_owned()),
180 Ok(ExprString(Expr::new_error(
181 "Expected term, got eof".to_owned()
182 )))
183 );
184 assert_eq!(
185 ExprString::try_from("a -> ->".to_owned()),
186 Err("Expected EOF".to_owned())
187 )
188 }
189
190 #[test]
191 fn date_pattern_fmt() {
192 assert_eq!(format!("{}", DatePattern::Dash), "-");
193 assert_eq!(format!("{}", DatePattern::Colon), ":");
194 assert_eq!(format!("{}", DatePattern::Space), " ");
195 assert_eq!(format!("{}", DatePattern::Literal("a".to_owned())), "'a'");
196 assert_eq!(format!("{}", DatePattern::Match("a".to_owned())), "a");
197
198 assert_eq!(
199 format!(
200 "{}",
201 DatePattern::Optional(vec![DatePattern::Literal("a".to_owned())])
202 ),
203 "['a']"
204 );
205 }
206}