use crate::error::AsStatic;
use crate::sheet::Visibility;
use crate::xlink::{XLinkActuate, XLinkShow, XLinkType};
use crate::OdsError;
use chrono::Duration;
use chrono::NaiveDateTime;
use kparse::prelude::*;
use kparse::{TokenizerError, TokenizerResult};
use nom::character::complete::digit1;
use nom::combinator::{all_consuming, eof, opt};
use nom::number::complete::double;
use nom::sequence::{pair, preceded, terminated, tuple};
use nom::AsChar;
use std::fmt::{Display, Formatter};
use std::str::{from_utf8, from_utf8_unchecked};
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum RCode {
NomError,
Byte,
Digit,
Integer,
DateTime,
Bool,
Duration,
}
impl AsStatic<str> for RCode {
#[inline]
fn as_static(&self) -> &'static str {
match self {
RCode::NomError => "NomError",
RCode::Byte => "Byte",
RCode::Digit => "Digit",
RCode::Integer => "Integer",
RCode::DateTime => "DateTime",
RCode::Bool => "Bool",
RCode::Duration => "Duration",
}
}
}
impl Display for RCode {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl Code for RCode {
const NOM_ERROR: Self = Self::NomError;
}
pub(crate) type KSpan<'s> = &'s [u8];
pub(crate) type KTokenResult<'s, O> = Result<O, nom::Err<TokenizerError<RCode, KSpan<'s>>>>;
pub(crate) type KTokenizerResult<'s, O> = TokenizerResult<RCode, KSpan<'s>, O>;
pub(crate) type KTokenizerError<'s> = TokenizerError<RCode, KSpan<'s>>;
#[inline]
pub(crate) fn parse_visibility(input: KSpan<'_>) -> Result<Visibility, OdsError> {
match input {
b"visible" => Ok(Visibility::Visible),
b"filter" => Ok(Visibility::Filtered),
b"collapse" => Ok(Visibility::Collapsed),
_ => Err(OdsError::Ods(format!(
"Unknown value for table:visibility {}",
from_utf8(input)?
))),
}
}
#[inline]
pub(crate) fn parse_xlink_actuate(input: KSpan<'_>) -> Result<XLinkActuate, OdsError> {
match input {
b"onLoad" => Ok(XLinkActuate::OnLoad),
b"onRequest" => Ok(XLinkActuate::OnRequest),
_ => Err(OdsError::Parse(
"invalid xlink:actuate",
Some(from_utf8(input)?.to_string()),
)),
}
}
#[inline]
pub(crate) fn parse_xlink_show(input: KSpan<'_>) -> Result<XLinkShow, OdsError> {
match input {
b"new" => Ok(XLinkShow::New),
b"replace" => Ok(XLinkShow::Replace),
_ => Err(OdsError::Parse(
"invalid xlink:show",
Some(from_utf8(input)?.to_string()),
)),
}
}
#[inline]
pub(crate) fn parse_xlink_type(input: KSpan<'_>) -> Result<XLinkType, OdsError> {
match input {
b"simple" => Ok(XLinkType::Simple),
b"extended" => Ok(XLinkType::Extended),
b"locator" => Ok(XLinkType::Locator),
b"arc" => Ok(XLinkType::Arc),
b"resource" => Ok(XLinkType::Resource),
b"title" => Ok(XLinkType::Title),
b"none" => Ok(XLinkType::None),
_ => Err(OdsError::Parse(
"invalid xlink:type",
Some(from_utf8(input)?.to_string()),
)),
}
}
#[inline]
pub(crate) fn parse_string(input: KSpan<'_>) -> Result<String, OdsError> {
Ok(String::from_utf8_lossy(input).to_string())
}
#[inline]
pub(crate) fn parse_currency(input: KSpan<'_>) -> Result<String, OdsError> {
Ok(String::from_utf8_lossy(input).to_string())
}
#[inline]
pub(crate) fn parse_bool(input: KSpan<'_>) -> Result<bool, OdsError> {
Ok(token_bool(input)?)
}
#[inline]
pub(crate) fn parse_u32(input: KSpan<'_>) -> Result<u32, OdsError> {
Ok(token_u32(input)?)
}
#[inline]
pub(crate) fn parse_i64(input: KSpan<'_>) -> Result<i64, OdsError> {
Ok(token_i64(input)?)
}
#[inline]
pub(crate) fn parse_i32(input: KSpan<'_>) -> Result<i32, OdsError> {
Ok(token_i32(input)?)
}
#[inline]
pub(crate) fn parse_i16(input: KSpan<'_>) -> Result<i16, OdsError> {
Ok(token_i16(input)?)
}
#[inline]
pub(crate) fn parse_f64(input: KSpan<'_>) -> Result<f64, OdsError> {
Ok(token_float(input)?)
}
#[inline]
pub(crate) fn parse_datetime(input: KSpan<'_>) -> Result<NaiveDateTime, OdsError> {
Ok(token_datetime(input)?)
}
#[inline]
pub(crate) fn parse_duration(input: KSpan<'_>) -> Result<Duration, OdsError> {
Ok(token_duration(input)?)
}
#[inline(always)]
fn token_bool(input: KSpan<'_>) -> KTokenResult<'_, bool> {
match input {
b"true" => Ok(true),
b"false" => Ok(false),
_ => Err(nom::Err::Error(KTokenizerError::new(RCode::Bool, input))),
}
}
#[inline(always)]
fn token_i16(input: KSpan<'_>) -> KTokenResult<'_, i16> {
sign_all_digits(input)?;
let result = match unsafe { from_utf8_unchecked(input) }.parse::<i16>() {
Ok(result) => result,
Err(_) => {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Integer, input)));
}
};
Ok(result)
}
#[inline(always)]
fn token_u32(input: KSpan<'_>) -> KTokenResult<'_, u32> {
all_digits(input)?;
let result = match unsafe { from_utf8_unchecked(input) }.parse::<u32>() {
Ok(result) => result,
Err(_) => {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Integer, input)));
}
};
Ok(result)
}
#[inline(always)]
fn token_i32(input: KSpan<'_>) -> KTokenResult<'_, i32> {
sign_all_digits(input)?;
let result = match unsafe { from_utf8_unchecked(input) }.parse::<i32>() {
Ok(result) => result,
Err(_) => {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Integer, input)));
}
};
Ok(result)
}
#[inline(always)]
fn token_i64(input: KSpan<'_>) -> KTokenResult<'_, i64> {
sign_all_digits(input)?;
let result = match unsafe { from_utf8_unchecked(input) }.parse::<i64>() {
Ok(result) => result,
Err(_) => {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Integer, input)));
}
};
Ok(result)
}
#[inline(always)]
fn token_float(input: KSpan<'_>) -> KTokenResult<'_, f64> {
let (_, result) = all_consuming(double)(input)?;
Ok(result)
}
#[inline]
fn token_datepart(input: KSpan<'_>) -> KTokenizerResult<'_, i64> {
let (rest, result) = digit1(input)?;
let result = match unsafe { from_utf8_unchecked(result) }.parse::<i64>() {
Ok(result) => result,
Err(_) => {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Integer, input)));
}
};
Ok((rest, result))
}
#[inline]
fn token_nano(input: KSpan<'_>) -> KTokenizerResult<'_, i64> {
let (rest, result) = digit1(input)?;
let mut v = 0i64;
for i in 0..9 {
if i < result.len() {
v *= 10;
v += (result[i] - b'0') as i64;
} else {
v *= 10;
}
}
Ok((rest, v))
}
#[inline(always)]
fn token_datetime(input: KSpan<'_>) -> KTokenResult<'_, NaiveDateTime> {
let (_, (minus, year, _, month, _, day, time, _)) = terminated(
tuple((
opt(byte(b'-')),
token_datepart,
byte(b'-'),
token_datepart,
byte(b'-'),
token_datepart,
opt(tuple((
byte(b'T'),
token_datepart,
byte(b':'),
token_datepart,
byte(b':'),
token_datepart,
opt(tuple((byte(b'.'), token_nano))),
))),
opt(byte(b'Z')),
)),
eof,
)(input)?;
let sign = match minus {
Some(_) => -1,
None => 1,
};
let mut p = chrono::format::Parsed::new();
p.year = Some((sign * year) as i32);
p.month = Some(month as u32);
p.day = Some(day as u32);
if let Some((_, hour, _, minute, _, second, nanos)) = time {
p.hour_div_12 = Some((hour / 12) as u32);
p.hour_mod_12 = Some((hour % 12) as u32);
p.minute = Some(minute as u32);
p.second = Some(second as u32);
if let Some((_, nanos)) = nanos {
p.nanosecond = Some(nanos as u32);
}
} else {
p.hour_div_12 = Some(0);
p.hour_mod_12 = Some(0);
p.minute = Some(0);
p.second = Some(0);
}
match p.to_naive_datetime_with_offset(0) {
Ok(v) => Ok(v),
Err(_) => Ok(NaiveDateTime::default()),
}
}
#[inline(always)]
fn token_duration(input: KSpan<'_>) -> KTokenResult<'_, Duration> {
let (_, (_, day, time)) = all_consuming(tuple((
byte(b'P'),
opt(terminated(token_datepart, byte(b'D'))),
opt(tuple((
byte(b'T'),
opt(terminated(token_datepart, byte(b'H'))),
opt(terminated(token_datepart, byte(b'M'))),
opt(terminated(
pair(token_datepart, opt(preceded(byte(b'.'), token_nano))),
byte(b'S'),
)),
))),
)))(input)?;
let mut result = Duration::try_seconds(0).ok_or(nom::Err::Error(KTokenizerError::new(
RCode::Duration,
input,
)))?;
if let Some(day) = day {
result += Duration::try_days(day).ok_or(nom::Err::Error(KTokenizerError::new(
RCode::Duration,
input,
)))?;
}
if let Some((_, hour, minute, second)) = time {
if let Some(hour) = hour {
result += Duration::try_hours(hour).ok_or(nom::Err::Error(KTokenizerError::new(
RCode::Duration,
input,
)))?;
}
if let Some(minute) = minute {
result += Duration::try_minutes(minute).ok_or(nom::Err::Error(
KTokenizerError::new(RCode::Duration, input),
))?;
}
if let Some((second, nanos)) = second {
result += Duration::try_seconds(second).ok_or(nom::Err::Error(
KTokenizerError::new(RCode::Duration, input),
))?;
if let Some(nanos) = nanos {
result += Duration::nanoseconds(nanos);
}
}
}
Ok(result)
}
#[inline(always)]
fn all_digits(input: KSpan<'_>) -> KTokenResult<'_, ()> {
for c in input {
if !c.is_dec_digit() {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Digit, input)));
}
}
Ok(())
}
#[inline(always)]
fn sign_all_digits(input: KSpan<'_>) -> KTokenResult<'_, ()> {
let mut it = input.iter();
match it.next() {
Some(b'-') => {}
Some(v) if v.is_dec_digit() => {}
_ => return Err(nom::Err::Error(KTokenizerError::new(RCode::Digit, input))),
}
for b in it {
if !b.is_dec_digit() {
return Err(nom::Err::Error(KTokenizerError::new(RCode::Digit, input)));
}
}
Ok(())
}
#[inline(always)]
pub(crate) fn byte(c: u8) -> impl Fn(KSpan<'_>) -> KTokenizerResult<'_, ()> {
move |i: KSpan<'_>| {
if !i.is_empty() && i[0] == c {
Ok((&i[1..], ()))
} else {
Err(nom::Err::Error(KTokenizerError::new(RCode::Byte, i)))
}
}
}
#[cfg(test)]
mod tests {
use crate::io::parse::{
parse_bool, parse_datetime, parse_duration, parse_f64, parse_i32, parse_u32, token_nano,
};
use crate::OdsError;
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
#[test]
fn test_u32() -> Result<(), OdsError> {
assert_eq!(parse_u32(b"1234")?, 1234);
parse_u32(b"123456789000").unwrap_err();
parse_u32(b"1234 ").unwrap_err();
parse_u32(b"-1234 ").unwrap_err();
parse_u32(b"-1234").unwrap_err();
Ok(())
}
#[test]
fn test_i32() -> Result<(), OdsError> {
assert_eq!(parse_i32(b"1234")?, 1234);
assert_eq!(parse_i32(b"-1234")?, -1234);
parse_i32(b"1234 ").unwrap_err();
parse_i32(b"-1234 ").unwrap_err();
parse_i32(b"123456789000").unwrap_err();
Ok(())
}
#[test]
fn test_float() -> Result<(), OdsError> {
assert_eq!(parse_f64(b"1234")?, 1234.);
assert_eq!(parse_f64(b"-1234")?, -1234.);
assert_eq!(parse_f64(b"123456789000")?, 123456789000.);
assert_eq!(parse_f64(b"1234.5678")?, 1234.5678);
parse_f64(b"1234 ").unwrap_err();
parse_f64(b"-1234 ").unwrap_err();
Ok(())
}
#[test]
fn test_datetime() -> Result<(), OdsError> {
assert_eq!(
parse_datetime(b"19999-01-01")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(19999, 1, 1).expect("date"),
NaiveTime::default()
)
);
assert_eq!(
parse_datetime(b"1999-01-01")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(1999, 1, 1).expect("date"),
NaiveTime::default()
)
);
assert_eq!(
parse_datetime(b"-45-01-01")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(-45, 1, 1).expect("date"),
NaiveTime::default()
)
);
assert_eq!(
parse_datetime(b"2004-02-29")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2004, 2, 29).expect("date"),
NaiveTime::default()
)
);
assert_eq!(
parse_datetime(b"2000-02-29")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2000, 2, 29).expect("date"),
NaiveTime::default()
)
);
assert_eq!(
parse_datetime(b"2000-01-01T11:22:33")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2000, 1, 1).expect("date"),
NaiveTime::from_hms_opt(11, 22, 33).expect("time")
)
);
assert_eq!(
parse_datetime(b"2000-01-01T11:22:33.1234")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2000, 1, 1).expect("date"),
NaiveTime::from_hms_micro_opt(11, 22, 33, 123400).expect("time")
)
);
assert_eq!(
parse_datetime(b"2000-01-01T11:22:33.123456789111")?,
NaiveDateTime::new(
NaiveDate::from_ymd_opt(2000, 1, 1).expect("date"),
NaiveTime::from_hms_nano_opt(11, 22, 33, 123456789).expect("time")
)
);
Ok(())
}
#[test]
fn test_duration() -> Result<(), OdsError> {
assert_eq!(parse_duration(b"PT12H12M12S")?.num_milliseconds(), 43932000);
assert_eq!(
parse_duration(b"PT12H12M12.223S")?.num_milliseconds(),
43932223
);
Ok(())
}
#[test]
fn test_bool() -> Result<(), OdsError> {
assert!(parse_bool(b"true")?);
assert!(!(parse_bool(b"false")?));
parse_bool(b"ffoso").unwrap_err();
Ok(())
}
#[test]
fn test_nano() -> Result<(), OdsError> {
assert_eq!(token_nano(b"123")?.1, 123000000i64);
assert_eq!(token_nano(b"123456789")?.1, 123456789i64);
assert_eq!(token_nano(b"1234567897777")?.1, 123456789i64);
Ok(())
}
}