From 7cd5558d2b19c65e2f4af045b6ee0f07d6f0cec9 Mon Sep 17 00:00:00 2001 From: Tpt Date: Fri, 17 Jan 2020 16:29:59 +0100 Subject: [PATCH] Adds extra compiler and Clippy lints --- lib/src/lib.rs | 66 +++++- lib/src/model/blank_node.rs | 4 +- lib/src/model/graph.rs | 32 ++- lib/src/model/isomorphism.rs | 14 +- lib/src/model/literal.rs | 14 +- lib/src/model/triple.rs | 14 +- lib/src/model/xsd/date_time.rs | 282 ++++++++++++------------ lib/src/model/xsd/decimal.rs | 298 +++++++++++++------------- lib/src/model/xsd/duration.rs | 137 ++++++------ lib/src/model/xsd/parser.rs | 45 ++-- lib/src/repository.rs | 4 +- lib/src/sparql/algebra.rs | 81 +++---- lib/src/sparql/eval.rs | 176 ++++++++------- lib/src/sparql/mod.rs | 22 +- lib/src/sparql/parser.rs | 3 +- lib/src/sparql/plan.rs | 12 +- lib/src/sparql/plan_builder.rs | 45 ++-- lib/src/sparql/sparql_grammar.rustpeg | 6 +- lib/src/sparql/xml_results.rs | 6 +- lib/src/store/memory.rs | 4 +- lib/src/store/mod.rs | 16 +- lib/src/store/numeric_encoder.rs | 28 +-- lib/src/store/rocksdb.rs | 14 +- 23 files changed, 705 insertions(+), 618 deletions(-) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 5893e495..0930e61a 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -34,6 +34,70 @@ //! } //! # Result::Ok(()) //! ``` +#![deny( + future_incompatible, + nonstandard_style, + //TODO rust_2018_idioms, + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + //TODO unsafe_code, + unused_qualifications +)] +#![warn( + clippy::unimplemented, + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_sign_loss, + clippy::checked_conversions, + clippy::decimal_literal_representation, + //TODO clippy::doc_markdown, + clippy::empty_enum, + clippy::expl_impl_clone_on_copy, + clippy::explicit_into_iter_loop, + clippy::explicit_iter_loop, + //TODO clippy::fallible_impl_from, + clippy::filter_map, + clippy::filter_map_next, + clippy::find_map, + clippy::get_unwrap, + clippy::if_not_else, + clippy::inline_always, + clippy::invalid_upcast_comparisons, + clippy::items_after_statements, + clippy::map_flatten, + //TODO clippy::match_same_arms, + clippy::maybe_infinite_iter, + clippy::mem_forget, + clippy::multiple_inherent_impl, + clippy::mut_mut, + clippy::needless_borrow, + clippy::needless_continue, + clippy::needless_pass_by_value, + clippy::non_ascii_literal, + clippy::option_map_unwrap_or, + clippy::option_map_unwrap_or_else, + clippy::path_buf_push_overwrite, + clippy::print_stdout, + clippy::pub_enum_variant_names, + //TODO clippy::redundant_closure_for_method_calls, + clippy::replace_consts, + clippy::result_map_unwrap_or_else, + // clippy::shadow_reuse, + // clippy::shadow_same, + // clippy::shadow_unrelated, + // clippy::single_match_else, + clippy::string_add, + clippy::string_add_assign, + clippy::type_repetition_in_bounds, + clippy::unicode_not_nfc, + //TODO clippy::unseparated_literal_suffix, + clippy::used_underscore_binding, + clippy::wildcard_dependencies, + clippy::wrong_pub_self_convention, +)] pub mod model; mod repository; @@ -42,7 +106,7 @@ pub(crate) mod store; mod syntax; pub use failure::Error; -pub type Result = ::std::result::Result; +pub type Result = ::std::result::Result; pub use crate::repository::Repository; pub use crate::repository::RepositoryConnection; pub use crate::repository::RepositoryTransaction; diff --git a/lib/src/model/blank_node.rs b/lib/src/model/blank_node.rs index 1afceb0c..e485fe24 100644 --- a/lib/src/model/blank_node.rs +++ b/lib/src/model/blank_node.rs @@ -14,7 +14,7 @@ use std::str; /// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation. /// `BlankNode::default().to_string()` should return something like `_:00112233445566778899aabbccddeeff` /// -#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)] +#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)] pub struct BlankNode { id: u128, str: [u8; 32], @@ -34,7 +34,7 @@ impl BlankNode { } /// Returns the underlying ID of this blank node - pub(crate) fn id(&self) -> u128 { + pub(crate) const fn id(&self) -> u128 { self.id } } diff --git a/lib/src/model/graph.rs b/lib/src/model/graph.rs index d773c5e6..7c7c5fdf 100644 --- a/lib/src/model/graph.rs +++ b/lib/src/model/graph.rs @@ -43,9 +43,13 @@ impl SimpleGraph { subject: &'a NamedOrBlankNode, predicate: &'a NamedNode, ) -> impl Iterator + 'a { - self.iter() - .filter(move |t| t.subject() == subject && t.predicate() == predicate) - .map(|t| t.object()) + self.iter().filter_map(move |t| { + if t.subject() == subject && t.predicate() == predicate { + Some(t.object()) + } else { + None + } + }) } pub fn object_for_subject_predicate<'a>( @@ -62,9 +66,13 @@ impl SimpleGraph { subject: &'a NamedOrBlankNode, object: &'a Term, ) -> impl Iterator + 'a { - self.iter() - .filter(move |t| t.subject() == subject && t.object() == object) - .map(|t| t.predicate()) + self.iter().filter_map(move |t| { + if t.subject() == subject && t.object() == object { + Some(t.predicate()) + } else { + None + } + }) } pub fn triples_for_predicate<'a>( @@ -79,9 +87,13 @@ impl SimpleGraph { predicate: &'a NamedNode, object: &'a Term, ) -> impl Iterator + 'a { - self.iter() - .filter(move |t| t.predicate() == predicate && t.object() == object) - .map(|t| t.subject()) + self.iter().filter_map(move |t| { + if t.predicate() == predicate && t.object() == object { + Some(t.subject()) + } else { + None + } + }) } pub fn triples_for_object<'a>( @@ -157,7 +169,7 @@ impl Extend for SimpleGraph { } impl fmt::Display for SimpleGraph { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { for t in &self.triples { writeln!(f, "{}", t)?; } diff --git a/lib/src/model/isomorphism.rs b/lib/src/model/isomorphism.rs index 995d37f9..63d79e01 100644 --- a/lib/src/model/isomorphism.rs +++ b/lib/src/model/isomorphism.rs @@ -48,7 +48,7 @@ fn split_hash_buckets<'a>( new_bnodes_by_hash.insert(hash, bnodes); // Nothing to improve } else { for bnode in bnodes { - let mut starts = vec![NamedOrBlankNode::from(bnode.clone())]; + let mut starts = vec![NamedOrBlankNode::from(*bnode)]; for _ in 0..distance { let mut new_starts = Vec::default(); for s in starts { @@ -71,7 +71,7 @@ fn split_hash_buckets<'a>( hash.hash(&mut hasher); // We start with the previous hash // NB: we need to sort the triples to have the same hash - let mut po_set: BTreeSet = BTreeSet::default(); + let mut po_set: BTreeSet> = BTreeSet::default(); for start in &starts { for po in predicate_objects_for_subject(graph, start) { match &po.object { @@ -86,7 +86,7 @@ fn split_hash_buckets<'a>( po.hash(&mut hasher); } - let mut sp_set: BTreeSet = BTreeSet::default(); + let mut sp_set: BTreeSet> = BTreeSet::default(); let term_starts: Vec<_> = starts.into_iter().map(|t| t.into()).collect(); for start in &term_starts { for sp in subject_predicates_for_object(graph, start) { @@ -123,7 +123,7 @@ fn build_and_check_containment_from_hashes<'a>( ) -> bool { if let Some((a_node, remaining_a_node)) = current_a_nodes.split_last() { let b_nodes = current_b_nodes.iter().cloned().collect::>(); - for b_node in b_nodes.into_iter() { + for b_node in b_nodes { current_b_nodes.remove(b_node); a_to_b_mapping.insert(a_node, b_node); if check_is_contained_focused(a_to_b_mapping, a_node, a, b) @@ -260,11 +260,7 @@ pub fn are_graphs_isomorphic(a: &SimpleGraph, b: &SimpleGraph) -> bool { b_bnodes_by_hash.insert(0, graph_blank_nodes(&b_bnodes_triples)); for distance in 0..5 { - let max_size = a_bnodes_by_hash - .values() - .map(|l| l.len()) - .max() - .unwrap_or(0); + let max_size = a_bnodes_by_hash.values().map(Vec::len).max().unwrap_or(0); if max_size < 2 { break; // We only have small buckets } diff --git a/lib/src/model/literal.rs b/lib/src/model/literal.rs index b2c0c4d8..9aa0096e 100644 --- a/lib/src/model/literal.rs +++ b/lib/src/model/literal.rs @@ -275,15 +275,15 @@ impl From for Literal { impl<'a> From<&'a Literal> for rio::Literal<'a> { fn from(literal: &'a Literal) -> Self { if literal.is_plain() { - literal - .language() - .map(|lang| rio::Literal::LanguageTaggedString { + literal.language().map_or_else( + || rio::Literal::Simple { value: literal.value(), - language: &lang, - }) - .unwrap_or_else(|| rio::Literal::Simple { + }, + |lang| rio::Literal::LanguageTaggedString { value: literal.value(), - }) + language: lang, + }, + ) } else { rio::Literal::Typed { value: literal.value(), diff --git a/lib/src/model/triple.rs b/lib/src/model/triple.rs index ec334e80..ef58448e 100644 --- a/lib/src/model/triple.rs +++ b/lib/src/model/triple.rs @@ -162,7 +162,7 @@ impl Triple { } /// The [subject](https://www.w3.org/TR/rdf11-concepts/#dfn-subject) of this triple - pub fn subject(&self) -> &NamedOrBlankNode { + pub const fn subject(&self) -> &NamedOrBlankNode { &self.subject } @@ -172,7 +172,7 @@ impl Triple { } /// The [predicate](https://www.w3.org/TR/rdf11-concepts/#dfn-predicate) of this triple - pub fn predicate(&self) -> &NamedNode { + pub const fn predicate(&self) -> &NamedNode { &self.predicate } @@ -182,7 +182,7 @@ impl Triple { } /// The [object](https://www.w3.org/TR/rdf11-concepts/#dfn-object) of this triple - pub fn object(&self) -> &Term { + pub const fn object(&self) -> &Term { &self.object } @@ -244,7 +244,7 @@ impl Quad { } /// The [subject](https://www.w3.org/TR/rdf11-concepts/#dfn-subject) of this triple - pub fn subject(&self) -> &NamedOrBlankNode { + pub const fn subject(&self) -> &NamedOrBlankNode { &self.subject } @@ -254,7 +254,7 @@ impl Quad { } /// The [predicate](https://www.w3.org/TR/rdf11-concepts/#dfn-predicate) of this triple - pub fn predicate(&self) -> &NamedNode { + pub const fn predicate(&self) -> &NamedNode { &self.predicate } @@ -264,7 +264,7 @@ impl Quad { } /// The [object](https://www.w3.org/TR/rdf11-concepts/#dfn-object) of this triple - pub fn object(&self) -> &Term { + pub const fn object(&self) -> &Term { &self.object } @@ -274,7 +274,7 @@ impl Quad { } /// The name of the RDF [graph](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-graph) in which the triple is or None if it is in the [default graph](https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph) - pub fn graph_name(&self) -> &Option { + pub const fn graph_name(&self) -> &Option { &self.graph_name } diff --git a/lib/src/model/xsd/date_time.rs b/lib/src/model/xsd/date_time.rs index e16d79bc..a4b71790 100644 --- a/lib/src/model/xsd/date_time.rs +++ b/lib/src/model/xsd/date_time.rs @@ -26,7 +26,7 @@ impl DateTime { timezone_offset: Option, ) -> Result { Ok(Self { - timestamp: Timestamp::new(DateTimeSevenPropertyModel { + timestamp: Timestamp::new(&DateTimeSevenPropertyModel { year: Some(year), month: Some(month), day: Some(day), @@ -49,56 +49,7 @@ impl DateTime { timestamp: Timestamp::from_le_bytes(bytes), } } -} - -/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). -impl From for DateTime { - fn from(date: Date) -> Self { - DateTime::new( - date.year(), - date.month(), - date.day(), - 0, - 0, - Decimal::default(), - date.timezone_offset(), - ) - .unwrap() - } -} - -impl FromStr for DateTime { - type Err = XsdParseError; - - fn from_str(input: &str) -> Result { - parse_value(date_time_lexical_rep, input) - } -} - -impl fmt::Display for DateTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let year = self.year(); - if year < 0 { - write!(f, "-")?; - } - write!( - f, - "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", - year.abs(), - self.month(), - self.day(), - self.hour(), - self.minute(), - self.second() - )?; - if let Some(timezone_offset) = self.timezone_offset() { - write!(f, "{}", timezone_offset)?; - } - Ok(()) - } -} -impl DateTime { /// [fn:year-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-year-from-dateTime) pub fn year(&self) -> i64 { self.timestamp.year() @@ -168,7 +119,8 @@ impl DateTime { }) } else { Some(Self { - timestamp: Timestamp::new(date_time_plus_duration(rhs, self.properties())?).ok()?, + timestamp: Timestamp::new(&date_time_plus_duration(rhs, &self.properties())?) + .ok()?, }) } } @@ -182,13 +134,60 @@ impl DateTime { }) } else { Some(Self { - timestamp: Timestamp::new(date_time_plus_duration(-rhs, self.properties())?) + timestamp: Timestamp::new(&date_time_plus_duration(-rhs, &self.properties())?) .ok()?, }) } } } +/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). +impl From for DateTime { + fn from(date: Date) -> Self { + DateTime::new( + date.year(), + date.month(), + date.day(), + 0, + 0, + Decimal::default(), + date.timezone_offset(), + ) + .unwrap() + } +} + +impl FromStr for DateTime { + type Err = XsdParseError; + + fn from_str(input: &str) -> Result { + parse_value(date_time_lexical_rep, input) + } +} + +impl fmt::Display for DateTime { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let year = self.year(); + if year < 0 { + write!(f, "-")?; + } + write!( + f, + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", + year.abs(), + self.month(), + self.day(), + self.hour(), + self.minute(), + self.second() + )?; + if let Some(timezone_offset) = self.timezone_offset() { + write!(f, "{}", timezone_offset)?; + } + Ok(()) + } +} + /// [XML Schema `time` datatype](https://www.w3.org/TR/xmlschema11-2/#time) implementation. #[derive(Eq, PartialEq, PartialOrd, Debug, Clone, Copy, Hash)] pub struct Time { @@ -206,7 +205,7 @@ impl Time { hour = 0; } Ok(Self { - timestamp: Timestamp::new(DateTimeSevenPropertyModel { + timestamp: Timestamp::new(&DateTimeSevenPropertyModel { year: None, month: None, day: None, @@ -223,46 +222,7 @@ impl Time { timestamp: Timestamp::from_le_bytes(bytes), } } -} - -/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). -impl From for Time { - fn from(date_time: DateTime) -> Self { - Time::new( - date_time.hour(), - date_time.minute(), - date_time.second(), - date_time.timezone_offset(), - ) - .unwrap() - } -} - -impl FromStr for Time { - type Err = XsdParseError; - - fn from_str(input: &str) -> Result { - parse_value(time_lexical_rep, input) - } -} -impl fmt::Display for Time { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{:02}:{:02}:{:02}", - self.hour(), - self.minute(), - self.second() - )?; - if let Some(timezone_offset) = self.timezone_offset() { - write!(f, "{}", timezone_offset)?; - } - Ok(()) - } -} - -impl Time { /// [fn:hour-from-time](https://www.w3.org/TR/xpath-functions/#func-hour-from-time) pub fn hour(&self) -> u8 { self.timestamp.hour() @@ -335,6 +295,43 @@ impl Time { } } +/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). +impl From for Time { + fn from(date_time: DateTime) -> Self { + Time::new( + date_time.hour(), + date_time.minute(), + date_time.second(), + date_time.timezone_offset(), + ) + .unwrap() + } +} + +impl FromStr for Time { + type Err = XsdParseError; + + fn from_str(input: &str) -> Result { + parse_value(time_lexical_rep, input) + } +} + +impl fmt::Display for Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:02}:{:02}:{:02}", + self.hour(), + self.minute(), + self.second() + )?; + if let Some(timezone_offset) = self.timezone_offset() { + write!(f, "{}", timezone_offset)?; + } + Ok(()) + } +} + /// [XML Schema `date` datatype](https://www.w3.org/TR/xmlschema11-2/#date) implementation. #[derive(Eq, PartialEq, PartialOrd, Debug, Clone, Copy, Hash)] pub struct Date { @@ -349,7 +346,7 @@ impl Date { timezone_offset: Option, ) -> Result { Ok(Self { - timestamp: Timestamp::new(DateTimeSevenPropertyModel { + timestamp: Timestamp::new(&DateTimeSevenPropertyModel { year: Some(year), month: Some(month), day: Some(day), @@ -366,44 +363,7 @@ impl Date { timestamp: Timestamp::from_le_bytes(bytes), } } -} - -/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). -impl From for Date { - fn from(date_time: DateTime) -> Self { - Date::new( - date_time.year(), - date_time.month(), - date_time.day(), - date_time.timezone_offset(), - ) - .unwrap() - } -} - -impl FromStr for Date { - type Err = XsdParseError; - fn from_str(input: &str) -> Result { - parse_value(date_lexical_rep, input) - } -} - -impl fmt::Display for Date { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let year = self.year(); - if year < 0 { - write!(f, "-")?; - } - write!(f, "{:04}-{:02}-{:02}", year.abs(), self.month(), self.day())?; - if let Some(timezone_offset) = self.timezone_offset() { - write!(f, "{}", timezone_offset)?; - } - Ok(()) - } -} - -impl Date { /// [fn:year-from-date](https://www.w3.org/TR/xpath-functions/#func-year-from-date) pub fn year(&self) -> i64 { self.timestamp.year() @@ -448,6 +408,41 @@ impl Date { } } +/// Conversion according to [XPath cast rules](https://www.w3.org/TR/xpath-functions/#casting-to-datetimes). +impl From for Date { + fn from(date_time: DateTime) -> Self { + Date::new( + date_time.year(), + date_time.month(), + date_time.day(), + date_time.timezone_offset(), + ) + .unwrap() + } +} + +impl FromStr for Date { + type Err = XsdParseError; + + fn from_str(input: &str) -> Result { + parse_value(date_lexical_rep, input) + } +} + +impl fmt::Display for Date { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let year = self.year(); + if year < 0 { + write!(f, "-")?; + } + write!(f, "{:04}-{:02}-{:02}", year.abs(), self.month(), self.day())?; + if let Some(timezone_offset) = self.timezone_offset() { + write!(f, "{}", timezone_offset)?; + } + Ok(()) + } +} + #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] pub struct TimezoneOffset { offset: i16, // in minute with respect to UTC @@ -468,6 +463,10 @@ impl TimezoneOffset { offset: i16::from_le_bytes(bytes), } } + + pub fn to_le_bytes(self) -> [u8; 2] { + self.offset.to_le_bytes() + } } impl From for TimezoneOffset { @@ -492,12 +491,6 @@ impl fmt::Display for TimezoneOffset { } } -impl TimezoneOffset { - pub fn to_le_bytes(self) -> [u8; 2] { - self.offset.to_le_bytes() - } -} - /// [The Date/time Seven-property model](https://www.w3.org/TR/xmlschema11-2/#dt-dt-7PropMod) #[derive(Eq, PartialEq, Debug, Clone, Hash)] struct DateTimeSevenPropertyModel { @@ -564,7 +557,7 @@ impl Hash for Timestamp { } impl Timestamp { - fn new(props: DateTimeSevenPropertyModel) -> Result { + fn new(props: &DateTimeSevenPropertyModel) -> Result { // Validation if let (Some(day), Some(month)) = (props.day, props.month) { // Constraint: Day-of-month Values @@ -585,14 +578,14 @@ impl Timestamp { fn now() -> Result { Timestamp::new( - date_time_plus_duration( + &date_time_plus_duration( SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH)? .try_into() .map_err(|_| DateTimeError { kind: DateTimeErrorKind::Overflow, })?, - DateTimeSevenPropertyModel { + &DateTimeSevenPropertyModel { year: Some(1970), month: Some(1), day: Some(1), @@ -624,6 +617,7 @@ impl Timestamp { } } + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] fn year_month_day(&self) -> (i64, u8, u8) { let mut days = (self.value.as_i128() + i128::from( @@ -697,6 +691,7 @@ impl Timestamp { day } + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] fn hour(&self) -> u8 { (((self.value.as_i128() + i128::from( @@ -708,6 +703,7 @@ impl Timestamp { / 3600) as u8 } + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] fn minute(&self) -> u8 { (((self.value.as_i128() + i128::from( @@ -723,7 +719,7 @@ impl Timestamp { self.value.checked_rem_euclid(60).unwrap().abs() } - fn timezone_offset(&self) -> Option { + const fn timezone_offset(&self) -> Option { self.timezone_offset } @@ -771,7 +767,7 @@ fn normalize_month(yr: i64, mo: i64) -> Option<(i64, u8)> { // Needed to make it work with negative durations let yr = yr.checked_add(mo.checked_sub(1)?.checked_div(12)?.checked_sub(1)?)?; let mo = u8::try_from( - 12i64 + 12_i64 .checked_add(mo.checked_sub(1)?.checked_rem(12)?)? .checked_add(1)?, ) @@ -847,7 +843,7 @@ fn days_in_month(y: Option, m: u8) -> u8 { /// The [dateTimePlusDuration](https://www.w3.org/TR/xmlschema11-2/#vp-dt-dateTimePlusDuration) function fn date_time_plus_duration( du: Duration, - dt: DateTimeSevenPropertyModel, + dt: &DateTimeSevenPropertyModel, ) -> Option { let yr = dt.year.unwrap_or(1); let mo = dt.month.unwrap_or(1); @@ -874,7 +870,7 @@ fn date_time_plus_duration( } /// The [timeOnTimeline](https://www.w3.org/TR/xmlschema11-2/#vp-dt-timeOnTimeline) function -fn time_on_timeline(props: DateTimeSevenPropertyModel) -> Option { +fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option { let yr = props.year.map_or(1971, |y| y - 1); let mo = props.month.unwrap_or(12); let da = props diff --git a/lib/src/model/xsd/decimal.rs b/lib/src/model/xsd/decimal.rs index 1f0aa737..29560217 100644 --- a/lib/src/model/xsd/decimal.rs +++ b/lib/src/model/xsd/decimal.rs @@ -28,13 +28,14 @@ pub struct Decimal { impl Decimal { /// Constructs the decimal i / 10^n + #[allow(clippy::cast_possible_truncation)] pub fn new(i: i128, n: u32) -> Result { if n > DECIMAL_PART_DIGITS as u32 { //TODO: check if end with zeros? return Err(DecimalOverflowError); } Ok(Self { - value: i.checked_div(10i128.pow(n)).ok_or(DecimalOverflowError)?, + value: i.checked_div(10_i128.pow(n)).ok_or(DecimalOverflowError)?, }) } @@ -44,6 +45,150 @@ impl Decimal { value: i128::from_le_bytes(bytes), } } + + pub fn to_le_bytes(&self) -> [u8; 16] { + self.value.to_le_bytes() + } + + /// [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add) + #[inline] + pub fn checked_add(&self, rhs: impl Into) -> Option { + Some(Self { + value: self.value.checked_add(rhs.into().value)?, + }) + } + + /// [op:numeric-subtract](https://www.w3.org/TR/xpath-functions/#func-numeric-subtract) + #[inline] + pub fn checked_sub(&self, rhs: impl Into) -> Option { + Some(Self { + value: self.value.checked_sub(rhs.into().value)?, + }) + } + + /// [op:numeric-multiply](https://www.w3.org/TR/xpath-functions/#func-numeric-multiply) + #[inline] + pub fn checked_mul(&self, rhs: impl Into) -> Option { + //TODO: better algorithm to keep precision + Some(Self { + value: self + .value + .checked_div(DECIMAL_PART_HALF_POW)? + .checked_mul(rhs.into().value.checked_div(DECIMAL_PART_HALF_POW)?)?, + }) + } + + /// [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide) + #[inline] + pub fn checked_div(&self, rhs: impl Into) -> Option { + //TODO: better algorithm to keep precision + Some(Self { + value: self + .value + .checked_mul(DECIMAL_PART_HALF_POW)? + .checked_div(rhs.into().value)? + .checked_mul(DECIMAL_PART_HALF_POW)?, + }) + } + + /// TODO: XSD? is well defined for not integer + pub fn checked_rem(&self, rhs: impl Into) -> Option { + Some(Self { + value: self.value.checked_rem(rhs.into().value)?, + }) + } + + pub fn checked_rem_euclid(&self, rhs: impl Into) -> Option { + Some(Self { + value: self.value.checked_rem_euclid(rhs.into().value)?, + }) + } + + /// [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs) + #[inline] + pub const fn abs(&self) -> Decimal { + Self { + value: self.value.abs(), + } + } + + /// [fn:round](https://www.w3.org/TR/xpath-functions/#func-round) + #[inline] + pub fn round(&self) -> Decimal { + let value = self.value / DECIMAL_PART_POW_MINUS_ONE; + Self { + value: if value >= 0 { + (value / 10 + if value % 10 >= 5 { 1 } else { 0 }) * DECIMAL_PART_POW + } else { + (value / 10 + if -value % 10 > 5 { -1 } else { 0 }) * DECIMAL_PART_POW + }, + } + } + + /// [fn:ceiling](https://www.w3.org/TR/xpath-functions/#func-ceiling) + #[inline] + pub fn ceil(&self) -> Decimal { + Self { + value: if self.value >= 0 && self.value % DECIMAL_PART_POW != 0 { + (self.value / DECIMAL_PART_POW + 1) * DECIMAL_PART_POW + } else { + (self.value / DECIMAL_PART_POW) * DECIMAL_PART_POW + }, + } + } + + /// [fn:floor](https://www.w3.org/TR/xpath-functions/#func-floor) + #[inline] + pub fn floor(&self) -> Decimal { + Self { + value: if self.value >= 0 || self.value % DECIMAL_PART_POW == 0 { + (self.value / DECIMAL_PART_POW) * DECIMAL_PART_POW + } else { + (self.value / DECIMAL_PART_POW - 1) * DECIMAL_PART_POW + }, + } + } + + pub const fn is_negative(&self) -> bool { + self.value < 0 + } + + pub const fn is_positive(&self) -> bool { + self.value > 0 + } + + /// Creates a `Decimal` from a `f32` without taking care of precision + #[inline] + pub(crate) fn from_f32(v: f32) -> Self { + Self::from_f64(v.into()) + } + + /// Creates a `f32` from a `Decimal` without taking care of precision + #[inline] + #[allow(clippy::cast_possible_truncation)] + pub fn to_f32(&self) -> f32 { + self.to_f64() as f32 + } + + /// Creates a `Decimal` from a `f64` without taking care of precision + #[inline] + #[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)] + pub(crate) fn from_f64(v: f64) -> Self { + Self { + value: (v * (DECIMAL_PART_POW as f64)) as i128, + } + } + + /// Creates a `f64` from a `Decimal` without taking care of precision + #[inline] + #[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)] + pub fn to_f64(&self) -> f64 { + (self.value as f64) / (DECIMAL_PART_POW as f64) + } + + pub(super) const fn as_i128(&self) -> i128 { + self.value / DECIMAL_PART_POW + } } impl From for Decimal { @@ -146,7 +291,7 @@ impl FromStr for Decimal { _ => (1, 0), }; - let mut value = 0i128; + let mut value = 0_i128; let mut with_before_dot = false; while cursor < input.len() && b'0' <= input[cursor] && input[cursor] <= b'9' { value = value @@ -246,7 +391,7 @@ impl From for ParseDecimalError { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct DecimalOverflowError; impl fmt::Display for DecimalOverflowError { @@ -259,6 +404,7 @@ impl Error for DecimalOverflowError {} impl fmt::Display for Decimal { /// Formats the decimal following its canonical representation + #[allow(clippy::cast_possible_truncation)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.value == 0 { return if let Some(width) = f.width() { @@ -289,8 +435,7 @@ impl fmt::Display for Decimal { .iter() .cloned() .enumerate() - .find(|(_, v)| *v != b'0') - .map(|(i, _)| i) + .find_map(|(i, v)| if v == b'0' { None } else { Some(i) }) .unwrap_or(40); if last_non_zero >= DECIMAL_PART_DIGITS { @@ -343,149 +488,6 @@ impl Neg for Decimal { } } -impl Decimal { - pub fn to_le_bytes(&self) -> [u8; 16] { - self.value.to_le_bytes() - } - - /// [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add) - #[inline] - pub fn checked_add(&self, rhs: impl Into) -> Option { - Some(Self { - value: self.value.checked_add(rhs.into().value)?, - }) - } - - /// [op:numeric-subtract](https://www.w3.org/TR/xpath-functions/#func-numeric-subtract) - #[inline] - pub fn checked_sub(&self, rhs: impl Into) -> Option { - Some(Self { - value: self.value.checked_sub(rhs.into().value)?, - }) - } - - /// [op:numeric-multiply](https://www.w3.org/TR/xpath-functions/#func-numeric-multiply) - #[inline] - pub fn checked_mul(&self, rhs: impl Into) -> Option { - //TODO: better algorithm to keep precision - Some(Self { - value: self - .value - .checked_div(DECIMAL_PART_HALF_POW)? - .checked_mul(rhs.into().value.checked_div(DECIMAL_PART_HALF_POW)?)?, - }) - } - - /// [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide) - #[inline] - pub fn checked_div(&self, rhs: impl Into) -> Option { - //TODO: better algorithm to keep precision - Some(Self { - value: self - .value - .checked_mul(DECIMAL_PART_HALF_POW)? - .checked_div(rhs.into().value)? - .checked_mul(DECIMAL_PART_HALF_POW)?, - }) - } - - /// TODO: XSD? is well defined for not integer - pub fn checked_rem(&self, rhs: impl Into) -> Option { - Some(Self { - value: self.value.checked_rem(rhs.into().value)?, - }) - } - - pub fn checked_rem_euclid(&self, rhs: impl Into) -> Option { - Some(Self { - value: self.value.checked_rem_euclid(rhs.into().value)?, - }) - } - - /// [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs) - #[inline] - pub fn abs(&self) -> Decimal { - Self { - value: self.value.abs(), - } - } - - /// [fn:round](https://www.w3.org/TR/xpath-functions/#func-round) - #[inline] - pub fn round(&self) -> Decimal { - let value = self.value / DECIMAL_PART_POW_MINUS_ONE; - Self { - value: if value >= 0 { - (value / 10 + if value % 10 >= 5 { 1 } else { 0 }) * DECIMAL_PART_POW - } else { - (value / 10 + if -value % 10 > 5 { -1 } else { 0 }) * DECIMAL_PART_POW - }, - } - } - - /// [fn:ceiling](https://www.w3.org/TR/xpath-functions/#func-ceiling) - #[inline] - pub fn ceil(&self) -> Decimal { - Self { - value: if self.value >= 0 && self.value % DECIMAL_PART_POW != 0 { - (self.value / DECIMAL_PART_POW + 1) * DECIMAL_PART_POW - } else { - (self.value / DECIMAL_PART_POW) * DECIMAL_PART_POW - }, - } - } - - /// [fn:floor](https://www.w3.org/TR/xpath-functions/#func-floor) - #[inline] - pub fn floor(&self) -> Decimal { - Self { - value: if self.value >= 0 || self.value % DECIMAL_PART_POW == 0 { - (self.value / DECIMAL_PART_POW) * DECIMAL_PART_POW - } else { - (self.value / DECIMAL_PART_POW - 1) * DECIMAL_PART_POW - }, - } - } - - pub fn is_negative(&self) -> bool { - self.value < 0 - } - - pub fn is_positive(&self) -> bool { - self.value > 0 - } - - /// Creates a `Decimal` from a `f32` without taking care of precision - #[inline] - pub(crate) fn from_f32(v: f32) -> Self { - Self::from_f64(v.into()) - } - - /// Creates a `f32` from a `Decimal` without taking care of precision - #[inline] - pub fn to_f32(&self) -> f32 { - self.to_f64() as f32 - } - - /// Creates a `Decimal` from a `f64` without taking care of precision - #[inline] - pub(crate) fn from_f64(v: f64) -> Self { - Self { - value: (v * (DECIMAL_PART_POW as f64)) as i128, - } - } - - /// Creates a `f64` from a `Decimal` without taking care of precision - #[inline] - pub fn to_f64(&self) -> f64 { - (self.value as f64) / (DECIMAL_PART_POW as f64) - } - - pub(super) fn as_i128(&self) -> i128 { - self.value / DECIMAL_PART_POW - } -} - impl TryFrom for i64 { type Error = DecimalOverflowError; diff --git a/lib/src/model/xsd/duration.rs b/lib/src/model/xsd/duration.rs index ec78f902..7a8aaf8b 100644 --- a/lib/src/model/xsd/duration.rs +++ b/lib/src/model/xsd/duration.rs @@ -39,6 +39,72 @@ impl Duration { seconds: Decimal::from_le_bytes(seconds), } } + + /// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration) + pub fn years(&self) -> i64 { + self.months / 12 + } + + /// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration) + pub fn months(&self) -> i64 { + self.months % 12 + } + + /// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration) + #[allow(clippy::cast_possible_truncation)] + pub fn days(&self) -> i64 { + (self.seconds.as_i128() / 86400) as i64 + } + + /// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration) + #[allow(clippy::cast_possible_truncation)] + pub fn hours(&self) -> i64 { + ((self.seconds.as_i128() % 86400) / 3600) as i64 + } + + /// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration) + #[allow(clippy::cast_possible_truncation)] + pub fn minutes(&self) -> i64 { + ((self.seconds.as_i128() % 3600) / 60) as i64 + } + + /// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration) + pub fn seconds(&self) -> Decimal { + self.seconds.checked_rem(60).unwrap() + } + + pub(super) const fn all_months(&self) -> i64 { + self.months + } + + pub(super) const fn all_seconds(&self) -> Decimal { + self.seconds + } + + pub fn to_le_bytes(&self) -> [u8; 24] { + let mut bytes = [0; 24]; + bytes[0..8].copy_from_slice(&self.months.to_le_bytes()); + bytes[8..24].copy_from_slice(&self.seconds.to_le_bytes()); + bytes + } + + /// [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations) and [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations) + pub fn checked_add(&self, rhs: impl Into) -> Option { + let rhs = rhs.into(); + Some(Self { + months: self.months.checked_add(rhs.months)?, + seconds: self.seconds.checked_add(rhs.seconds)?, + }) + } + + /// [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations) and [op:subtract-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations) + pub fn checked_sub(&self, rhs: impl Into) -> Option { + let rhs = rhs.into(); + Some(Self { + months: self.months.checked_sub(rhs.months)?, + seconds: self.seconds.checked_sub(rhs.seconds)?, + }) + } } impl TryFrom for Duration { @@ -85,10 +151,10 @@ impl fmt::Display for Duration { let m = ym % 12; if y != 0 { - if m != 0 { - write!(f, "{}Y{}M", y, m)?; - } else { + if m == 0 { write!(f, "{}Y", y)?; + } else { + write!(f, "{}Y{}M", y, m)?; } } else if m != 0 || ss == 0.into() { write!(f, "{}M", m)?; @@ -154,71 +220,6 @@ impl PartialOrd for Duration { } } -impl Duration { - /// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration) - pub fn years(&self) -> i64 { - self.months / 12 - } - - /// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration) - pub fn months(&self) -> i64 { - self.months % 12 - } - - /// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration) - pub fn days(&self) -> i64 { - (self.seconds.as_i128() / 86400) as i64 - } - - /// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration) - pub fn hours(&self) -> i64 { - ((self.seconds.as_i128() % 86400) / 3600) as i64 - } - - /// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration) - pub fn minutes(&self) -> i64 { - ((self.seconds.as_i128() % 3600) / 60) as i64 - } - - /// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration) - pub fn seconds(&self) -> Decimal { - self.seconds.checked_rem(60).unwrap() - } - - pub(super) fn all_months(&self) -> i64 { - self.months - } - - pub(super) fn all_seconds(&self) -> Decimal { - self.seconds - } - - pub fn to_le_bytes(&self) -> [u8; 24] { - let mut bytes = [0; 24]; - bytes[0..8].copy_from_slice(&self.months.to_le_bytes()); - bytes[8..24].copy_from_slice(&self.seconds.to_le_bytes()); - bytes - } - - /// [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations) and [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations) - pub fn checked_add(&self, rhs: impl Into) -> Option { - let rhs = rhs.into(); - Some(Self { - months: self.months.checked_add(rhs.months)?, - seconds: self.seconds.checked_add(rhs.seconds)?, - }) - } - - /// [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations) and [op:subtract-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations) - pub fn checked_sub(&self, rhs: impl Into) -> Option { - let rhs = rhs.into(); - Some(Self { - months: self.months.checked_sub(rhs.months)?, - seconds: self.seconds.checked_sub(rhs.seconds)?, - }) - } -} - impl Neg for Duration { type Output = Self; diff --git a/lib/src/model/xsd/parser.rs b/lib/src/model/xsd/parser.rs index e583f495..fa3fca34 100644 --- a/lib/src/model/xsd/parser.rs +++ b/lib/src/model/xsd/parser.rs @@ -167,32 +167,32 @@ pub fn parse_value<'a, T>( //TODO: check every computation // [6] duYearFrag ::= unsignedNoDecimalPtNumeral 'Y' -fn du_year_frag(input: &str) -> XsdResult { +fn du_year_frag(input: &str) -> XsdResult<'_, i64> { terminated(unsigned_no_decimal_pt_numeral, char('Y'))(input) } // [7] duMonthFrag ::= unsignedNoDecimalPtNumeral 'M' -fn du_month_frag(input: &str) -> XsdResult { +fn du_month_frag(input: &str) -> XsdResult<'_, i64> { terminated(unsigned_no_decimal_pt_numeral, char('M'))(input) } // [8] duDayFrag ::= unsignedNoDecimalPtNumeral 'D' -fn du_day_frag(input: &str) -> XsdResult { +fn du_day_frag(input: &str) -> XsdResult<'_, i64> { terminated(unsigned_no_decimal_pt_numeral, char('D'))(input) } // [9] duHourFrag ::= unsignedNoDecimalPtNumeral 'H' -fn du_hour_frag(input: &str) -> XsdResult { +fn du_hour_frag(input: &str) -> XsdResult<'_, i64> { terminated(unsigned_no_decimal_pt_numeral, char('H'))(input) } // [10] duMinuteFrag ::= unsignedNoDecimalPtNumeral 'M' -fn du_minute_frag(input: &str) -> XsdResult { +fn du_minute_frag(input: &str) -> XsdResult<'_, i64> { terminated(unsigned_no_decimal_pt_numeral, char('M'))(input) } // [11] duSecondFrag ::= (unsignedNoDecimalPtNumeral | unsignedDecimalPtNumeral) 'S' -fn du_second_frag(input: &str) -> XsdResult { +fn du_second_frag(input: &str) -> XsdResult<'_, Decimal> { terminated( map_res( recognize(tuple((digit0, opt(preceded(char('.'), digit0))))), @@ -203,7 +203,7 @@ fn du_second_frag(input: &str) -> XsdResult { } // [12] duYearMonthFrag ::= (duYearFrag duMonthFrag?) | duMonthFrag -fn du_year_month_frag(input: &str) -> XsdResult { +fn du_year_month_frag(input: &str) -> XsdResult<'_, i64> { alt(( map(tuple((du_year_frag, opt(du_month_frag))), |(y, m)| { 12 * y + m.unwrap_or(0) @@ -213,7 +213,7 @@ fn du_year_month_frag(input: &str) -> XsdResult { } // [13] duTimeFrag ::= 'T' ((duHourFrag duMinuteFrag? duSecondFrag?) | (duMinuteFrag duSecondFrag?) | duSecondFrag) -fn du_time_frag(input: &str) -> XsdResult { +fn du_time_frag(input: &str) -> XsdResult<'_, Decimal> { preceded( char('T'), alt(( @@ -236,7 +236,7 @@ fn du_time_frag(input: &str) -> XsdResult { } // [14] duDayTimeFrag ::= (duDayFrag duTimeFrag?) | duTimeFrag -fn du_day_time_frag(input: &str) -> XsdResult { +fn du_day_time_frag(input: &str) -> XsdResult<'_, Decimal> { alt(( map_res(tuple((du_day_frag, opt(du_time_frag))), |(d, t)| { Decimal::from(d) @@ -250,7 +250,7 @@ fn du_day_time_frag(input: &str) -> XsdResult { } // [15] durationLexicalRep ::= '-'? 'P' ((duYearMonthFrag duDayTimeFrag?) | duDayTimeFrag) -pub fn duration_lexical_rep(input: &str) -> XsdResult { +pub fn duration_lexical_rep(input: &str) -> XsdResult<'_, Duration> { map( tuple(( opt(char('-')), @@ -276,7 +276,7 @@ pub fn duration_lexical_rep(input: &str) -> XsdResult { } // [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag? -pub fn date_time_lexical_rep(input: &str) -> XsdResult { +pub fn date_time_lexical_rep(input: &str) -> XsdResult<'_, DateTime> { map_res( tuple(( year_frag, @@ -301,7 +301,7 @@ pub fn date_time_lexical_rep(input: &str) -> XsdResult { } // [17] timeLexicalRep ::= ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag? -pub fn time_lexical_rep(input: &str) -> XsdResult