1use 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 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 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}