runic 1.1.0

Stupid string to duration converter
Documentation
use crate::at::At;
use crate::c::{DAY, HOUR_F64, MINUTE_F64};
use crate::parser::{PestRule as Rule, ScriptParser};
use crate::script_timezone::ScriptTimezone;
use anyhow::Result;
use chrono::{FixedOffset, TimeZone, Timelike};
use pest::{iterators::Pair, Parser};

pub struct Describe {
    utc: i64,
    offset: i32,
}

impl Describe {
    pub fn with(script: &str, timestamp: i64, offset: i32) -> Result<u64> {
        let cx = Self {
            utc: timestamp,
            offset,
        };

        let mut duration_total = 0;
        let mut at_total = 0;
        let parsed = ScriptParser::parse(Rule::Input, script)?;

        for expr in parsed {
            match expr.as_rule() {
                Rule::AtTimeExpr => {
                    let at = At::parse(expr)?;
                    at_total += cx.compute_duration_from_target_expr(at);
                }
                Rule::DurationExpr => {
                    duration_total += Self::compute_duration_from_timeout_expr(expr);
                }
                _ => {}
            }
        }

        let total = {
            let total = at_total + duration_total;

            if total.is_negative() {
                0
            } else {
                total
            }
        };

        Ok(total as _)
    }

    fn compute_duration_from_target_expr(&self, at: At) -> i64 {
        let target = {
            let offset = match at.script_timezone {
                ScriptTimezone::Mirror => FixedOffset::east(self.offset),
                ScriptTimezone::Custom(script_offset) => FixedOffset::east(script_offset),
            };

            let target = offset.timestamp(self.utc, 0);
            let target = target.with_hour(at.hours).unwrap();
            let target = target.with_minute(at.minutes).unwrap();
            let target = target.with_second(0).unwrap();
            let target = target.with_nanosecond(0).unwrap();

            target.timestamp()
        };

        let duration = target - self.utc;

        if duration.is_negative() {
            DAY + duration
        } else {
            duration
        }
    }

    fn compute_duration_from_timeout_expr(expr: Pair<Rule>) -> i64 {
        let mut needle: f64 = 0.0;

        for prop in expr.into_inner() {
            match prop.as_rule() {
                Rule::Duration => {
                    needle = prop.as_str().parse().unwrap();
                }
                Rule::Hours => {
                    return (needle * HOUR_F64) as _;
                }
                Rule::Minutes => {
                    return (needle * MINUTE_F64) as _;
                }
                Rule::Seconds => {
                    return needle as _;
                }
                _ => {}
            }
        }

        unreachable!()
    }
}