Skip to main content

rink_core/output/
reply.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::fmt::{flat_join, join, Span, TokenFmt};
6use super::{DocString, NumberParts};
7use crate::ast::{Expr, Precedence, UnaryOpType};
8use crate::output::Digits;
9use crate::runtime::MissingDeps;
10use crate::types::DateTime;
11use serde_derive::Serialize;
12use std::collections::BTreeMap;
13use std::fmt::{Display, Formatter, Result as FmtResult};
14use std::iter::once;
15use std::rc::Rc;
16
17#[derive(Debug, Clone, Serialize)]
18#[serde(tag = "type")]
19#[serde(rename_all = "lowercase")]
20pub enum ExprParts {
21    Literal {
22        text: String,
23    },
24    Unit {
25        name: String,
26    },
27    Property {
28        property: String,
29        subject: Vec<ExprParts>,
30    },
31    Error {
32        message: String,
33    },
34}
35
36#[derive(Debug, Clone, Serialize)]
37pub struct ExprReply {
38    exprs: Vec<ExprParts>,
39    ast: Expr,
40}
41
42#[derive(Debug, Clone, Serialize)]
43#[serde(rename_all = "camelCase")]
44pub struct DefReply {
45    pub canon_name: String,
46    pub def: Option<String>,
47    pub def_expr: Option<ExprReply>,
48    pub value: Option<NumberParts>,
49    pub doc: Option<DocString>,
50}
51
52#[derive(Debug, Clone, Serialize)]
53pub struct ConversionReply {
54    pub value: NumberParts,
55}
56
57#[derive(Debug, Clone, Serialize)]
58pub struct FactorizeReply {
59    pub factorizations: Vec<Factorization>,
60}
61
62#[derive(Debug, Clone, Serialize)]
63#[serde(transparent)]
64pub struct Factorization {
65    pub units: BTreeMap<Rc<String>, usize>,
66}
67
68#[derive(Debug, Clone, Serialize)]
69pub struct UnitsInCategory {
70    pub category: Option<String>,
71    pub units: Vec<String>,
72}
73
74#[derive(Debug, Clone, Serialize)]
75pub struct UnitsForReply {
76    pub units: Vec<UnitsInCategory>,
77    /// Dimensions and quantity are set.
78    pub of: NumberParts,
79}
80
81#[derive(Debug, Clone, Serialize)]
82pub struct UnitListReply {
83    pub rest: NumberParts,
84    pub list: Vec<NumberParts>,
85}
86
87#[derive(Debug, Clone, Serialize)]
88pub struct DurationReply {
89    pub raw: NumberParts,
90    pub years: NumberParts,
91    pub months: NumberParts,
92    pub weeks: NumberParts,
93    pub days: NumberParts,
94    pub hours: NumberParts,
95    pub minutes: NumberParts,
96    pub seconds: NumberParts,
97}
98
99#[derive(Debug, Clone, Serialize)]
100pub struct SearchReply {
101    pub results: Vec<NumberParts>,
102}
103
104#[derive(Debug, Clone, Serialize)]
105pub struct PropertyReply {
106    pub name: String,
107    pub value: NumberParts,
108    pub doc: Option<DocString>,
109}
110
111#[derive(Debug, Clone, Serialize)]
112pub struct SubstanceReply {
113    pub name: String,
114    pub doc: Option<DocString>,
115    pub amount: NumberParts,
116    pub properties: Vec<PropertyReply>,
117}
118
119#[derive(Debug, Clone, Serialize)]
120pub struct DateReply {
121    pub year: i32,
122    pub month: i32,
123    pub day: i32,
124    pub hour: i32,
125    pub minute: i32,
126    pub second: i32,
127    pub nanosecond: i32,
128    /// chrono-humanize output, if enabled.
129    pub human: Option<String>,
130    pub string: String,
131    pub rfc3339: String,
132}
133
134#[derive(Debug, Clone, Serialize)]
135#[allow(clippy::large_enum_variant)]
136#[serde(rename_all = "camelCase")]
137#[serde(tag = "type")]
138pub enum QueryReply {
139    Number(NumberParts),
140    Date(DateReply),
141    Substance(SubstanceReply),
142    Duration(Box<DurationReply>),
143    Def(Box<DefReply>),
144    Conversion(Box<ConversionReply>),
145    Factorize(FactorizeReply),
146    UnitsFor(UnitsForReply),
147    UnitList(UnitListReply),
148    Search(SearchReply),
149}
150
151#[derive(Debug, Clone, Serialize)]
152pub struct ConformanceError {
153    pub left: NumberParts,
154    pub right: NumberParts,
155    pub suggestions: Vec<String>,
156}
157
158#[derive(Debug, Clone, Serialize)]
159pub struct NotFoundError {
160    pub got: String,
161    pub suggestion: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize)]
165#[serde(tag = "type")]
166#[serde(rename_all = "camelCase")]
167pub enum QueryError {
168    Conformance(Box<ConformanceError>),
169    NotFound(NotFoundError),
170    MissingDeps(MissingDeps),
171    Generic { message: String },
172}
173
174impl std::error::Error for QueryError {}
175
176impl QueryError {
177    pub fn generic(message: String) -> QueryError {
178        QueryError::Generic { message }
179    }
180}
181
182impl ExprReply {
183    pub fn from(expr: &Expr) -> ExprReply {
184        let mut parts = vec![];
185
186        fn recurse(expr: &Expr, parts: &mut Vec<ExprParts>, prec: Precedence) {
187            macro_rules! literal {
188                ($e:expr) => {{
189                    parts.push(ExprParts::Literal {
190                        text: $e.to_owned(),
191                    })
192                }};
193            }
194            match *expr {
195                Expr::Unit { ref name } => parts.push(ExprParts::Unit { name: name.clone() }),
196                Expr::Quote { ref string } => literal!(format!("'{}'", string)),
197                Expr::Const { ref value } => {
198                    let (_exact, val) = value.to_string(10, Digits::Default);
199                    literal!(val)
200                }
201                Expr::Date { .. } => literal!("NYI: date expr to expr parts"),
202                Expr::Mul { ref exprs } => {
203                    if prec < Precedence::Mul {
204                        literal!("(");
205                    }
206                    for expr in exprs.iter() {
207                        recurse(expr, parts, Precedence::Pow);
208                    }
209                    if prec < Precedence::Mul {
210                        literal!(")");
211                    }
212                }
213                Expr::Call { ref func, ref args } => {
214                    literal!(format!("{}(", func.name()));
215                    if let Some(first) = args.first() {
216                        recurse(first, parts, Precedence::Equals);
217                    }
218                    for arg in args.iter().skip(1) {
219                        literal!(",");
220                        recurse(arg, parts, Precedence::Equals);
221                    }
222                    literal!(")")
223                }
224                Expr::BinOp(ref binop) => {
225                    let op_prec = Precedence::from(binop.op);
226                    if prec < op_prec {
227                        literal!("(");
228                    }
229                    recurse(&binop.left, parts, Precedence::left(binop.op));
230                    literal!(binop.op.symbol());
231                    recurse(&binop.right, parts, Precedence::right(binop.op));
232                    if prec < op_prec {
233                        literal!(")");
234                    }
235                }
236                Expr::UnaryOp(ref unaryop) => match unaryop.op {
237                    UnaryOpType::Positive => {
238                        literal!("+");
239                        recurse(&unaryop.expr, parts, Precedence::Plus)
240                    }
241                    UnaryOpType::Negative => {
242                        literal!("-");
243                        recurse(&unaryop.expr, parts, Precedence::Plus)
244                    }
245                    UnaryOpType::Degree(ref suffix) => {
246                        if prec < Precedence::Mul {
247                            literal!("(");
248                        }
249                        recurse(&unaryop.expr, parts, Precedence::Mul);
250                        literal!(suffix.to_string());
251                        if prec < Precedence::Mul {
252                            literal!(")");
253                        }
254                    }
255                },
256                Expr::Of {
257                    ref property,
258                    ref expr,
259                } => {
260                    if prec < Precedence::Add {
261                        literal!("(");
262                    }
263                    let mut sub = vec![];
264                    recurse(expr, &mut sub, Precedence::Div);
265                    parts.push(ExprParts::Property {
266                        property: property.to_owned(),
267                        subject: sub,
268                    });
269                    if prec < Precedence::Add {
270                        literal!(")");
271                    }
272                }
273                Expr::Error { ref message } => parts.push(ExprParts::Error {
274                    message: message.to_owned(),
275                }),
276            }
277        }
278
279        recurse(expr, &mut parts, Precedence::Equals);
280
281        ExprReply {
282            exprs: parts,
283            ast: expr.clone(),
284        }
285    }
286}
287
288impl From<NotFoundError> for QueryError {
289    fn from(v: NotFoundError) -> Self {
290        QueryError::NotFound(v)
291    }
292}
293
294impl From<String> for QueryError {
295    fn from(message: String) -> Self {
296        QueryError::Generic { message }
297    }
298}
299
300impl Display for QueryReply {
301    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
302        self.display(fmt)
303    }
304}
305
306impl Display for QueryError {
307    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
308        self.display(fmt)
309    }
310}
311
312impl Display for NotFoundError {
313    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
314        self.display(fmt)
315    }
316}
317
318impl Display for ConformanceError {
319    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
320        self.display(fmt)
321    }
322}
323
324impl DateReply {
325    pub fn new(ctx: &crate::loader::Context, date: DateTime) -> DateReply {
326        DateReply {
327            string: date.to_string(),
328            rfc3339: date.to_rfc3339(),
329            year: date.year(),
330            month: date.month() as i32,
331            day: date.day() as i32,
332            hour: date.hour() as i32,
333            minute: date.minute() as i32,
334            second: date.second() as i32,
335            nanosecond: date.nanosecond() as i32,
336            human: ctx.humanize(&date),
337        }
338    }
339}
340
341impl Display for DateReply {
342    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
343        self.display(fmt)
344    }
345}
346
347impl Display for SubstanceReply {
348    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
349        self.display(fmt)
350    }
351}
352
353impl Display for DefReply {
354    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
355        self.display(fmt)
356    }
357}
358
359impl Display for ConversionReply {
360    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
361        self.display(fmt)
362    }
363}
364
365impl Display for FactorizeReply {
366    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
367        self.display(fmt)
368    }
369}
370
371impl Display for UnitsForReply {
372    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
373        self.display(fmt)
374    }
375}
376
377impl Display for DurationReply {
378    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
379        self.display(fmt)
380    }
381}
382
383impl Display for UnitListReply {
384    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
385        self.display(fmt)
386    }
387}
388
389impl Display for SearchReply {
390    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
391        self.display(fmt)
392    }
393}
394
395impl<'a> TokenFmt<'a> for QueryReply {
396    fn to_spans(&'a self) -> Vec<Span<'a>> {
397        match self {
398            QueryReply::Number(reply) => reply.to_spans(),
399            QueryReply::Date(reply) => reply.to_spans(),
400            QueryReply::Substance(reply) => reply.to_spans(),
401            QueryReply::Duration(reply) => reply.to_spans(),
402            QueryReply::Def(reply) => reply.to_spans(),
403            QueryReply::Conversion(reply) => reply.to_spans(),
404            QueryReply::Factorize(reply) => reply.to_spans(),
405            QueryReply::UnitsFor(reply) => reply.to_spans(),
406            QueryReply::UnitList(reply) => reply.to_spans(),
407            QueryReply::Search(reply) => reply.to_spans(),
408        }
409    }
410}
411
412impl<'a> TokenFmt<'a> for DateReply {
413    fn to_spans(&'a self) -> Vec<Span<'a>> {
414        if let Some(ref human) = self.human {
415            vec![
416                Span::date_time(&self.string),
417                Span::plain(" ("),
418                Span::plain(human),
419                Span::plain(")"),
420            ]
421        } else {
422            vec![Span::date_time(&self.string)]
423        }
424    }
425}
426
427impl<'a> TokenFmt<'a> for SubstanceReply {
428    fn to_spans(&'a self) -> Vec<Span<'a>> {
429        let mut tokens = vec![Span::unit(&self.name), Span::plain(": ")];
430        if let Some(ref doc) = self.doc {
431            tokens.push(Span::child(doc));
432            tokens.push(Span::plain(" "));
433        }
434        tokens.push(Span::list_begin(""));
435        tokens.extend(join(
436            self.properties.iter().map(|prop| Span::child(prop)),
437            Span::list_sep("; "),
438        ));
439        tokens
440    }
441}
442
443impl<'a> TokenFmt<'a> for PropertyReply {
444    fn to_spans(&'a self) -> Vec<Span<'a>> {
445        let mut tokens = vec![
446            Span::prop_name(&self.name),
447            Span::plain(" = "),
448            Span::child(&self.value),
449        ];
450        if let Some(ref doc) = self.doc {
451            tokens.push(Span::plain(". "));
452            tokens.push(Span::child(doc));
453        }
454        tokens
455    }
456}
457
458impl<'a> TokenFmt<'a> for DurationReply {
459    fn to_spans(&'a self) -> Vec<Span<'a>> {
460        let parts = [
461            &self.years,
462            &self.months,
463            &self.weeks,
464            &self.days,
465            &self.hours,
466            &self.minutes,
467        ];
468
469        let res = join(
470            parts
471                .iter()
472                .copied()
473                .filter(|x| x.exact_value.as_ref().map(|x| &**x) != Some("0"))
474                .chain(once(&self.seconds))
475                .map(|x| Span::child(x)),
476            Span::plain(", "),
477        );
478
479        if let Some(ref q) = self.raw.quantity {
480            res.chain(once(Span::plain(" (")))
481                .chain(once(Span::quantity(q)))
482                .chain(once(Span::plain(")")))
483                .collect()
484        } else {
485            res.collect()
486        }
487    }
488}
489
490impl<'a> TokenFmt<'a> for DefReply {
491    fn to_spans(&'a self) -> Vec<Span<'a>> {
492        let mut tokens = vec![Span::plain("Definition: "), Span::unit(&self.canon_name)];
493        if let Some(ref def) = self.def {
494            tokens.push(Span::plain(" = "));
495            tokens.push(Span::plain(def));
496        }
497        if let Some(ref value) = self.value {
498            tokens.push(Span::plain(" = "));
499            tokens.extend(value.token_format("n u p").to_spans());
500        }
501        if let Some(ref doc) = self.doc {
502            tokens.push(Span::plain(". "));
503            tokens.push(Span::child(doc));
504        }
505        tokens
506    }
507}
508
509impl<'a> TokenFmt<'a> for ConversionReply {
510    fn to_spans(&'a self) -> Vec<Span<'a>> {
511        self.value.to_spans()
512    }
513}
514
515impl<'a> TokenFmt<'a> for FactorizeReply {
516    fn to_spans(&'a self) -> Vec<Span<'a>> {
517        once(Span::list_begin("Factorizations: "))
518            .chain(join(
519                self.factorizations.iter().map(|fac| Span::child(fac)),
520                Span::list_sep("; "),
521            ))
522            .collect()
523    }
524}
525
526impl<'a> TokenFmt<'a> for Factorization {
527    fn to_spans(&'a self) -> Vec<Span<'a>> {
528        flat_join(
529            self.units.iter().map(|(unit, pow)| {
530                if *pow == 1 {
531                    vec![Span::unit(&**unit)]
532                } else {
533                    vec![Span::unit(&**unit), Span::pow(format!("^{}", pow))]
534                }
535            }),
536            Span::plain(" "),
537        )
538        .collect()
539    }
540}
541
542impl<'a> TokenFmt<'a> for UnitsForReply {
543    fn to_spans(&'a self) -> Vec<Span<'a>> {
544        let mut tokens = vec![Span::plain("Units for ")];
545        tokens.extend(self.of.token_format("D w").to_spans());
546        tokens.push(Span::list_begin(": "));
547        tokens.extend(join(
548            self.units.iter().map(|cat| Span::child(cat)),
549            Span::list_sep("; "),
550        ));
551
552        tokens
553    }
554}
555
556impl<'a> TokenFmt<'a> for UnitsInCategory {
557    fn to_spans(&'a self) -> Vec<Span<'a>> {
558        let mut tokens = vec![
559            if let Some(ref cat) = self.category {
560                Span::plain(cat)
561            } else {
562                Span::plain("Uncategorized")
563            },
564            Span::list_begin(": "),
565        ];
566        tokens.extend(join(
567            self.units.iter().map(Span::unit),
568            Span::list_sep(", "),
569        ));
570        tokens
571    }
572}
573
574impl<'a> TokenFmt<'a> for UnitListReply {
575    fn to_spans(&'a self) -> Vec<Span<'a>> {
576        let mut tokens = vec![Span::list_begin("")];
577        tokens.extend(join(
578            self.list.iter().map(|num| Span::child(num)),
579            Span::list_sep(", "),
580        ));
581        if let Some(ref quantity) = self.rest.quantity {
582            tokens.push(Span::plain(" ("));
583            tokens.push(Span::quantity(quantity));
584            tokens.push(Span::plain(")"));
585        }
586        tokens
587    }
588}
589
590impl<'a> TokenFmt<'a> for SearchReply {
591    fn to_spans(&'a self) -> Vec<Span<'a>> {
592        once(Span::list_begin("Search results: "))
593            .chain(flat_join(
594                self.results
595                    .iter()
596                    .map(|x| x.token_format("u p").to_spans()),
597                Span::list_sep(", "),
598            ))
599            .collect()
600    }
601}
602
603impl<'a> TokenFmt<'a> for MissingDeps {
604    fn to_spans(&'a self) -> Vec<Span<'a>> {
605        once(Span::list_begin("Missing dependencies: "))
606            .chain(join(
607                self.needed().iter().map(|x| Span::unit(x)),
608                Span::list_sep(", "),
609            ))
610            .collect()
611    }
612}
613
614impl<'a> TokenFmt<'a> for QueryError {
615    fn to_spans(&'a self) -> Vec<Span<'a>> {
616        match self {
617            QueryError::Conformance(err) => err.to_spans(),
618            QueryError::NotFound(err) => err.to_spans(),
619            QueryError::MissingDeps(err) => err.to_spans(),
620            QueryError::Generic { message } => vec![Span::plain(message)],
621        }
622    }
623}
624
625impl<'a> TokenFmt<'a> for ConformanceError {
626    fn to_spans(&'a self) -> Vec<Span<'a>> {
627        let mut tokens = vec![
628            Span::error("Conformance error: "),
629            Span::child(&self.left),
630            Span::plain(" != "),
631            Span::child(&self.right),
632            Span::list_begin("\nSuggestions: "),
633        ];
634        tokens.extend(join(
635            self.suggestions.iter().map(Span::plain),
636            Span::list_sep(", "),
637        ));
638        tokens
639    }
640}
641
642impl<'a> TokenFmt<'a> for NotFoundError {
643    fn to_spans(&'a self) -> Vec<Span<'a>> {
644        let mut tokens = vec![Span::error("No such unit "), Span::user_input(&self.got)];
645        if let Some(ref suggestion) = self.suggestion {
646            tokens.push(Span::error(", did you mean "));
647            tokens.push(Span::unit(suggestion));
648            tokens.push(Span::error("?"));
649        }
650        tokens
651    }
652}
653
654impl<'a> TokenFmt<'a> for Result<QueryReply, QueryError> {
655    fn to_spans(&'a self) -> Vec<Span<'a>> {
656        match self {
657            Ok(res) => res.to_spans(),
658            Err(err) => err.to_spans(),
659        }
660    }
661}