1use crate::{
8 aim::{Typedef, WriterLike, Writer},
9 literals::{parse_date, parse_number, parse_text},
10};
11use anyhow::{Result, anyhow};
12use jiff::civil::DateTime;
13use nom::{
14 IResult, Parser,
15 branch::alt,
16 bytes::complete::tag,
17 character::complete::{char, multispace0},
18 combinator::{map, opt, value}, sequence::delimited,
19};
20use std::cmp::Ordering;
21use std::{
22 fmt,
23 sync::Arc,
24};
25
26#[derive(Debug, Clone, PartialEq)]
31pub enum Literal {
32 Empty,
34 Bool(bool),
36 Date(DateTime),
38 Number(f64),
40 Task(Option<bool>, Arc<str>),
43 Text(Arc<str>),
45}
46
47impl Literal {
48 pub fn is_type(&self, typedef: &Typedef) -> bool {
58 match self {
59 Literal::Empty => false,
60 Literal::Bool(_) => typedef.is_bool() | typedef.is_any(),
61 Literal::Date(_) => typedef.is_date() | typedef.is_any(),
62 Literal::Number(_) => typedef.is_number() | typedef.is_any(),
63 Literal::Task(..) => typedef.is_task() | typedef.is_any(),
64 Literal::Text(_) => typedef.is_text() | typedef.is_any(),
65 }
66 }
67
68 pub fn get_type(&self) -> Result<Typedef> {
75 match self {
76 Literal::Empty => Err(anyhow!("Expecting type, found Empty")),
77 Literal::Bool(_) => Ok(Typedef::Bool),
78 Literal::Date(_) => Ok(Typedef::Date),
79 Literal::Number(_) => Ok(Typedef::Number),
80 Literal::Task(..) => Ok(Typedef::Task),
81 Literal::Text(_) => Ok(Typedef::Text),
82 }
83 }
84
85 pub fn as_type(self, literal: &Literal) -> Result<Literal> {
100 match literal {
101 Literal::Empty => Err(anyhow!(
102 "Expecting type as {}, found Empty",
103 literal.type_as_string()
104 )),
105 Literal::Bool(_) => Ok(self.as_bool()),
106 Literal::Date(_) => self.as_date(),
107 Literal::Number(_) => self.as_number(),
108 Literal::Task(..) => self.as_task(),
109 Literal::Text(_) => self.as_text(),
110 }
111 }
112
113 pub fn to_type(self, typedef: &Typedef) -> Result<Literal> {
114 match typedef {
115 Typedef::Any => Ok(self),
116 Typedef::Bool => Ok(self.as_bool()),
117 Typedef::Date => self.as_date(),
118 Typedef::Number => self.as_number(),
119 Typedef::Task => self.as_task(),
120 Typedef::Text => self.as_text(),
121 _ => Err(anyhow!("Expecting literal type, found {}", typedef)),
122 }
123 }
124
125 pub fn is_empty(&self) -> bool {
127 matches!(self, Literal::Empty)
128 }
129
130 pub fn is_bool(&self) -> bool {
132 matches!(self, Literal::Bool(_))
133 }
134
135 pub fn from_bool(b: bool) -> Self {
137 Literal::Bool(b)
138 }
139
140 pub fn as_bool(self) -> Literal {
155 match self {
156 Literal::Empty => Literal::Bool(false),
157 Literal::Bool(_) => self,
158 Literal::Date(_) => {
159 if let Ok(number) = self.as_number() {
161 number.as_bool()
162 } else {
163 Literal::Bool(false)
164 }
165 }
166 Literal::Number(number) => Literal::Bool(number != 0.0),
167 Literal::Task(status, _) => match status {
168 Some(state) => Literal::Bool(state),
169 None => Literal::Bool(false),
170 },
171 Literal::Text(text) => Literal::Bool(!text.is_empty()),
172 }
173 }
174
175 pub fn to_bool(&self) -> bool {
179 match self {
180 Literal::Empty => false,
181 Literal::Bool(b) => *b,
182 Literal::Date(_) => {
183 if let Ok(number) = self.to_number() {
185 number != 0.0
186 } else {
187 false
188 }
189 }
190 Literal::Number(number) => *number != 0.0,
191 Literal::Task(status, _) => (*status).unwrap_or_default(),
192 Literal::Text(text) => !text.is_empty(),
193 }
194 }
195
196 pub fn is_date(&self) -> bool {
198 matches!(self, Literal::Date(_))
199 }
200
201 pub fn from_date(d: DateTime) -> Self {
203 Literal::Date(d)
204 }
205
206 pub fn as_date(self) -> Result<Literal> {
215 match self {
216 Literal::Empty => Err(anyhow!("Expecting type as Date, found Empty")),
217 Literal::Bool(state) => {
218 let timestamp = if state { 1i64 } else { 0i64 };
220 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
221 Ok(epoch) => {
222 match jiff::Span::new().try_seconds(timestamp) {
224 Ok(duration) => match epoch.checked_add(duration) {
225 Ok(new_dt) => Ok(Literal::Date(new_dt)),
226 Err(_) => Ok(Literal::Date(epoch)),
227 },
228 Err(_) => Ok(Literal::Date(epoch)),
229 }
230 }
231 Err(_) => Err(anyhow!("Failed to create Date from Bool")),
232 }
233 }
234 Literal::Date(_) => Ok(self),
235 Literal::Number(number) => {
236 let timestamp = number as i64;
238 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
239 Ok(epoch) => {
240 match jiff::Span::new().try_seconds(timestamp) {
242 Ok(duration) => match epoch.checked_add(duration) {
243 Ok(new_dt) => Ok(Literal::Date(new_dt)),
244 Err(_) => {
245 Err(anyhow!("Failed to create Date from Number({})", number))
246 }
247 },
248 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
249 }
250 }
251 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
252 }
253 }
254 Literal::Task(_, text) => {
255 match parse_date(&text) {
257 Ok((_, date)) => Ok(Literal::Date(date)),
258 Err(_) => Err(anyhow!("Failed to parse Task text as Date")),
259 }
260 }
261 Literal::Text(text) => {
262 match parse_date(&text) {
264 Ok((_, date)) => Ok(Literal::Date(date)),
265 Err(_) => Err(anyhow!("Failed to parse Text({}) as Date", text)),
266 }
267 }
268 }
269 }
270
271 pub fn is_number(&self) -> bool {
273 matches!(self, Literal::Number(_))
274 }
275
276 pub fn from_number(n: f64) -> Self {
278 Literal::Number(n)
279 }
280
281 pub fn as_number(self) -> Result<Literal> {
290 match self {
291 Literal::Empty => Err(anyhow!("Expecting type as Number, found Empty")),
292 Literal::Bool(state) => Ok(Literal::Number(if state { 1.0 } else { 0.0 })),
293 Literal::Date(dt) => {
294 if let Ok(utc_dt) = dt.to_zoned(jiff::tz::TimeZone::UTC) {
295 let seconds = utc_dt.timestamp().as_second();
296 Ok(Literal::Number(seconds as f64))
297 } else {
298 Ok(Literal::Number(0.0))
299 }
300 }
301 Literal::Number(_) => Ok(self),
302 Literal::Task(status, _) => match status {
303 Some(state) => Ok(Literal::Number(if state { 1.0 } else { -1.0 })),
304 None => Ok(Literal::Number(0.0)),
305 },
306 Literal::Text(text) => match text.parse::<f64>() {
307 Ok(number) => Ok(Literal::Number(number)),
308 Err(_) => Err(anyhow!("Expecting Text as Number, found \"{}\"", text)),
309 },
310 }
311 }
312
313 pub fn to_number(&self) -> Result<f64> {
317 match self {
318 Literal::Empty => Err(anyhow!("Expecting type as Number, found Empty")),
319 Literal::Bool(state) => Ok(if *state { 1.0 } else { 0.0 }),
320 Literal::Date(dt) => {
321 if let Ok(utc_dt) = dt.to_zoned(jiff::tz::TimeZone::UTC) {
322 let seconds = utc_dt.timestamp().as_second();
323 Ok(seconds as f64)
324 } else {
325 Ok(0.0)
326 }
327 }
328 Literal::Number(number) => Ok(*number),
329 Literal::Task(status, _) => match *status {
330 Some(state) => Ok(if state { 1.0 } else { -1.0 }),
331 None => Ok(0.0),
332 },
333 Literal::Text(text) => match text.parse::<f64>() {
334 Ok(number) => Ok(number),
335 Err(_) => Err(anyhow!("Expecting Text as Number, found \"{}\"", text)),
336 },
337 }
338 }
339
340 pub fn is_task(&self) -> bool {
342 matches!(self, Literal::Task(..))
343 }
344
345 pub fn from_task(status: Option<bool>, task: Arc<str>) -> Self {
347 Literal::Task(status, task)
348 }
349
350 pub fn as_task(self) -> Result<Literal> {
358 match self {
359 Literal::Empty => Err(anyhow!("Expecting type as Task, found Empty")),
360 Literal::Bool(state) => Ok(Literal::Task(Some(state), Arc::from(state.to_string()))),
361 Literal::Date(dt) => Ok(Literal::Task(None, Arc::from(dt.to_string()))),
362 Literal::Number(number) => {
363 let status = if number > 0.0 {
364 Some(true)
365 } else if number < 0.0 {
366 Some(false)
367 } else {
368 None
369 };
370 Ok(Literal::Task(status, Arc::from(number.to_string())))
371 }
372 Literal::Task(..) => Ok(self),
373 Literal::Text(text) => Ok(Literal::Task(None, text)),
374 }
375 }
376
377 pub fn is_text(&self) -> bool {
379 matches!(self, Literal::Text(_))
380 }
381
382 pub fn from_text(text: Arc<str>) -> Self {
384 Literal::Text(text)
385 }
386
387 pub fn as_text(self) -> Result<Literal> {
395 match self {
396 Literal::Empty => Err(anyhow!("Expecting type as Text, found Empty")),
397 Literal::Bool(state) => Ok(Literal::Text(Arc::from(state.to_string()))),
398 Literal::Date(dt) => Ok(Literal::Text(Arc::from(dt.to_string()))),
399 Literal::Number(number) => Ok(Literal::Text(Arc::from(number.to_string()))),
400 Literal::Task(_, text) => Ok(Literal::Text(text)),
401 Literal::Text(_) => Ok(self),
402 }
403 }
404
405 pub fn type_as_string(&self) -> &'static str {
407 match self {
408 Literal::Empty => "Empty",
409 Literal::Bool(_) => "Bool",
410 Literal::Date(_) => "Date",
411 Literal::Number(_) => "Number",
412 Literal::Task(..) => "Task",
413 Literal::Text(_) => "Text",
414 }
415 }
416
417 fn type_order(&self) -> u8 {
419 match self {
420 Literal::Empty => 0,
421 Literal::Bool(_) => 1,
422 Literal::Number(_) => 2,
423 Literal::Date(_) => 3,
424 Literal::Text(_) => 4,
425 Literal::Task(..) => 5,
426 }
427 }
428
429 fn type_promote(self, literal: &Literal) -> Result<Literal> {
430 match literal {
431 Literal::Task(..) => {
432 match self {
433 Literal::Text(t) => Ok(Literal::Task(None, t)),
434 Literal::Date(dt) => Ok(Literal::Task(None, Arc::from(dt.to_string()))),
435 Literal::Number(n) => Ok(Literal::Task(None, Arc::from(n.to_string()))),
436 _ => Err(anyhow!("Type promote to Task failed")),
438 }
439 }
440 Literal::Text(_) => {
441 match self {
442 Literal::Date(dt) => Ok(Literal::Text(Arc::from(dt.to_string()))),
443 Literal::Number(n) => Ok(Literal::Text(Arc::from(n.to_string()))),
444 _ => Err(anyhow!("Type promote to Text failed")),
446 }
447 }
448 Literal::Date(_) => {
449 match self {
450 Literal::Number(number) => {
451 let timestamp = number as i64;
453 match DateTime::new(1970, 1, 1, 0, 0, 0, 0) {
454 Ok(epoch) => {
455 match jiff::Span::new().try_seconds(timestamp) {
457 Ok(duration) => match epoch.checked_add(duration) {
458 Ok(new_dt) => Ok(Literal::Date(new_dt)),
459 Err(_) => Err(anyhow!(
460 "Failed to create Date from Number({})",
461 number
462 )),
463 },
464 Err(_) => Err(anyhow!(
465 "Failed to create Date from Number({})",
466 number
467 )),
468 }
469 }
470 Err(_) => Err(anyhow!("Failed to create Date from Number({})", number)),
471 }
472 }
473 _ => Err(anyhow!("Type promote to Date failed")),
474 }
475 }
476 Literal::Number(_) => match self {
477 Literal::Bool(b) => Ok(Literal::Number(if b { 1.0 } else { 0.0 })),
478 _ => Err(anyhow!("Type promote to Task failed")),
479 },
480 _ => Err(anyhow!("Type promote failed")),
481 }
482 }
483
484 pub fn print(&self, writer: &mut Writer) {
485 match self {
486 Literal::Bool(b) => writer.write_bool(*b),
487 Literal::Date(d) => writer.write_date(d),
488 Literal::Empty => {}
489 Literal::Number(n) => writer.write_f64(*n),
490 Literal::Task(status, text) => {
491 match status {
492 Some(check) => writer.write_str(if *check { "[x] " } else { "[-] " }),
493 None => writer.write_str("[ ] "),
494 }
495 writer.print(text);
496 },
497 Literal::Text(text) => writer.print(text),
498 }
499 }
500
501 pub fn to_formula(&self) -> String {
503 let mut writer = Writer::formulizer();
504 self.print(&mut writer);
505 writer.finish()
506 }
507}
508
509impl WriterLike for Literal {
510 fn write(&self, writer: &mut Writer) {
511 self.print(writer);
512 }
513}
514
515impl fmt::Display for Literal {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 write!(f, "{}", self.to_stringized())
518 }
519}
520
521impl PartialOrd for Literal {
522 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
523 match (self, other) {
525 (Literal::Text(t), Literal::Bool(b)) => {
526 if let Ok(a) = t.parse::<bool>() { return a.partial_cmp(b) }
527 },
528 (Literal::Bool(a), Literal::Text(t)) => {
529 if let Ok(b) = t.parse::<bool>() { return a.partial_cmp(&b) }
530 },
531 (Literal::Text(t), Literal::Number(b)) => {
532 if let Ok(a) = t.parse::<f64>() { return a.partial_cmp(b) }
533 },
534 (Literal::Number(a), Literal::Text(t)) => {
535 if let Ok(b) = t.parse::<f64>() { return a.partial_cmp(&b) }
536 }
537 (Literal::Text(t), Literal::Empty) | (Literal::Empty, Literal::Text(t)) => {
538 if t.is_empty() {
539 return Some(Ordering::Equal);
540 }
541 }
542 _ => {}
543 }
544 let self_type_order = self.type_order();
547 let other_type_order = other.type_order();
548
549 if self_type_order > other_type_order {
550 match other.clone().type_promote(self) {
551 Ok(that) => self.partial_cmp(&that),
552 _ => Some(Ordering::Greater),
553 }
554 } else if self_type_order < other_type_order {
555 match self.clone().type_promote(other) {
556 Ok(this) => this.partial_cmp(other),
557 _ => Some(Ordering::Less),
558 }
559 } else {
560 match (self, other) {
562 (Literal::Empty, Literal::Empty) => Some(Ordering::Equal),
563 (Literal::Bool(a), Literal::Bool(b)) => a.partial_cmp(b),
564 (Literal::Date(a), Literal::Date(b)) => a.partial_cmp(b),
565 (Literal::Number(a), Literal::Number(b)) => a.partial_cmp(b),
566 (Literal::Task(status1, text1), Literal::Task(status2, text2)) => {
567 match (status1, status2) {
569 (Some(true), Some(false)) => Some(Ordering::Greater),
570 (Some(true), None) => Some(Ordering::Greater),
571 (None, Some(true)) => Some(Ordering::Less),
572 (None, Some(false)) => Some(Ordering::Greater),
573 (Some(false), Some(true)) => Some(Ordering::Less),
574 (Some(false), None) => Some(Ordering::Less),
575 (Some(true), Some(true)) | (Some(false), Some(false)) | (None, None) => {
577 text1.partial_cmp(text2)
578 }
579 }
580 }
581 (Literal::Text(a), Literal::Text(b)) => a.partial_cmp(b),
582 _ => Some(Ordering::Equal), }
584 }
585 }
586}
587
588impl Eq for Literal {}
589
590impl Ord for Literal {
591 fn cmp(&self, other: &Self) -> Ordering {
592 self.partial_cmp(other).unwrap_or(Ordering::Equal)
594 }
595}
596
597pub fn parse_literal(input: &str) -> IResult<&str, Literal> {
602 delimited(
603 multispace0,
604 alt((
605 map(parse_bool, Literal::Bool),
606 map(parse_date, Literal::Date),
607 map(parse_number, Literal::Number),
608 map(parse_task, |(status, text)| Literal::Task(status, text)),
609 map(parse_text, Literal::Text),
610 )),
611 multispace0
612 ).parse(input)
613}
614
615
616pub fn parse_bool(input: &str) -> IResult<&str, bool> {
620 alt((map(tag("true"), |_| true), map(tag("false"), |_| false))).parse(input)
621}
622
623pub fn parse_task(input: &str) -> IResult<&str, (Option<bool>, Arc<str>)> {
628 (
629 delimited(
630 char('['),
631 delimited(
632 multispace0,
633 opt(alt((
634 value(true, char('+')),
635 value(false, char('-')),
636 ))),
637 multispace0,
638 ),
639 char(']'),
640 ),
641 multispace0,
642 parse_text,
643 ).parse(input)
644 .map(|(input, (status, _, text))| (input, (status, text)))
645}