use std::collections::HashMap;
use chrono::prelude::*;
use chrono::Duration;
use crate::bind::{ParseRelative, Param, Result};
use crate::core::interfaces::{Parse};
use crate::core::corpus::{get_offset_local_utc, unitize_relative_text};
use crate::utils::{create_datetime, create_timestamp, eliminate_noise, split_with_numeric, str_convert};
const MAX_SECTION_LEN_TRUSTED: usize = 5;
const MAX_TEXT_LEN_TRUSTED: usize = 12;
const MAX_NUMBER_MONTH: i64 = 12;
const MAX_NUMBER_DAY: i64 = 31;
const MAX_NUMBER_WEEK: i64 = 7;
const MAX_NUMBER_HOURS: i64 = 24;
const MAX_NUMBER_MINUTES: i64 = 60;
const MAX_NUMBER_SECONDS: i64 = 60;
impl Parse for ParseRelative{
fn parse(&mut self, text: &str) -> Result{
let mut r = self.work(text);
self.assemble(&mut r);
return r
}
}
impl ParseRelative{
pub fn new(options: Option<Param>) -> ParseRelative{
if let Some(param) = options{
return ParseRelative{param}
}
return ParseRelative{..Default::default()}
}
fn assemble(&self, item: &mut Result){
item.datetime.local.datetime = item.time;
item.datetime.local.timestamp = create_timestamp(item.time);
item.datetime.timezone.datetime = item.time - Duration::seconds(get_offset_local_utc());
item.datetime.timezone.timestamp = create_timestamp(item.datetime.timezone.datetime);
}
fn work(&self, text: &str) -> Result{
let mut data = Result {method: String::from("relative"), ..Default::default() };
let mut item = eliminate_noise(text);
item = self.unitize_text(item.as_str());
let section: Vec<String> = split_with_numeric(item);
if section.len() >= MAX_SECTION_LEN_TRUSTED{
return data
}
let numeric = self.search_numeric(§ion);
for (index, item) in section.iter().enumerate() {
if self.param.strict && item.chars().count() > MAX_TEXT_LEN_TRUSTED{
continue
}
let result_relative_text = unitize_relative_text(item.as_str());
if !result_relative_text.hit{continue}
if result_relative_text.same && numeric.len() == 0{
data.time = create_datetime(true, false);
data.status = true;
return data
}
for i in (0..index).rev(){
if let Some(n) = numeric.get(&i){
if let Some(di) = self.reload_datetime_with_text(&result_relative_text.text, *n){
data.status = true;
data.time = di;
return data
}
}
}
}
return data
}
fn reload_datetime_with_text(&self,text: &str, number: i64) -> Option<NaiveDateTime>{
let datetime = Local::now().naive_local();
match text{
"year ago" =>{
let date = datetime.date();
if let Some(effective) = NaiveDate::from_ymd_opt(date.year() - number as i32, date.month(), date.day()){
let item = NaiveDateTime::new(effective, datetime.time());
return Some(item)
}
return None
}
"month ago" =>{
if number > MAX_NUMBER_MONTH{
return None
}
let date = datetime.date();
if let Some(effective) = NaiveDate::from_ymd_opt(date.year(), date.month() - number as u32, date.day()){
let item = NaiveDateTime::new(effective, datetime.time());
return Some(item)
}
return None
}
"week ago" =>{
if number > MAX_NUMBER_WEEK{
return None
}
return Some(datetime - Duration::weeks(number))
}
"day ago" =>{
if number > MAX_NUMBER_DAY{
return None
}
return Some(datetime - Duration::days(number))
}
"hour ago" =>{
if number > MAX_NUMBER_HOURS{
return None
}
return Some(datetime - Duration::hours(number))
}
"minute ago" =>{
if number > MAX_NUMBER_MINUTES{
return None
}
return Some(datetime - Duration::minutes(number))
}
"second ago" =>{
if number > MAX_NUMBER_SECONDS*MAX_NUMBER_SECONDS{
return None
}
return Some(datetime - Duration::seconds(number))
}
_ => {}
}
return None
}
fn search_numeric(&self, section: &Vec<String>) -> HashMap<usize, i64>{
let mut data:HashMap<usize, i64> = HashMap::new();
for (index, item) in section.iter().enumerate() {
if let Some(number) = str_convert(item.as_str()){
if number <1 || number > (MAX_NUMBER_SECONDS*MAX_NUMBER_SECONDS) as i32{
continue
}
data.insert(index, number as i64);
}
}
return data
}
fn unitize_text(&self, text: &str) -> String{
let mut data: Vec<String> = Vec::new();
let section: Vec<&str> = text.split(" ").collect();
for text in section{
let item = text.to_lowercase();
data.push(item);
}
return data.join(" ")
}
}