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