Add Id
type for creating unique message identifiers; Change DateTime
to only deal with instants after the Unix epoch and simplify the math.
This commit is contained in:
parent
64b5051341
commit
af49f327ec
13 changed files with 232 additions and 48 deletions
|
@ -9,6 +9,7 @@ libc = "0.2.146"
|
||||||
rustls-pemfile = "1.0"
|
rustls-pemfile = "1.0"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
|
tinyrand = "0.5"
|
||||||
x509-parser = "0.15"
|
x509-parser = "0.15"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
|
@ -1,2 +1,69 @@
|
||||||
|
use {
|
||||||
|
crate::prelude::ParseRequestError,
|
||||||
|
std::{fmt, io, string::FromUtf8Error, time::SystemTimeError},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Error;
|
pub enum Error {
|
||||||
|
Io(io::Error),
|
||||||
|
Request(ParseRequestError),
|
||||||
|
Storage(String),
|
||||||
|
Rustls(rustls::Error),
|
||||||
|
Time(SystemTimeError),
|
||||||
|
Utf8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Io(e) => write!(f, "{e}"),
|
||||||
|
Self::Storage(e) => write!(f, "{e}"),
|
||||||
|
Self::Request(e) => write!(f, "{e}"),
|
||||||
|
Self::Rustls(e) => write!(f, "{e}"),
|
||||||
|
Self::Time(e) => write!(f, "{e}"),
|
||||||
|
Self::Utf8 => write!(f, "Utf8 error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
Self::Io(e) => Some(e),
|
||||||
|
Self::Request(e) => Some(e),
|
||||||
|
Self::Rustls(e) => Some(e),
|
||||||
|
Self::Time(e) => Some(e),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ParseRequestError> for Error {
|
||||||
|
fn from(value: ParseRequestError) -> Self {
|
||||||
|
Self::Request(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rustls::Error> for Error {
|
||||||
|
fn from(value: rustls::Error) -> Self {
|
||||||
|
Self::Rustls(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SystemTimeError> for Error {
|
||||||
|
fn from(value: SystemTimeError) -> Self {
|
||||||
|
Self::Time(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FromUtf8Error> for Error {
|
||||||
|
fn from(_value: FromUtf8Error) -> Self {
|
||||||
|
Self::Utf8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,19 @@ pub mod builder;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod verifier;
|
pub mod verifier;
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
prelude::{ClientCertificateStore, MailStore},
|
||||||
|
request::Request,
|
||||||
|
response::Response,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
io::Read,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -14,4 +27,28 @@ pub struct Connection {
|
||||||
pub inner: rustls::ServerConnection,
|
pub inner: rustls::ServerConnection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Connection {}
|
impl Connection {
|
||||||
|
pub fn handle_request<C: ClientCertificateStore + FingerPrintStore, S: MailStore>(
|
||||||
|
&mut self,
|
||||||
|
verifier: Verifier<C>,
|
||||||
|
mailstore: Arc<Mutex<S>>,
|
||||||
|
) -> Result<Response, Error> {
|
||||||
|
let mut buf: Vec<u8> = Vec::with_capacity(2048);
|
||||||
|
self.inner.reader().read_to_end(&mut buf)?;
|
||||||
|
let raw = String::from_utf8(buf)?;
|
||||||
|
let request: Request = raw.parse()?;
|
||||||
|
let res = if let Ok(store) = mailstore.lock() {
|
||||||
|
if store.has_mailuser(&request.recipient.to_string()) {
|
||||||
|
let time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||||
|
//store.add_message(&request.recipient.to_string(), "Inbox", request.message)
|
||||||
|
// .map_err(|e| Error::Storage(e.to_string()))?;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::mailuser::Mailuser;
|
use crate::mailuser::Mailuser;
|
||||||
use rustls::server::ClientCertVerifier;
|
use rustls::server::{ClientCertVerified, ClientCertVerifier};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -23,7 +23,7 @@ impl<S: FingerPrintStore> ClientCertVerifier for Verifier<S> {
|
||||||
end_entity: &rustls::Certificate,
|
end_entity: &rustls::Certificate,
|
||||||
intermediates: &[rustls::Certificate],
|
intermediates: &[rustls::Certificate],
|
||||||
now: std::time::SystemTime,
|
now: std::time::SystemTime,
|
||||||
) -> Result<rustls::server::ClientCertVerified, rustls::Error> {
|
) -> Result<ClientCertVerified, rustls::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::prelude::{DateTime, Link, Mailbox, Recipients};
|
use crate::prelude::{DateTime, Id, Link, Mailbox, Recipients};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -30,6 +30,12 @@ pub enum GemtextNode {
|
||||||
Link(Link),
|
Link(Link),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Id> for GemtextNode {
|
||||||
|
fn from(value: Id) -> Self {
|
||||||
|
Self::Timestamp(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for GemtextNode {
|
impl fmt::Display for GemtextNode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -264,9 +264,7 @@ impl MailStore for Filesystem {
|
||||||
let mut path = self.path.clone();
|
let mut path = self.path.clone();
|
||||||
path.push(&mb.host.to_string());
|
path.push(&mb.host.to_string());
|
||||||
path.push(&mb.username);
|
path.push(&mb.username);
|
||||||
fs::DirBuilder::new()
|
fs::DirBuilder::new().recursive(true).create(&path)?;
|
||||||
.recursive(true)
|
|
||||||
.create(&path)?;
|
|
||||||
let p = CString::new(path.to_str().ok_or(Error::Utf8)?.to_string())?;
|
let p = CString::new(path.to_str().ok_or(Error::Utf8)?.to_string())?;
|
||||||
if let Some(pw) = pw::Passwd::getpw()? {
|
if let Some(pw) = pw::Passwd::getpw()? {
|
||||||
let groups = pw.groups()?;
|
let groups = pw.groups()?;
|
||||||
|
|
|
@ -10,7 +10,7 @@ mod filesystem;
|
||||||
pub use filesystem::{Error as FilesystemError, Filesystem, MultiDomain};
|
pub use filesystem::{Error as FilesystemError, Filesystem, MultiDomain};
|
||||||
|
|
||||||
pub trait MailStore {
|
pub trait MailStore {
|
||||||
type Error;
|
type Error: std::error::Error;
|
||||||
|
|
||||||
/// Retreives a list of all valid mailusers on this server
|
/// Retreives a list of all valid mailusers on this server
|
||||||
fn users(&self) -> Vec<Mailuser>;
|
fn users(&self) -> Vec<Mailuser>;
|
||||||
|
|
62
src/message/id.rs
Normal file
62
src/message/id.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
error::Error,
|
||||||
|
fmt,
|
||||||
|
fs::File,
|
||||||
|
io::{self, Read},
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
},
|
||||||
|
tinyrand::{RandRange, Seeded, StdRand},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALPHABET: [char; 67] = [
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
|
||||||
|
't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||||
|
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4',
|
||||||
|
'5', '6', '7', '8', '9', '@', '%', '&', '=', '+',
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Id {
|
||||||
|
pub time: u64,
|
||||||
|
pub id: [char; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Id {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}.", self.time)?;
|
||||||
|
self.id.iter().try_for_each(|c| write!(f, "{c}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
pub fn new() -> Result<Self, Box<dyn Error>> {
|
||||||
|
let time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||||
|
let mut rand = StdRand::seed(random_seed()?);
|
||||||
|
let mut id = ['0', '0', '0', '0', '0', '0', '0', '0'];
|
||||||
|
for item in &mut id {
|
||||||
|
if let Some(c) = ALPHABET.get(rand.next_range(0..68)) {
|
||||||
|
*item = *c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self { time, id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn u64_from_bytes(b: [u8; 8]) -> u64 {
|
||||||
|
u64::from(b[0])
|
||||||
|
| (u64::from(b[1]) << 8)
|
||||||
|
| (u64::from(b[2]) << 16)
|
||||||
|
| (u64::from(b[3]) << 24)
|
||||||
|
| (u64::from(b[4]) << 32)
|
||||||
|
| (u64::from(b[5]) << 40)
|
||||||
|
| (u64::from(b[6]) << 48)
|
||||||
|
| (u64::from(b[7]) << 56)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed() -> io::Result<u64> {
|
||||||
|
let mut buf = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
|
let mut fd = File::open("/dev/urandom")?;
|
||||||
|
fd.read_exact(&mut buf)?;
|
||||||
|
Ok(u64_from_bytes(buf))
|
||||||
|
}
|
|
@ -6,9 +6,10 @@ use std::{fmt, str::FromStr};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
mod id;
|
||||||
mod link;
|
mod link;
|
||||||
mod parser;
|
mod parser;
|
||||||
pub use {error::Error, link::Link, parser::Parser};
|
pub use {error::Error, id::Id, link::Link, parser::Parser};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct Recipients {
|
pub struct Recipients {
|
||||||
|
|
|
@ -22,6 +22,7 @@ impl Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::missing_panics_doc)]
|
||||||
pub fn parse(mut self, content: &str) -> Result<Message, super::Error> {
|
pub fn parse(mut self, content: &str) -> Result<Message, super::Error> {
|
||||||
let lines = content.lines();
|
let lines = content.lines();
|
||||||
for l in lines {
|
for l in lines {
|
||||||
|
|
|
@ -6,7 +6,7 @@ pub use super::{
|
||||||
mailbox::{Error as ParseMailboxError, Mailbox},
|
mailbox::{Error as ParseMailboxError, Mailbox},
|
||||||
mailstore::{Account, Domain, Filesystem, FilesystemError, Folder, MailStore},
|
mailstore::{Account, Domain, Filesystem, FilesystemError, Folder, MailStore},
|
||||||
mailuser::Mailuser,
|
mailuser::Mailuser,
|
||||||
message::{Error as ParseMessageError, Link, Message, Recipients},
|
message::{Error as ParseMessageError, Id, Link, Message, Recipients},
|
||||||
//receiver,
|
//receiver,
|
||||||
request::{Error as ParseRequestError, Request},
|
request::{Error as ParseRequestError, Request},
|
||||||
response::{Error as ParseResponseError, Response},
|
response::{Error as ParseResponseError, Response},
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
//! A container representing a moment in ISO-8601 format Time
|
//! A container representing a moment in ISO-8601 format Time
|
||||||
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
use crate::prelude::Id;
|
||||||
use std::{fmt, str::FromStr};
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
@ -8,13 +10,13 @@ pub use {error::Error, parser::Parser};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const SECS_PER_DAY: i64 = 86400;
|
const SECS_PER_DAY: u64 = 86400;
|
||||||
|
|
||||||
fn days_since_epoch(ts: i64) -> i64 {
|
fn days_since_epoch(ts: u64) -> u64 {
|
||||||
ts / SECS_PER_DAY
|
ts / SECS_PER_DAY
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_leap(year: i64) -> bool {
|
fn is_leap(year: u32) -> bool {
|
||||||
year % 4 == 0 && (year % 100 != 0 || year % 16 == 0)
|
year % 4 == 0 && (year % 100 != 0 || year % 16 == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +24,7 @@ fn is_leap(year: i64) -> bool {
|
||||||
/// given month is February, then we also need to know
|
/// given month is February, then we also need to know
|
||||||
/// whether it is a leap year or not, hence this function
|
/// whether it is a leap year or not, hence this function
|
||||||
/// takes the year as an argument as well.
|
/// takes the year as an argument as well.
|
||||||
pub fn days_in_month(month: u8, year: i64) -> i64 {
|
pub fn days_in_month(month: u8, year: u32) -> u8 {
|
||||||
assert!(month < 13);
|
assert!(month < 13);
|
||||||
match month {
|
match month {
|
||||||
1 | 3 | 5 | 7 | 10 | 12 => 31,
|
1 | 3 | 5 | 7 | 10 | 12 => 31,
|
||||||
|
@ -142,8 +144,8 @@ impl fmt::Display for DateTime {
|
||||||
|
|
||||||
impl PartialOrd for DateTime {
|
impl PartialOrd for DateTime {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
let a: i64 = (*self).into();
|
let a: u64 = (*self).into();
|
||||||
let b: i64 = (*other).into();
|
let b: u64 = (*other).into();
|
||||||
a.partial_cmp(&b)
|
a.partial_cmp(&b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,12 +158,8 @@ impl FromStr for DateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<i64> for DateTime {
|
impl From<u64> for DateTime {
|
||||||
fn from(ts: i64) -> Self {
|
fn from(ts: u64) -> Self {
|
||||||
// Only allow using this function for positive timestamp values,
|
|
||||||
// ie no dates before 1970-01-01
|
|
||||||
assert!(ts.is_positive());
|
|
||||||
|
|
||||||
let mut year = 1970;
|
let mut year = 1970;
|
||||||
let mut days = days_since_epoch(ts);
|
let mut days = days_since_epoch(ts);
|
||||||
loop {
|
loop {
|
||||||
|
@ -174,8 +172,9 @@ impl From<i64> for DateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut month = 1;
|
let mut month = 1;
|
||||||
while days_in_month(month, year) <= days {
|
let m_days = u64::from(days_in_month(month, year));
|
||||||
days -= days_in_month(month, year);
|
while m_days <= days {
|
||||||
|
days -= m_days;
|
||||||
month += 1;
|
month += 1;
|
||||||
}
|
}
|
||||||
let seconds = ts % SECS_PER_DAY;
|
let seconds = ts % SECS_PER_DAY;
|
||||||
|
@ -184,8 +183,8 @@ impl From<i64> for DateTime {
|
||||||
let hour = minutes / 60;
|
let hour = minutes / 60;
|
||||||
let minute = minutes % 60;
|
let minute = minutes % 60;
|
||||||
DateTime {
|
DateTime {
|
||||||
year: year as u32,
|
year,
|
||||||
month: month.try_into().unwrap(),
|
month,
|
||||||
day: (days + 1).try_into().unwrap(),
|
day: (days + 1).try_into().unwrap(),
|
||||||
hour: Some((hour).try_into().unwrap()),
|
hour: Some((hour).try_into().unwrap()),
|
||||||
minute: Some((minute).try_into().unwrap()),
|
minute: Some((minute).try_into().unwrap()),
|
||||||
|
@ -195,20 +194,28 @@ impl From<i64> for DateTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DateTime> for i64 {
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
|
impl From<Id> for DateTime {
|
||||||
|
fn from(value: Id) -> Self {
|
||||||
|
let time = value.time;
|
||||||
|
time.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DateTime> for u64 {
|
||||||
fn from(dt: DateTime) -> Self {
|
fn from(dt: DateTime) -> Self {
|
||||||
let mut seconds = dt.second.unwrap_or(0).into();
|
let mut seconds = dt.second.unwrap_or(0).into();
|
||||||
seconds += dt.minute.unwrap_or(0) as i64 * 60;
|
seconds += u64::from(dt.minute.unwrap_or(0)) * 60;
|
||||||
seconds += (dt.hour.unwrap_or(1)) as i64 * 3600;
|
seconds += u64::from(dt.hour.unwrap_or(1)) * 3600;
|
||||||
seconds += SECS_PER_DAY * (dt.day as i64 - 1);
|
seconds += SECS_PER_DAY * (u64::from(dt.day) - 1);
|
||||||
let mut m = 1;
|
let mut m = 1;
|
||||||
while m < dt.month {
|
while m < dt.month {
|
||||||
seconds += SECS_PER_DAY * days_in_month(m, dt.year.into());
|
seconds += SECS_PER_DAY * u64::from(days_in_month(m, dt.year));
|
||||||
m += 1;
|
m += 1;
|
||||||
}
|
}
|
||||||
let mut y = 1970;
|
let mut y = 1970;
|
||||||
while y < dt.year {
|
while y < dt.year {
|
||||||
if is_leap(y.into()) {
|
if is_leap(y) {
|
||||||
seconds += SECS_PER_DAY * 366;
|
seconds += SECS_PER_DAY * 366;
|
||||||
} else {
|
} else {
|
||||||
seconds += SECS_PER_DAY * 365;
|
seconds += SECS_PER_DAY * 365;
|
||||||
|
@ -216,9 +223,9 @@ impl From<DateTime> for i64 {
|
||||||
y += 1;
|
y += 1;
|
||||||
}
|
}
|
||||||
if let Some(TimeZone::Offset(offset)) = dt.tz {
|
if let Some(TimeZone::Offset(offset)) = dt.tz {
|
||||||
let mut offset_seconds = offset.hours as i64 * 3600;
|
let mut offset_seconds = u64::from(offset.hours) * 3600;
|
||||||
if let Some(offset_minutes) = offset.minutes {
|
if let Some(offset_minutes) = offset.minutes {
|
||||||
offset_seconds += offset_minutes as i64 * 60;
|
offset_seconds += u64::from(offset_minutes) * 60;
|
||||||
}
|
}
|
||||||
match offset.sign {
|
match offset.sign {
|
||||||
Sign::Positive => seconds += offset_seconds,
|
Sign::Positive => seconds += offset_seconds,
|
||||||
|
@ -237,7 +244,7 @@ impl DateTime {
|
||||||
/// offset is applied and the result returned.
|
/// offset is applied and the result returned.
|
||||||
pub fn normalize(&mut self) -> &Self {
|
pub fn normalize(&mut self) -> &Self {
|
||||||
if let Some(TimeZone::Offset(_)) = self.tz {
|
if let Some(TimeZone::Offset(_)) = self.tz {
|
||||||
*self = i64::from(*self).into();
|
*self = u64::from(*self).into();
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
#[allow(clippy::missing_panics_doc)]
|
#[allow(clippy::missing_panics_doc)]
|
||||||
fn validate_day(&self, day: u8) -> Result<(), Error> {
|
fn validate_day(&self, day: u8) -> Result<(), Error> {
|
||||||
let max = days_in_month(self.month.unwrap(), self.year.unwrap() as i64) as u8;
|
let max = days_in_month(self.month.unwrap(), self.year.unwrap());
|
||||||
if day > max {
|
if day > max {
|
||||||
Err(Error::InvalidDay)
|
Err(Error::InvalidDay)
|
||||||
} else {
|
} else {
|
||||||
|
@ -316,18 +316,22 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
if self.year.is_none() || self.month.is_none() || self.day.is_none() {
|
if let Some(year) = self.year {
|
||||||
return Err(Error::Truncated);
|
if let Some(month) = self.month {
|
||||||
}
|
if let Some(day) = self.day {
|
||||||
Ok(DateTime {
|
return Ok(DateTime {
|
||||||
year: self.year.unwrap(),
|
year,
|
||||||
month: self.month.unwrap(),
|
month,
|
||||||
day: self.day.unwrap(),
|
day,
|
||||||
hour: self.hour,
|
hour: self.hour,
|
||||||
minute: self.minute,
|
minute: self.minute,
|
||||||
second: self.second,
|
second: self.second,
|
||||||
tz: self.tz,
|
tz: self.tz,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::Truncated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue