rink_core/ast/
def.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use 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}