diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-06 16:10:36 +0200 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-06 16:10:36 +0200 |
commit | c0ac643f0545a0364c3b18c0fa92705399f8b238 (patch) | |
tree | f89f7a73c1c578a84712e116fa38c6644b21ff76 /melib | |
parent | f6de511abdafb4cb08c0264df6e2c88cf88faaab (diff) |
melib: add datetime module
Datetime module adds POSIX time functions interface
Diffstat (limited to 'melib')
-rw-r--r-- | melib/src/addressbook.rs | 18 | ||||
-rw-r--r-- | melib/src/addressbook/vcard.rs | 3 | ||||
-rw-r--r-- | melib/src/datetime.rs | 131 | ||||
-rw-r--r-- | melib/src/email.rs | 19 | ||||
-rw-r--r-- | melib/src/email/compose.rs | 7 | ||||
-rw-r--r-- | melib/src/email/parser.rs | 10 | ||||
-rw-r--r-- | melib/src/lib.rs | 4 | ||||
-rw-r--r-- | melib/src/logging.rs | 5 | ||||
-rw-r--r-- | melib/src/thread.rs | 8 |
9 files changed, 160 insertions, 45 deletions
diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index 714d462c..3b592090 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -22,7 +22,7 @@ #[cfg(feature = "vcard")] pub mod vcard; -use chrono::{DateTime, Local}; +use crate::datetime::{self, UnixTimestamp}; use fnv::FnvHashMap; use uuid::Uuid; @@ -59,8 +59,8 @@ impl From<String> for CardId { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct AddressBook { display_name: String, - created: DateTime<Local>, - last_edited: DateTime<Local>, + created: UnixTimestamp, + last_edited: UnixTimestamp, pub cards: FnvHashMap<CardId, Card>, } @@ -73,13 +73,13 @@ pub struct Card { name_prefix: String, name_suffix: String, //address - birthday: Option<DateTime<Local>>, + birthday: Option<UnixTimestamp>, email: String, url: String, key: String, color: u8, - last_edited: DateTime<Local>, + last_edited: UnixTimestamp, extra_properties: FnvHashMap<String, String>, /// If true, we can't make any changes because we do not manage this resource. @@ -90,8 +90,8 @@ impl AddressBook { pub fn new(display_name: String) -> AddressBook { AddressBook { display_name, - created: Local::now(), - last_edited: Local::now(), + created: datetime::now(), + last_edited: datetime::now(), cards: FnvHashMap::default(), } } @@ -154,7 +154,7 @@ impl Card { url: String::new(), key: String::new(), - last_edited: Local::now(), + last_edited: datetime::now(), external_resource: false, extra_properties: FnvHashMap::default(), color: 0, @@ -190,7 +190,7 @@ impl Card { self.key.as_str() } pub fn last_edited(&self) -> String { - self.last_edited.to_rfc2822() + datetime::timestamp_to_string(self.last_edited, None) } pub fn set_id(&mut self, new_val: CardId) { diff --git a/melib/src/addressbook/vcard.rs b/melib/src/addressbook/vcard.rs index 65488246..bc57e739 100644 --- a/melib/src/addressbook/vcard.rs +++ b/melib/src/addressbook/vcard.rs @@ -21,7 +21,6 @@ /// Convert VCard strings to meli Cards (contacts). use super::*; -use crate::chrono::TimeZone; use crate::error::{MeliError, Result}; use crate::parsec::{match_literal_anycase, one_or_more, peek, prefix, take_until, Parser}; use fnv::FnvHashMap; @@ -202,7 +201,7 @@ impl<V: VCardVersion> TryInto<Card> for VCard<V> { T102200Z T102200-0800 */ - card.birthday = chrono::Local.datetime_from_str(&val.value, "%Y%m%d").ok(); + card.birthday = crate::datetime::timestamp_from_string(val.value.as_str(), "%Y%m%d"); } if let Some(val) = self.0.remove("EMAIL") { card.set_email(val.value); diff --git a/melib/src/datetime.rs b/melib/src/datetime.rs new file mode 100644 index 00000000..7c56686f --- /dev/null +++ b/melib/src/datetime.rs @@ -0,0 +1,131 @@ +/* + * meli - melib POSIX libc time interface + * + * Copyright 2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see <http://www.gnu.org/licenses/>. + */ + +use std::convert::TryInto; +use std::ffi::{CStr, CString}; + +pub type UnixTimestamp = u64; + +use libc::{timeval, timezone}; + +extern "C" { + fn strptime( + s: *const ::std::os::raw::c_char, + format: *const ::std::os::raw::c_char, + tm: *mut ::libc::tm, + ) -> *const ::std::os::raw::c_char; + + fn strftime( + s: *mut ::std::os::raw::c_char, + max: ::libc::size_t, + format: *const ::std::os::raw::c_char, + tm: *const ::libc::tm, + ) -> ::libc::size_t; + + fn mktime(tm: *const ::libc::tm) -> ::libc::time_t; + + fn localtime_r(timep: *const ::libc::time_t, tm: *mut ::libc::tm) -> *mut ::libc::tm; + + fn gettimeofday(tv: *mut timeval, tz: *mut timezone) -> i32; +} + +pub fn timestamp_to_string(timestamp: UnixTimestamp, fmt: Option<&str>) -> String { + let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() }; + unsafe { + let i: i64 = timestamp.try_into().unwrap_or(0); + localtime_r(&i as *const i64, &mut new_tm as *mut ::libc::tm); + } + let fmt = fmt.map(|slice| CString::new(slice).unwrap()); + let format: &CStr = if let Some(ref s) = fmt { + &s + } else { + unsafe { CStr::from_bytes_with_nul_unchecked(b"%a, %d %b %Y %T %z\0") } + }; + let s: CString; + unsafe { + let mut vec: Vec<u8> = vec![0; 256]; + let ret = strftime( + vec.as_mut_ptr() as *mut _, + 256, + format.as_ptr(), + &new_tm as *const _, + ); + s = CString::new(&vec[0..ret]).unwrap(); + } + + s.to_string_lossy().to_string() +} + +pub fn rfc822_to_timestamp<T>(s: T) -> UnixTimestamp +where + T: Into<Vec<u8>>, +{ + let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() }; + unsafe { + let fmt = CStr::from_bytes_with_nul_unchecked(b"%a, %e %h %Y %H:%M:%S %z\0"); + let ret = strptime( + CString::new(s).unwrap().as_ptr(), + fmt.as_ptr(), + &mut new_tm as *mut _, + ); + if ret.is_null() { + return 0; + } + return mktime(&new_tm as *const _) as u64; + } +} + +pub fn timestamp_from_string<T>(s: T, fmt: &str) -> Option<UnixTimestamp> +where + T: Into<Vec<u8>>, +{ + let mut new_tm: ::libc::tm = unsafe { std::mem::zeroed() }; + let fmt = CString::new(fmt).unwrap(); + unsafe { + let ret = strptime( + CString::new(s).unwrap().as_ptr(), + fmt.as_ptr(), + &mut new_tm as *mut _, + ); + if ret.is_null() { + return None; + } + return Some(mktime(&new_tm as *const _) as u64); + } +} + +pub fn now() -> UnixTimestamp { + use std::mem::MaybeUninit; + let mut tv = MaybeUninit::<::libc::timeval>::uninit(); + let mut tz = MaybeUninit::<::libc::timezone>::uninit(); + unsafe { + let ret = gettimeofday(tv.as_mut_ptr(), tz.as_mut_ptr()); + if ret == -1 { + unreachable!("gettimeofday returned -1"); + } + (tv.assume_init()).tv_sec as UnixTimestamp + } +} + +#[test] +fn test_timestamp() { + timestamp_to_string(0); +} diff --git a/melib/src/email.rs b/melib/src/email.rs index 48f7c76b..cab8565e 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -39,6 +39,7 @@ pub use address::*; pub mod signatures; use crate::backends::BackendOp; +use crate::datetime::UnixTimestamp; use crate::error::{MeliError, Result}; use crate::thread::ThreadHash; @@ -51,9 +52,6 @@ use std::option::Option; use std::str; use std::string::String; -use chrono; -use chrono::TimeZone; - bitflags! { #[derive(Default, Serialize, Deserialize)] pub struct Flag: u8 { @@ -105,7 +103,6 @@ impl EnvelopeWrapper { } } -pub type UnixTimestamp = u64; pub type EnvelopeHash = u64; /// `Envelope` represents all the data of an email we need to know. @@ -350,14 +347,10 @@ impl Envelope { self.timestamp } - pub fn datetime(&self) -> chrono::DateTime<chrono::FixedOffset> { - if let Ok(d) = parser::date(&self.date.as_bytes()) { - return d; - } - chrono::FixedOffset::west(0) - .ymd(1970, 1, 1) - .and_hms(0, 0, 0) + pub fn datetime(&self) -> UnixTimestamp { + self.timestamp } + pub fn date_as_str(&self) -> &str { &self.date } @@ -572,8 +565,8 @@ impl Envelope { pub fn set_thread(&mut self, new_val: ThreadHash) { self.thread = new_val; } - pub fn set_datetime(&mut self, new_val: chrono::DateTime<chrono::FixedOffset>) { - self.timestamp = new_val.timestamp() as UnixTimestamp; + pub fn set_datetime(&mut self, new_val: UnixTimestamp) { + self.timestamp = new_val; } pub fn set_flag( &mut self, diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index 594ae1ba..47b24f86 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -2,7 +2,6 @@ use super::*; use crate::backends::BackendOp; use crate::email::attachments::AttachmentBuilder; use crate::shellexpand::ShellExpandTrait; -use chrono::{DateTime, Local}; use data_encoding::BASE64_MIME; use std::ffi::OsStr; use std::io::Read; @@ -35,8 +34,10 @@ impl Default for Draft { headers.insert("Cc".into(), "".into()); headers.insert("Bcc".into(), "".into()); - let now: DateTime<Local> = Local::now(); - headers.insert("Date".into(), now.to_rfc2822()); + headers.insert( + "Date".into(), + crate::datetime::timestamp_to_string(crate::datetime::now(), None), + ); headers.insert("Subject".into(), "".into()); headers.insert( "User-Agent".into(), diff --git a/melib/src/email/parser.rs b/melib/src/email/parser.rs index 7a2aad28..b392f04e 100644 --- a/melib/src/email/parser.rs +++ b/melib/src/email/parser.rs @@ -19,7 +19,6 @@ * along with meli. If not, see <http://www.gnu.org/licenses/>. */ use super::*; -use chrono; use data_encoding::BASE64_MIME; use encoding::{DecoderTrap, Encoding}; use nom::{is_hex_digit, le_u8}; @@ -658,18 +657,13 @@ fn eat_comments(input: &[u8]) -> Vec<u8> { * right now we expect input will have no extra spaces in between tokens * * We should use a custom parser here*/ -pub fn date(input: &[u8]) -> Result<chrono::DateTime<chrono::FixedOffset>> { +pub fn date(input: &[u8]) -> Result<UnixTimestamp> { let mut parsed_result = phrase(&eat_comments(input)).to_full_result()?; if let Some(pos) = parsed_result.find(b"-0000") { parsed_result[pos] = b'+'; } - Ok( - chrono::DateTime::parse_from_rfc2822( - String::from_utf8_lossy(parsed_result.trim()).as_ref(), - ) - .map_err(|err| MeliError::new(err.to_string()))?, - ) + Ok(crate::datetime::rfc822_to_timestamp(parsed_result.trim())) } named!(pub message_id<&[u8]>, diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 0ff5c75a..36cc0e72 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -104,6 +104,9 @@ pub mod dbg { #[cfg(feature = "unicode_algorithms")] extern crate text_processing; +pub mod datetime; +pub use datetime::UnixTimestamp; + #[macro_use] mod logging; pub use self::logging::LoggingLevel::*; @@ -129,7 +132,6 @@ extern crate serde_derive; /* parser */ #[macro_use] extern crate nom; -extern crate chrono; extern crate data_encoding; extern crate encoding; diff --git a/melib/src/logging.rs b/melib/src/logging.rs index 37985516..cef2fdb9 100644 --- a/melib/src/logging.rs +++ b/melib/src/logging.rs @@ -20,7 +20,6 @@ */ use crate::shellexpand::ShellExpandTrait; -use chrono::offset::Local; use std::fs::OpenOptions; use std::io::{BufWriter, Write}; use std::path::PathBuf; @@ -79,7 +78,9 @@ pub fn log(val: String, level: LoggingLevel) { let mut b = f.lock().unwrap(); if level <= b.level { b.dest - .write_all(Local::now().to_string().as_bytes()) + .write_all( + crate::datetime::timestamp_to_string(crate::datetime::now(), None).as_bytes(), + ) .unwrap(); b.dest.write_all(b" [").unwrap(); b.dest.write_all(level.to_string().as_bytes()).unwrap(); diff --git a/melib/src/thread.rs b/melib/src/thread.rs index a1f2445c..9c8fdbfe 100644 --- a/melib/src/thread.rs +++ b/melib/src/thread.rs @@ -32,6 +32,7 @@ * ownership. */ +use crate::datetime::UnixTimestamp; use crate::email::parser::BytesExt; use crate::email::*; use crate::structs::StackVec; @@ -432,13 +433,6 @@ impl ThreadNode { self.date } - pub fn datetime(&self) -> chrono::DateTime<chrono::Utc> { - use chrono::{TimeZone, Utc}; - use std::convert::TryInto; - - Utc.timestamp(self.date.try_into().unwrap_or(0), 0) - } - pub fn is_empty(&self) -> bool { self.parent.is_none() && self.message.is_none() && self.children.is_empty() } |