Makes XSD datatypes a standalone crate

Allows other crates to reuse Oxigraph XSD datatypes implementation
pull/336/head
Tpt 2 years ago committed by Thomas Tanon
parent 027da6d639
commit 854e29ee38
  1. 2
      .github/workflows/artifacts.yml
  2. 3
      .github/workflows/release.yml
  3. 4
      .github/workflows/tests.yml
  4. 12
      Cargo.lock
  5. 1
      Cargo.toml
  6. 5
      lib/Cargo.toml
  7. 1
      lib/oxrdf/Cargo.toml
  8. 130
      lib/oxrdf/src/literal.rs
  9. 23
      lib/oxsdatatypes/Cargo.toml
  10. 36
      lib/oxsdatatypes/README.md
  11. 135
      lib/oxsdatatypes/src/boolean.rs
  12. 178
      lib/oxsdatatypes/src/date_time.rs
  13. 358
      lib/oxsdatatypes/src/decimal.rs
  14. 105
      lib/oxsdatatypes/src/double.rs
  15. 124
      lib/oxsdatatypes/src/duration.rs
  16. 108
      lib/oxsdatatypes/src/float.rs
  17. 302
      lib/oxsdatatypes/src/integer.rs
  18. 25
      lib/oxsdatatypes/src/lib.rs
  19. 8
      lib/oxsdatatypes/src/parser.rs
  20. 13
      lib/src/lib.rs
  21. 108
      lib/src/model.rs
  22. 140
      lib/src/sparql/eval.rs
  23. 61
      lib/src/storage/binary_encoder.rs
  24. 80
      lib/src/storage/numeric_encoder.rs
  25. 13
      lib/src/xsd/mod.rs

@ -66,7 +66,7 @@ jobs:
- run: docker run -v "$(pwd)":/workdir --platform linux/x86_64 quay.io/pypa/manylinux2014_x86_64 /bin/bash /workdir/.github/workflows/manylinux_build_script.sh - run: docker run -v "$(pwd)":/workdir --platform linux/x86_64 quay.io/pypa/manylinux2014_x86_64 /bin/bash /workdir/.github/workflows/manylinux_build_script.sh
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
name: pyoxigraph_x86_64_linux name: pyoxigraph_wheel_x86_64_linux
path: target/wheels/*.whl path: target/wheels/*.whl
wheel_mac: wheel_mac:

@ -51,6 +51,9 @@ jobs:
- run: cargo publish - run: cargo publish
working-directory: ./oxrocksdb-sys working-directory: ./oxrocksdb-sys
continue-on-error: true continue-on-error: true
- run: cargo publish
working-directory: ./lib/oxsdatatypes
continue-on-error: true
- run: cargo publish - run: cargo publish
working-directory: ./lib/oxrdf working-directory: ./lib/oxrdf
continue-on-error: true continue-on-error: true

@ -26,6 +26,8 @@ jobs:
with: with:
submodules: true submodules: true
- run: rustup update && rustup component add clippy - run: rustup update && rustup component add clippy
- run: cargo clippy
working-directory: ./lib/oxsdatatypes
- run: cargo clippy - run: cargo clippy
working-directory: ./lib/oxrdf working-directory: ./lib/oxrdf
- run: cargo clippy - run: cargo clippy
@ -41,6 +43,8 @@ jobs:
with: with:
submodules: true submodules: true
- run: rustup override set 1.60.0 && rustup component add clippy - run: rustup override set 1.60.0 && rustup component add clippy
- run: cargo clippy -- -D warnings -D clippy::all
working-directory: ./lib/oxsdatatypes
- run: cargo clippy -- -D warnings -D clippy::all - run: cargo clippy -- -D warnings -D clippy::all
working-directory: ./lib/oxrdf working-directory: ./lib/oxrdf
- run: cargo clippy -- -D warnings -D clippy::all - run: cargo clippy -- -D warnings -D clippy::all

12
Cargo.lock generated

@ -982,16 +982,15 @@ dependencies = [
"digest", "digest",
"getrandom", "getrandom",
"hex", "hex",
"js-sys",
"lazy_static", "lazy_static",
"libc", "libc",
"md-5", "md-5",
"nom",
"oxhttp", "oxhttp",
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"oxrocksdb-sys", "oxrocksdb-sys",
"oxsdatatypes",
"rand", "rand",
"regex", "regex",
"rio_api", "rio_api",
@ -1069,6 +1068,7 @@ dependencies = [
"lasso", "lasso",
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxsdatatypes",
"rand", "rand",
] ]
@ -1081,6 +1081,14 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "oxsdatatypes"
version = "0.1.0"
dependencies = [
"js-sys",
"nom",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.1" version = "0.12.1"

@ -3,6 +3,7 @@ members = [
"js", "js",
"lib", "lib",
"lib/oxrdf", "lib/oxrdf",
"lib/oxsdatatypes",
"lib/spargebra", "lib/spargebra",
"lib/sparesults", "lib/sparesults",
"lib/sparql-smith", "lib/sparql-smith",

@ -34,11 +34,11 @@ rio_api = "0.8"
rio_turtle = "0.8" rio_turtle = "0.8"
rio_xml = "0.8" rio_xml = "0.8"
hex = "0.4" hex = "0.4"
nom = "7"
siphasher = "0.3" siphasher = "0.3"
lazy_static = "1" lazy_static = "1"
sysinfo = "0.27" sysinfo = "0.27"
oxrdf = { version = "0.1.1", path="oxrdf", features = ["rdf-star"] } oxrdf = { version = "0.1.1", path="oxrdf", features = ["rdf-star", "oxsdatatypes"] }
oxsdatatypes = { version = "0.1.0", path="oxsdatatypes" }
spargebra = { version = "0.2.3", path="spargebra", features = ["rdf-star"] } spargebra = { version = "0.2.3", path="spargebra", features = ["rdf-star"] }
sparesults = { version = "0.1.3", path="sparesults", features = ["rdf-star"] } sparesults = { version = "0.1.3", path="sparesults", features = ["rdf-star"] }
@ -48,7 +48,6 @@ oxrocksdb-sys = { version = "0.3.10", path="../oxrocksdb-sys" }
oxhttp = { version = "0.1", optional = true } oxhttp = { version = "0.1", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"
getrandom = {version="0.2", features=["js"]} getrandom = {version="0.2", features=["js"]}
[dev-dependencies] [dev-dependencies]

@ -21,6 +21,7 @@ rdf-star = []
rand = "0.8" rand = "0.8"
oxilangtag = "0.1" oxilangtag = "0.1"
oxiri = "0.2" oxiri = "0.2"
oxsdatatypes = { version = "0.1.0", path="../oxsdatatypes", optional = true }
lasso = { version = "0.6", features = ["inline-more"] } lasso = { version = "0.6", features = ["inline-more"] }
[package.metadata.docs.rs] [package.metadata.docs.rs]

@ -3,6 +3,8 @@ use crate::vocab::rdf;
use crate::vocab::xsd; use crate::vocab::xsd;
use crate::NamedNodeRef; use crate::NamedNodeRef;
use oxilangtag::{LanguageTag, LanguageTagParseError}; use oxilangtag::{LanguageTag, LanguageTagParseError};
#[cfg(feature = "oxsdatatypes")]
use oxsdatatypes::*;
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
@ -293,6 +295,134 @@ impl From<f64> for Literal {
} }
} }
#[cfg(feature = "oxsdatatypes")]
impl From<Boolean> for Literal {
#[inline]
fn from(value: Boolean) -> Self {
Self::new_typed_literal(value.to_string(), xsd::BOOLEAN)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Float> for Literal {
#[inline]
fn from(value: Float) -> Self {
Self::new_typed_literal(value.to_string(), xsd::FLOAT)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Double> for Literal {
#[inline]
fn from(value: Double) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DOUBLE)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Integer> for Literal {
#[inline]
fn from(value: Integer) -> Self {
Self::new_typed_literal(value.to_string(), xsd::INTEGER)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Decimal> for Literal {
#[inline]
fn from(value: Decimal) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DECIMAL)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<DateTime> for Literal {
#[inline]
fn from(value: DateTime) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DATE_TIME)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Time> for Literal {
#[inline]
fn from(value: Time) -> Self {
Self::new_typed_literal(value.to_string(), xsd::TIME)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Date> for Literal {
#[inline]
fn from(value: Date) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DATE)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<GYearMonth> for Literal {
#[inline]
fn from(value: GYearMonth) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_YEAR_MONTH)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<GYear> for Literal {
#[inline]
fn from(value: GYear) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_YEAR)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<GMonthDay> for Literal {
#[inline]
fn from(value: GMonthDay) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_MONTH_DAY)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<GMonth> for Literal {
#[inline]
fn from(value: GMonth) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_MONTH)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<GDay> for Literal {
#[inline]
fn from(value: GDay) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_DAY)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<Duration> for Literal {
#[inline]
fn from(value: Duration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DURATION)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<YearMonthDuration> for Literal {
#[inline]
fn from(value: YearMonthDuration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::YEAR_MONTH_DURATION)
}
}
#[cfg(feature = "oxsdatatypes")]
impl From<DayTimeDuration> for Literal {
#[inline]
fn from(value: DayTimeDuration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DAY_TIME_DURATION)
}
}
/// A borrowed RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// A borrowed RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
/// ///
/// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation: /// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation:

@ -0,0 +1,23 @@
[package]
name = "oxsdatatypes"
version = "0.1.0"
authors = ["Tpt <thomas@pellissier-tanon.fr>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
keywords = ["XML Schema"]
repository = "https://github.com/oxigraph/oxigraph/tree/main/lib/oxsdatatypes"
homepage = "https://oxigraph.org/"
description = """
An implementation of some XSD datatypes for SPARQL implementations
"""
edition = "2021"
rust-version = "1.60"
[dependencies]
nom = "7"
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"
[package.metadata.docs.rs]
all-features = true

@ -0,0 +1,36 @@
oxsdatatypes
============
[![Latest Version](https://img.shields.io/crates/v/oxsdatatypes.svg)](https://crates.io/crates/oxsdatatypes)
[![Released API docs](https://docs.rs/oxsdatatypes/badge.svg)](https://docs.rs/oxsdatatypes)
[![Crates.io downloads](https://img.shields.io/crates/d/oxsdatatypes)](https://crates.io/crates/oxsdatatypes)
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions)
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
oxsdatatypes is an implementation of some [XML Schema Definition Language Datatypes](https://www.w3.org/TR/xmlschema11-2/).
Its main aim is to ease the implementation of SPARQL or XPath.
Usage example:
```rust
use std::str::FromStr;
use oxsdatatypes::Decimal;
assert!(Decimal::from_str("22.2").unwrap() > Decimal::from_str("21").unwrap());
```
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
`<http://www.apache.org/licenses/LICENSE-2.0>`)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
`<http://opensource.org/licenses/MIT>`)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Oxigraph by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

@ -0,0 +1,135 @@
use crate::{Decimal, Double, Float, Integer};
use std::fmt;
use std::str::{FromStr, ParseBoolError};
/// [XML Schema `boolean` datatype](https://www.w3.org/TR/xmlschema11-2/#boolean)
///
/// Uses internally a [`bool`].
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Boolean {
value: bool,
}
impl Boolean {
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
}
impl From<bool> for Boolean {
#[inline]
fn from(value: bool) -> Self {
Self { value }
}
}
impl From<Integer> for Boolean {
#[inline]
fn from(value: Integer) -> Self {
(value != Integer::from(0)).into()
}
}
impl From<Decimal> for Boolean {
#[inline]
fn from(value: Decimal) -> Self {
(value != Decimal::from(0)).into()
}
}
impl From<Float> for Boolean {
#[inline]
fn from(value: Float) -> Self {
(value != Float::from(0.) && !value.is_naan()).into()
}
}
impl From<Double> for Boolean {
#[inline]
fn from(value: Double) -> Self {
(value != Double::from(0.) && !value.is_naan()).into()
}
}
impl From<Boolean> for bool {
#[inline]
fn from(value: Boolean) -> Self {
value.value
}
}
impl FromStr for Boolean {
type Err = ParseBoolError;
#[inline]
fn from_str(input: &str) -> Result<Self, ParseBoolError> {
Ok(match input {
"true" | "1" => true,
"false" | "0" => false,
_ => bool::from_str(input)?,
}
.into())
}
}
impl fmt::Display for Boolean {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str() -> Result<(), ParseBoolError> {
assert_eq!(Boolean::from(true), Boolean::from_str("true")?);
assert_eq!(Boolean::from(true), Boolean::from_str("1")?);
assert_eq!(Boolean::from(false), Boolean::from_str("false")?);
assert_eq!(Boolean::from(false), Boolean::from_str("0")?);
Ok(())
}
#[test]
fn to_string() {
assert_eq!("true", Boolean::from(true).to_string());
assert_eq!("false", Boolean::from(false).to_string());
}
#[test]
fn from_integer() {
assert_eq!(Boolean::from(false), Integer::from(0).into());
assert_eq!(Boolean::from(true), Integer::from(1).into());
assert_eq!(Boolean::from(true), Integer::from(2).into());
}
#[test]
fn from_decimal() {
assert_eq!(Boolean::from(false), Decimal::from(0).into());
assert_eq!(Boolean::from(true), Decimal::from(1).into());
assert_eq!(Boolean::from(true), Decimal::from(2).into());
}
#[test]
fn from_float() {
assert_eq!(Boolean::from(false), Float::from(0.).into());
assert_eq!(Boolean::from(true), Float::from(1.).into());
assert_eq!(Boolean::from(true), Float::from(2.).into());
assert_eq!(Boolean::from(false), Float::from(f32::NAN).into());
assert_eq!(Boolean::from(true), Float::from(f32::INFINITY).into());
}
#[test]
fn from_double() {
assert_eq!(Boolean::from(false), Double::from(0.).into());
assert_eq!(Boolean::from(true), Double::from(1.).into());
assert_eq!(Boolean::from(true), Double::from(2.).into());
assert_eq!(Boolean::from(false), Double::from(f64::NAN).into());
assert_eq!(Boolean::from(true), Double::from(f64::INFINITY).into());
}
}

@ -1,4 +1,4 @@
use crate::xsd::{Double, Float}; use crate::{Boolean, Double, Float, Integer};
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
@ -10,7 +10,7 @@ const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000;
const DECIMAL_PART_POW_MINUS_ONE: i128 = 100_000_000_000_000_000; const DECIMAL_PART_POW_MINUS_ONE: i128 = 100_000_000_000_000_000;
const DECIMAL_PART_HALF_POW: i128 = 1_000_000_000; const DECIMAL_PART_HALF_POW: i128 = 1_000_000_000;
/// [XML Schema `decimal` datatype](https://www.w3.org/TR/xmlschema11-2/#decimal) implementation. /// [XML Schema `decimal` datatype](https://www.w3.org/TR/xmlschema11-2/#decimal)
/// ///
/// It stores the decimal in a fix point encoding allowing nearly 18 digits before and 18 digits after ".". /// It stores the decimal in a fix point encoding allowing nearly 18 digits before and 18 digits after ".".
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -21,6 +21,7 @@ pub struct Decimal {
impl Decimal { impl Decimal {
/// Constructs the decimal i / 10^n /// Constructs the decimal i / 10^n
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn new(i: i128, n: u32) -> Result<Self, DecimalOverflowError> { pub fn new(i: i128, n: u32) -> Result<Self, DecimalOverflowError> {
let shift = (DECIMAL_PART_DIGITS as u32) let shift = (DECIMAL_PART_DIGITS as u32)
.checked_sub(n) .checked_sub(n)
@ -155,39 +156,10 @@ impl Decimal {
self.value > 0 self.value > 0
} }
/// Creates a `Decimal` from a `Float` without taking care of precision /// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub(crate) fn from_float(v: Float) -> Self {
Self::from_double(v.into())
}
/// Creates a `Float` from a `Decimal` without taking care of precision
#[inline]
#[allow(clippy::cast_possible_truncation)]
pub fn to_float(self) -> Float {
(f64::from(self.to_double()) as f32).into()
}
/// Creates a `Decimal` from a `Double` without taking care of precision
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
pub(crate) fn from_double(v: Double) -> Self {
Self {
value: (f64::from(v) * (DECIMAL_PART_POW as f64)) as i128,
}
}
/// Creates a `Double` from a `Decimal` without taking care of precision
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_precision_loss)]
pub fn to_double(self) -> Double {
((self.value as f64) / (DECIMAL_PART_POW as f64)).into()
}
/// Creates a `bool` from a `Decimal` according to xsd:boolean cast constraints
#[inline] #[inline]
pub fn to_bool(self) -> bool { pub fn is_identical_with(&self, other: &Self) -> bool {
self.value != 0 self == other
} }
#[inline] #[inline]
@ -195,15 +167,9 @@ impl Decimal {
self.value / DECIMAL_PART_POW self.value / DECIMAL_PART_POW
} }
#[cfg(test)] pub const MIN: Self = Self { value: i128::MIN };
pub(super) const fn min_value() -> Self {
Self { value: i128::MIN }
}
#[cfg(test)] pub const MAX: Self = Self { value: i128::MAX };
pub(super) const fn max_value() -> Self {
Self { value: i128::MAX }
}
#[cfg(test)] #[cfg(test)]
pub(super) const fn step() -> Self { pub(super) const fn step() -> Self {
@ -292,6 +258,13 @@ impl From<u64> for Decimal {
} }
} }
impl From<Integer> for Decimal {
#[inline]
fn from(value: Integer) -> Self {
i64::from(value).into()
}
}
impl TryFrom<i128> for Decimal { impl TryFrom<i128> for Decimal {
type Error = DecimalOverflowError; type Error = DecimalOverflowError;
@ -319,11 +292,76 @@ impl TryFrom<u128> for Decimal {
} }
} }
impl From<Boolean> for Decimal {
#[inline]
fn from(value: Boolean) -> Self {
bool::from(value).into()
}
}
impl TryFrom<Float> for Decimal {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Float) -> Result<Self, DecimalOverflowError> {
Double::from(value).try_into()
}
}
impl TryFrom<Double> for Decimal {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Double) -> Result<Self, DecimalOverflowError> {
let shifted = value * Double::from(DECIMAL_PART_POW as f64);
if shifted.is_finite()
&& Double::from(i128::MIN as f64) <= shifted
&& shifted <= Double::from(i128::MAX as f64)
{
Ok(Self {
value: f64::from(shifted) as i128,
})
} else {
Err(DecimalOverflowError)
}
}
}
impl From<Decimal> for Float {
#[inline]
fn from(value: Decimal) -> Self {
((value.value as f32) / (DECIMAL_PART_POW as f32)).into()
}
}
impl From<Decimal> for Double {
#[inline]
fn from(value: Decimal) -> Self {
((value.value as f64) / (DECIMAL_PART_POW as f64)).into()
}
}
impl TryFrom<Decimal> for Integer {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Decimal) -> Result<Self, DecimalOverflowError> {
Ok(i64::try_from(
value
.value
.checked_div(DECIMAL_PART_POW)
.ok_or(DecimalOverflowError)?,
)
.map_err(|_| DecimalOverflowError)?
.into())
}
}
impl FromStr for Decimal { impl FromStr for Decimal {
type Err = ParseDecimalError; type Err = DecimalParseError;
/// Parses decimals lexical mapping /// Parses decimals lexical mapping
fn from_str(input: &str) -> Result<Self, ParseDecimalError> { fn from_str(input: &str) -> Result<Self, DecimalParseError> {
// (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+) // (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)
let input = input.as_bytes(); let input = input.as_bytes();
if input.is_empty() { if input.is_empty() {
@ -389,64 +427,6 @@ impl FromStr for Decimal {
} }
} }
#[derive(Debug, Clone)]
pub struct ParseDecimalError {
kind: ParseDecimalErrorKind,
}
#[derive(Debug, Clone)]
enum ParseDecimalErrorKind {
Overflow,
Underflow,
UnexpectedChar,
UnexpectedEnd,
}
const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError {
kind: ParseDecimalErrorKind::Overflow,
};
const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError {
kind: ParseDecimalErrorKind::Underflow,
};
const PARSE_UNEXPECTED_CHAR: ParseDecimalError = ParseDecimalError {
kind: ParseDecimalErrorKind::UnexpectedChar,
};
const PARSE_UNEXPECTED_END: ParseDecimalError = ParseDecimalError {
kind: ParseDecimalErrorKind::UnexpectedEnd,
};
impl fmt::Display for ParseDecimalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ParseDecimalErrorKind::Overflow => write!(f, "Value overflow"),
ParseDecimalErrorKind::Underflow => write!(f, "Value underflow"),
ParseDecimalErrorKind::UnexpectedChar => write!(f, "Unexpected character"),
ParseDecimalErrorKind::UnexpectedEnd => write!(f, "Unexpected end of string"),
}
}
}
impl Error for ParseDecimalError {}
impl From<DecimalOverflowError> for ParseDecimalError {
fn from(_: DecimalOverflowError) -> Self {
Self {
kind: ParseDecimalErrorKind::Overflow,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DecimalOverflowError;
impl fmt::Display for DecimalOverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Value overflow")
}
}
impl Error for DecimalOverflowError {}
impl fmt::Display for Decimal { impl fmt::Display for Decimal {
/// Formats the decimal following its canonical representation. /// Formats the decimal following its canonical representation.
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
@ -533,19 +513,66 @@ impl Neg for Decimal {
} }
} }
impl TryFrom<Decimal> for i64 { /// An error when parsing a [`Decimal`].
type Error = DecimalOverflowError; #[derive(Debug, Clone)]
pub struct DecimalParseError {
kind: DecimalParseErrorKind,
}
fn try_from(value: Decimal) -> Result<Self, DecimalOverflowError> { #[derive(Debug, Clone)]
value enum DecimalParseErrorKind {
.value Overflow,
.checked_div(DECIMAL_PART_POW) Underflow,
.ok_or(DecimalOverflowError)? UnexpectedChar,
.try_into() UnexpectedEnd,
.map_err(|_| DecimalOverflowError) }
const PARSE_OVERFLOW: DecimalParseError = DecimalParseError {
kind: DecimalParseErrorKind::Overflow,
};
const PARSE_UNDERFLOW: DecimalParseError = DecimalParseError {
kind: DecimalParseErrorKind::Underflow,
};
const PARSE_UNEXPECTED_CHAR: DecimalParseError = DecimalParseError {
kind: DecimalParseErrorKind::UnexpectedChar,
};
const PARSE_UNEXPECTED_END: DecimalParseError = DecimalParseError {
kind: DecimalParseErrorKind::UnexpectedEnd,
};
impl fmt::Display for DecimalParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
DecimalParseErrorKind::Overflow => write!(f, "Value overflow"),
DecimalParseErrorKind::Underflow => write!(f, "Value underflow"),
DecimalParseErrorKind::UnexpectedChar => write!(f, "Unexpected character"),
DecimalParseErrorKind::UnexpectedEnd => write!(f, "Unexpected end of string"),
}
}
}
impl Error for DecimalParseError {}
impl From<DecimalOverflowError> for DecimalParseError {
fn from(_: DecimalOverflowError) -> Self {
Self {
kind: DecimalParseErrorKind::Overflow,
}
} }
} }
/// An overflow in [`Decimal`] computations.
#[derive(Debug, Clone, Copy)]
pub struct DecimalOverflowError;
impl fmt::Display for DecimalOverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Value overflow")
}
}
impl Error for DecimalOverflowError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -574,19 +601,22 @@ mod tests {
); );
assert_eq!(Decimal::from_str("0.1220").unwrap().to_string(), "0.122"); assert_eq!(Decimal::from_str("0.1220").unwrap().to_string(), "0.122");
assert_eq!(Decimal::from_str(".12200").unwrap().to_string(), "0.122"); assert_eq!(Decimal::from_str(".12200").unwrap().to_string(), "0.122");
assert_eq!(Decimal::from_str("1.").unwrap().to_string(), "1");
assert_eq!(Decimal::from_str("01.0").unwrap().to_string(), "1");
assert_eq!(Decimal::from_str("0").unwrap().to_string(), "0");
assert_eq!( assert_eq!(
Decimal::from_str(&Decimal::max_value().to_string()).unwrap(), Decimal::from_str(&Decimal::MAX.to_string()).unwrap(),
Decimal::max_value() Decimal::MAX
); );
assert_eq!( assert_eq!(
Decimal::from_str( Decimal::from_str(
&Decimal::min_value() &Decimal::MIN
.checked_add(Decimal::step()) .checked_add(Decimal::step())
.unwrap() .unwrap()
.to_string() .to_string()
) )
.unwrap(), .unwrap(),
Decimal::min_value().checked_add(Decimal::step()).unwrap() Decimal::MIN.checked_add(Decimal::step()).unwrap()
); );
} }
@ -609,18 +639,18 @@ mod tests {
#[test] #[test]
fn add() { fn add() {
assert!(Decimal::min_value().checked_add(Decimal::step()).is_some()); assert!(Decimal::MIN.checked_add(Decimal::step()).is_some());
assert!(Decimal::max_value().checked_add(Decimal::step()).is_none()); assert!(Decimal::MAX.checked_add(Decimal::step()).is_none());
assert_eq!( assert_eq!(
Decimal::max_value().checked_add(Decimal::min_value()), Decimal::MAX.checked_add(Decimal::MIN),
Some(-Decimal::step()) Some(-Decimal::step())
); );
} }
#[test] #[test]
fn sub() { fn sub() {
assert!(Decimal::min_value().checked_sub(Decimal::step()).is_none()); assert!(Decimal::MIN.checked_sub(Decimal::step()).is_none());
assert!(Decimal::max_value().checked_sub(Decimal::step()).is_some()); assert!(Decimal::MAX.checked_sub(Decimal::step()).is_some());
} }
#[test] #[test]
@ -715,4 +745,92 @@ mod tests {
assert_eq!(Decimal::from(i64::MIN).floor(), Decimal::from(i64::MIN)); assert_eq!(Decimal::from(i64::MIN).floor(), Decimal::from(i64::MIN));
assert_eq!(Decimal::from(i64::MAX).floor(), Decimal::from(i64::MAX)); assert_eq!(Decimal::from(i64::MAX).floor(), Decimal::from(i64::MAX));
} }
#[test]
fn to_be_bytes() {
assert_eq!(
Decimal::from_be_bytes(Decimal::from(i64::MIN).to_be_bytes()),
Decimal::from(i64::MIN)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(i64::MAX).to_be_bytes()),
Decimal::from(i64::MAX)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
Decimal::from(0)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
Decimal::from(0)
);
assert_eq!(
Decimal::from_be_bytes(Decimal::from_str("0.01").unwrap().to_be_bytes()),
Decimal::from_str("0.01").unwrap()
);
}
#[test]
fn from_bool() {
assert_eq!(Decimal::from(false), Decimal::from(0u8));
assert_eq!(Decimal::from(true), Decimal::from(1u8));
}
#[test]
fn from_float() {
assert_eq!(
Decimal::try_from(Float::from(0.)).unwrap(),
Decimal::from_str("0").unwrap()
);
assert_eq!(
Decimal::try_from(Float::from(-0.)).unwrap(),
Decimal::from_str("0.").unwrap()
);
assert_eq!(
Decimal::try_from(Float::from(-123.5)).unwrap(),
Decimal::from_str("-123.5").unwrap()
);
assert!(Decimal::try_from(Float::from(f32::NAN)).is_err());
assert!(Decimal::try_from(Float::from(f32::INFINITY)).is_err());
assert!(Decimal::try_from(Float::from(f32::NEG_INFINITY)).is_err());
assert!(Decimal::try_from(Float::from(f32::MIN)).is_err());
assert!(Decimal::try_from(Float::from(f32::MAX)).is_err());
assert!(
Decimal::try_from(Float::from(1672507302466.))
.unwrap()
.checked_sub(Decimal::from_str("1672507302466").unwrap())
.unwrap()
.abs()
< Decimal::from(1_000_000)
);
}
#[test]
fn from_double() {
assert_eq!(
Decimal::try_from(Double::from(0.)).unwrap(),
Decimal::from_str("0").unwrap()
);
assert_eq!(
Decimal::try_from(Double::from(-0.)).unwrap(),
Decimal::from_str("0").unwrap()
);
assert_eq!(
Decimal::try_from(Double::from(-123.1)).unwrap(),
Decimal::from_str("-123.1").unwrap()
);
assert!(
Decimal::try_from(Double::from(1672507302466.))
.unwrap()
.checked_sub(Decimal::from_str("1672507302466").unwrap())
.unwrap()
.abs()
< Decimal::from(1)
);
assert!(Decimal::try_from(Double::from(f64::NAN)).is_err());
assert!(Decimal::try_from(Double::from(f64::INFINITY)).is_err());
assert!(Decimal::try_from(Double::from(f64::NEG_INFINITY)).is_err());
assert!(Decimal::try_from(Double::from(f64::MIN)).is_err());
assert!(Decimal::try_from(Double::from(f64::MAX)).is_err());
}
} }

@ -1,15 +1,14 @@
use crate::xsd::Float; use crate::{Boolean, Float, Integer};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher};
use std::num::ParseFloatError; use std::num::ParseFloatError;
use std::ops::{Add, Div, Mul, Neg, Sub}; use std::ops::{Add, Div, Mul, Neg, Sub};
use std::str::FromStr; use std::str::FromStr;
/// [XML Schema `double` datatype](https://www.w3.org/TR/xmlschema11-2/#double) implementation. /// [XML Schema `double` datatype](https://www.w3.org/TR/xmlschema11-2/#double)
/// ///
/// The "==" implementation is identity, not equality /// Uses internally a [`f64`].
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default, PartialEq)]
#[repr(transparent)] #[repr(transparent)]
pub struct Double { pub struct Double {
value: f64, value: f64,
@ -52,33 +51,20 @@ impl Double {
self.value.round().into() self.value.round().into()
} }
/// Casts i64 into `Double`
#[inline] #[inline]
#[allow(clippy::cast_precision_loss)] pub fn is_naan(self) -> bool {
pub fn from_i64(value: i64) -> Self { self.value.is_nan()
Self {
value: value as f64,
}
}
/// Casts `Double` into i64 without taking care of loss
#[inline]
#[allow(clippy::cast_possible_truncation)]
pub fn to_i64(self) -> i64 {
self.value as i64
} }
/// Casts `Double` into f32 without taking care of loss
#[inline] #[inline]
#[allow(clippy::cast_possible_truncation)] pub fn is_finite(self) -> bool {
pub fn to_f32(self) -> f32 { self.value.is_finite()
self.value as f32
} }
/// Creates a `bool` from a `Decimal` according to xsd:boolean cast constraints /// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline] #[inline]
pub fn to_bool(self) -> bool { pub fn is_identical_with(&self, other: &Self) -> bool {
self.value != 0. && !self.value.is_nan() self.value.to_ne_bytes() == other.value.to_ne_bytes()
} }
} }
@ -159,10 +145,23 @@ impl From<Float> for Double {
} }
} }
impl From<Boolean> for Double {
#[inline]
fn from(value: Boolean) -> Self {
if bool::from(value) { 1. } else { 0. }.into()
}
}
impl From<Integer> for Double {
#[inline]
fn from(value: Integer) -> Self {
(i64::from(value) as f64).into()
}
}
impl FromStr for Double { impl FromStr for Double {
type Err = ParseFloatError; type Err = ParseFloatError;
/// Parses decimals lexical mapping
#[inline] #[inline]
fn from_str(input: &str) -> Result<Self, ParseFloatError> { fn from_str(input: &str) -> Result<Self, ParseFloatError> {
Ok(f64::from_str(input)?.into()) Ok(f64::from_str(input)?.into())
@ -182,15 +181,6 @@ impl fmt::Display for Double {
} }
} }
impl PartialEq for Double {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.value.to_ne_bytes() == other.value.to_ne_bytes()
}
}
impl Eq for Double {}
impl PartialOrd for Double { impl PartialOrd for Double {
#[inline] #[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@ -198,13 +188,6 @@ impl PartialOrd for Double {
} }
} }
impl Hash for Double {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.value.to_ne_bytes())
}
}
impl Neg for Double { impl Neg for Double {
type Output = Self; type Output = Self;
@ -257,13 +240,45 @@ mod tests {
#[test] #[test]
fn eq() { fn eq() {
assert_eq!(Double::from(0_f64), Double::from(0_f64)); assert_eq!(Double::from(0_f64), Double::from(0_f64));
assert_eq!(Double::from(f64::NAN), Double::from(f64::NAN)); assert_ne!(Double::from(f64::NAN), Double::from(f64::NAN));
assert_ne!(Double::from(-0.), Double::from(0.)); assert_eq!(Double::from(-0.), Double::from(0.));
}
#[test]
fn cmp() {
assert_eq!(
Double::from(0.).partial_cmp(&Double::from(0.)),
Some(Ordering::Equal)
);
assert_eq!(
Double::from(f64::INFINITY).partial_cmp(&Double::from(f64::MAX)),
Some(Ordering::Greater)
);
assert_eq!(
Double::from(f64::NEG_INFINITY).partial_cmp(&Double::from(f64::MIN)),
Some(Ordering::Less)
);
assert_eq!(Double::from(f64::NAN).partial_cmp(&Double::from(0.)), None);
assert_eq!(
Double::from(f64::NAN).partial_cmp(&Double::from(f64::NAN)),
None
);
assert_eq!(
Double::from(0.).partial_cmp(&Double::from(-0.)),
Some(Ordering::Equal)
);
}
#[test]
fn is_identical_with() {
assert!(Double::from(0.).is_identical_with(&Double::from(0.)));
assert!(Double::from(f64::NAN).is_identical_with(&Double::from(f64::NAN)));
assert!(!Double::from(-0.).is_identical_with(&Double::from(0.)));
} }
#[test] #[test]
fn from_str() -> Result<(), ParseFloatError> { fn from_str() -> Result<(), ParseFloatError> {
assert_eq!(Double::from(f64::NAN), Double::from_str("NaN")?); assert!(Double::from(f64::NAN).is_identical_with(&Double::from_str("NaN")?));
assert_eq!(Double::from(f64::INFINITY), Double::from_str("INF")?); assert_eq!(Double::from(f64::INFINITY), Double::from_str("INF")?);
assert_eq!(Double::from(f64::INFINITY), Double::from_str("+INF")?); assert_eq!(Double::from(f64::INFINITY), Double::from_str("+INF")?);
assert_eq!(Double::from(f64::NEG_INFINITY), Double::from_str("-INF")?); assert_eq!(Double::from(f64::NEG_INFINITY), Double::from_str("-INF")?);

@ -7,7 +7,7 @@ use std::ops::Neg;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration as StdDuration; use std::time::Duration as StdDuration;
/// [XML Schema `duration` datatype](https://www.w3.org/TR/xmlschema11-2/#duration) implementation. /// [XML Schema `duration` datatype](https://www.w3.org/TR/xmlschema11-2/#duration)
/// ///
/// It stores the duration using a pair of a `YearMonthDuration` and a `DayTimeDuration`. /// It stores the duration using a pair of a `YearMonthDuration` and a `DayTimeDuration`.
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Default)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Default)]
@ -17,6 +17,7 @@ pub struct Duration {
} }
impl Duration { impl Duration {
#[inline]
pub fn new(months: impl Into<i64>, seconds: impl Into<Decimal>) -> Self { pub fn new(months: impl Into<i64>, seconds: impl Into<Decimal>) -> Self {
Self { Self {
year_month: YearMonthDuration::new(months), year_month: YearMonthDuration::new(months),
@ -24,6 +25,7 @@ impl Duration {
} }
} }
#[inline]
pub fn from_be_bytes(bytes: [u8; 24]) -> Self { pub fn from_be_bytes(bytes: [u8; 24]) -> Self {
Self { Self {
year_month: YearMonthDuration::from_be_bytes(bytes[0..8].try_into().unwrap()), year_month: YearMonthDuration::from_be_bytes(bytes[0..8].try_into().unwrap()),
@ -32,43 +34,52 @@ impl Duration {
} }
/// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration) /// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration)
#[inline]
pub fn years(&self) -> i64 { pub fn years(&self) -> i64 {
self.year_month.years() self.year_month.years()
} }
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration) /// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
#[inline]
pub fn months(&self) -> i64 { pub fn months(&self) -> i64 {
self.year_month.months() self.year_month.months()
} }
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration) /// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration)
#[inline]
pub fn days(&self) -> i64 { pub fn days(&self) -> i64 {
self.day_time.days() self.day_time.days()
} }
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration) /// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
#[inline]
pub fn hours(&self) -> i64 { pub fn hours(&self) -> i64 {
self.day_time.hours() self.day_time.hours()
} }
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration) /// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
#[inline]
pub fn minutes(&self) -> i64 { pub fn minutes(&self) -> i64 {
self.day_time.minutes() self.day_time.minutes()
} }
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration) /// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
#[inline]
pub fn seconds(&self) -> Decimal { pub fn seconds(&self) -> Decimal {
self.day_time.seconds() self.day_time.seconds()
} }
#[inline]
pub(super) const fn all_months(&self) -> i64 { pub(super) const fn all_months(&self) -> i64 {
self.year_month.all_months() self.year_month.all_months()
} }
#[inline]
pub(super) const fn all_seconds(&self) -> Decimal { pub(super) const fn all_seconds(&self) -> Decimal {
self.day_time.all_seconds() self.day_time.all_seconds()
} }
#[inline]
pub fn to_be_bytes(self) -> [u8; 24] { pub fn to_be_bytes(self) -> [u8; 24] {
let mut bytes = [0; 24]; let mut bytes = [0; 24];
bytes[0..8].copy_from_slice(&self.year_month.to_be_bytes()); bytes[0..8].copy_from_slice(&self.year_month.to_be_bytes());
@ -77,6 +88,7 @@ impl Duration {
} }
/// [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) /// [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)
#[inline]
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
@ -86,6 +98,7 @@ impl Duration {
} }
/// [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) /// [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)
#[inline]
pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
@ -93,11 +106,18 @@ impl Duration {
day_time: self.day_time.checked_sub(rhs.day_time)?, day_time: self.day_time.checked_sub(rhs.day_time)?,
}) })
} }
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
} }
impl TryFrom<StdDuration> for Duration { impl TryFrom<StdDuration> for Duration {
type Error = DecimalOverflowError; type Error = DecimalOverflowError;
#[inline]
fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> { fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> {
Ok(DayTimeDuration::try_from(value)?.into()) Ok(DayTimeDuration::try_from(value)?.into())
} }
@ -113,6 +133,7 @@ impl FromStr for Duration {
impl fmt::Display for Duration { impl fmt::Display for Duration {
#[allow(clippy::many_single_char_names)] #[allow(clippy::many_single_char_names)]
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ym = self.year_month.months; let mut ym = self.year_month.months;
let mut ss = self.day_time.seconds; let mut ss = self.day_time.seconds;
@ -174,6 +195,7 @@ impl fmt::Display for Duration {
} }
impl PartialOrd for Duration { impl PartialOrd for Duration {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let first = DateTime::new(1969, 9, 1, 0, 0, 0.into(), None).ok()?; let first = DateTime::new(1969, 9, 1, 0, 0, 0.into(), None).ok()?;
let first_result = first let first_result = first
@ -205,6 +227,7 @@ impl PartialOrd for Duration {
impl Neg for Duration { impl Neg for Duration {
type Output = Self; type Output = Self;
#[inline]
fn neg(self) -> Self { fn neg(self) -> Self {
Self { Self {
year_month: self.year_month.neg(), year_month: self.year_month.neg(),
@ -213,7 +236,7 @@ impl Neg for Duration {
} }
} }
/// [XML Schema `yearMonthDuration` datatype](https://www.w3.org/TR/xmlschema11-2/#yearMonthDuration) implementation. /// [XML Schema `yearMonthDuration` datatype](https://www.w3.org/TR/xmlschema11-2/#yearMonthDuration)
/// ///
/// It stores the duration as a number of months encoded using a `i64` /// It stores the duration as a number of months encoded using a `i64`
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -222,12 +245,14 @@ pub struct YearMonthDuration {
} }
impl YearMonthDuration { impl YearMonthDuration {
#[inline]
pub fn new(months: impl Into<i64>) -> Self { pub fn new(months: impl Into<i64>) -> Self {
Self { Self {
months: months.into(), months: months.into(),
} }
} }
#[inline]
pub fn from_be_bytes(bytes: [u8; 8]) -> Self { pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
Self { Self {
months: i64::from_be_bytes(bytes), months: i64::from_be_bytes(bytes),
@ -235,48 +260,29 @@ impl YearMonthDuration {
} }
/// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration) /// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration)
#[inline]
pub fn years(self) -> i64 { pub fn years(self) -> i64 {
self.months / 12 self.months / 12
} }
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration) /// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
#[inline]
pub fn months(self) -> i64 { pub fn months(self) -> i64 {
self.months % 12 self.months % 12
} }
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration) #[inline]
#[allow(clippy::unused_self)]
pub fn days(self) -> i64 {
0
}
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
#[allow(clippy::unused_self)]
pub fn hours(self) -> i64 {
0
}
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
#[allow(clippy::unused_self)]
pub fn minutes(self) -> i64 {
0
}
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
#[allow(clippy::unused_self)]
pub fn seconds(self) -> Decimal {
Decimal::default()
}
pub(super) const fn all_months(self) -> i64 { pub(super) const fn all_months(self) -> i64 {
self.months self.months
} }
#[inline]
pub fn to_be_bytes(self) -> [u8; 8] { pub fn to_be_bytes(self) -> [u8; 8] {
self.months.to_be_bytes() self.months.to_be_bytes()
} }
/// [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations) /// [op:add-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDurations)
#[inline]
pub fn checked_add(self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_add(self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
@ -285,15 +291,23 @@ impl YearMonthDuration {
} }
/// [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations) /// [op:subtract-yearMonthDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDurations)
#[inline]
pub fn checked_sub(self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_sub(self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
months: self.months.checked_sub(rhs.months)?, months: self.months.checked_sub(rhs.months)?,
}) })
} }
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
} }
impl From<YearMonthDuration> for Duration { impl From<YearMonthDuration> for Duration {
#[inline]
fn from(value: YearMonthDuration) -> Self { fn from(value: YearMonthDuration) -> Self {
Self { Self {
year_month: value, year_month: value,
@ -305,6 +319,7 @@ impl From<YearMonthDuration> for Duration {
impl TryFrom<Duration> for YearMonthDuration { impl TryFrom<Duration> for YearMonthDuration {
type Error = DecimalOverflowError; type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> { fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> {
if value.day_time == DayTimeDuration::default() { if value.day_time == DayTimeDuration::default() {
Ok(value.year_month) Ok(value.year_month)
@ -323,6 +338,7 @@ impl FromStr for YearMonthDuration {
} }
impl fmt::Display for YearMonthDuration { impl fmt::Display for YearMonthDuration {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.months == 0 { if self.months == 0 {
write!(f, "P0M") write!(f, "P0M")
@ -333,24 +349,28 @@ impl fmt::Display for YearMonthDuration {
} }
impl PartialEq<Duration> for YearMonthDuration { impl PartialEq<Duration> for YearMonthDuration {
#[inline]
fn eq(&self, other: &Duration) -> bool { fn eq(&self, other: &Duration) -> bool {
Duration::from(*self).eq(other) Duration::from(*self).eq(other)
} }
} }
impl PartialEq<YearMonthDuration> for Duration { impl PartialEq<YearMonthDuration> for Duration {
#[inline]
fn eq(&self, other: &YearMonthDuration) -> bool { fn eq(&self, other: &YearMonthDuration) -> bool {
self.eq(&Self::from(*other)) self.eq(&Self::from(*other))
} }
} }
impl PartialOrd<Duration> for YearMonthDuration { impl PartialOrd<Duration> for YearMonthDuration {
#[inline]
fn partial_cmp(&self, other: &Duration) -> Option<Ordering> { fn partial_cmp(&self, other: &Duration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(other) Duration::from(*self).partial_cmp(other)
} }
} }
impl PartialOrd<YearMonthDuration> for Duration { impl PartialOrd<YearMonthDuration> for Duration {
#[inline]
fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> { fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other)) self.partial_cmp(&Self::from(*other))
} }
@ -359,6 +379,7 @@ impl PartialOrd<YearMonthDuration> for Duration {
impl Neg for YearMonthDuration { impl Neg for YearMonthDuration {
type Output = Self; type Output = Self;
#[inline]
fn neg(self) -> Self { fn neg(self) -> Self {
Self { Self {
months: self.months.neg(), months: self.months.neg(),
@ -366,7 +387,7 @@ impl Neg for YearMonthDuration {
} }
} }
/// [XML Schema `dayTimeDuration` datatype](https://www.w3.org/TR/xmlschema11-2/#dayTimeDuration) implementation. /// [XML Schema `dayTimeDuration` datatype](https://www.w3.org/TR/xmlschema11-2/#dayTimeDuration)
/// ///
/// It stores the duration as a number of seconds encoded using a `Decimal` /// It stores the duration as a number of seconds encoded using a `Decimal`
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)] #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -375,62 +396,59 @@ pub struct DayTimeDuration {
} }
impl DayTimeDuration { impl DayTimeDuration {
#[inline]
pub fn new(seconds: impl Into<Decimal>) -> Self { pub fn new(seconds: impl Into<Decimal>) -> Self {
Self { Self {
seconds: seconds.into(), seconds: seconds.into(),
} }
} }
#[inline]
pub fn from_be_bytes(bytes: [u8; 16]) -> Self { pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
Self { Self {
seconds: Decimal::from_be_bytes(bytes), seconds: Decimal::from_be_bytes(bytes),
} }
} }
/// [fn:years-from-duration](https://www.w3.org/TR/xpath-functions/#func-years-from-duration)
#[allow(clippy::unused_self)]
pub fn years(&self) -> i64 {
0
}
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
#[allow(clippy::unused_self)]
pub fn months(&self) -> i64 {
0
}
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration) /// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration)
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn days(&self) -> i64 { pub fn days(&self) -> i64 {
(self.seconds.as_i128() / 86400) as i64 (self.seconds.as_i128() / 86400) as i64
} }
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration) /// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn hours(&self) -> i64 { pub fn hours(&self) -> i64 {
((self.seconds.as_i128() % 86400) / 3600) as i64 ((self.seconds.as_i128() % 86400) / 3600) as i64
} }
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration) /// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn minutes(&self) -> i64 { pub fn minutes(&self) -> i64 {
((self.seconds.as_i128() % 3600) / 60) as i64 ((self.seconds.as_i128() % 3600) / 60) as i64
} }
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration) /// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
#[inline]
pub fn seconds(&self) -> Decimal { pub fn seconds(&self) -> Decimal {
self.seconds.checked_rem(60).unwrap() self.seconds.checked_rem(60).unwrap()
} }
#[inline]
pub(super) const fn all_seconds(&self) -> Decimal { pub(super) const fn all_seconds(&self) -> Decimal {
self.seconds self.seconds
} }
#[inline]
pub fn to_be_bytes(self) -> [u8; 16] { pub fn to_be_bytes(self) -> [u8; 16] {
self.seconds.to_be_bytes() self.seconds.to_be_bytes()
} }
/// [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations) /// [op:add-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDurations)
#[inline]
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
@ -439,15 +457,23 @@ impl DayTimeDuration {
} }
/// [op:subtract-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations) /// [op:subtract-dayTimeDurations](https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDurations)
#[inline]
pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> { pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into(); let rhs = rhs.into();
Some(Self { Some(Self {
seconds: self.seconds.checked_sub(rhs.seconds)?, seconds: self.seconds.checked_sub(rhs.seconds)?,
}) })
} }
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
} }
impl From<DayTimeDuration> for Duration { impl From<DayTimeDuration> for Duration {
#[inline]
fn from(value: DayTimeDuration) -> Self { fn from(value: DayTimeDuration) -> Self {
Self { Self {
year_month: YearMonthDuration::default(), year_month: YearMonthDuration::default(),
@ -459,6 +485,7 @@ impl From<DayTimeDuration> for Duration {
impl TryFrom<Duration> for DayTimeDuration { impl TryFrom<Duration> for DayTimeDuration {
type Error = DecimalOverflowError; type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> { fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> {
if value.year_month == YearMonthDuration::default() { if value.year_month == YearMonthDuration::default() {
Ok(value.day_time) Ok(value.day_time)
@ -471,6 +498,7 @@ impl TryFrom<Duration> for DayTimeDuration {
impl TryFrom<StdDuration> for DayTimeDuration { impl TryFrom<StdDuration> for DayTimeDuration {
type Error = DecimalOverflowError; type Error = DecimalOverflowError;
#[inline]
fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> { fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> {
Ok(Self { Ok(Self {
seconds: Decimal::new( seconds: Decimal::new(
@ -490,54 +518,63 @@ impl FromStr for DayTimeDuration {
} }
impl fmt::Display for DayTimeDuration { impl fmt::Display for DayTimeDuration {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Duration::from(*self).fmt(f) Duration::from(*self).fmt(f)
} }
} }
impl PartialEq<Duration> for DayTimeDuration { impl PartialEq<Duration> for DayTimeDuration {
#[inline]
fn eq(&self, other: &Duration) -> bool { fn eq(&self, other: &Duration) -> bool {
Duration::from(*self).eq(other) Duration::from(*self).eq(other)
} }
} }
impl PartialEq<DayTimeDuration> for Duration { impl PartialEq<DayTimeDuration> for Duration {
#[inline]
fn eq(&self, other: &DayTimeDuration) -> bool { fn eq(&self, other: &DayTimeDuration) -> bool {
self.eq(&Self::from(*other)) self.eq(&Self::from(*other))
} }
} }
impl PartialEq<YearMonthDuration> for DayTimeDuration { impl PartialEq<YearMonthDuration> for DayTimeDuration {
#[inline]
fn eq(&self, other: &YearMonthDuration) -> bool { fn eq(&self, other: &YearMonthDuration) -> bool {
Duration::from(*self).eq(&Duration::from(*other)) Duration::from(*self).eq(&Duration::from(*other))
} }
} }
impl PartialEq<DayTimeDuration> for YearMonthDuration { impl PartialEq<DayTimeDuration> for YearMonthDuration {
#[inline]
fn eq(&self, other: &DayTimeDuration) -> bool { fn eq(&self, other: &DayTimeDuration) -> bool {
Duration::from(*self).eq(&Duration::from(*other)) Duration::from(*self).eq(&Duration::from(*other))
} }
} }
impl PartialOrd<Duration> for DayTimeDuration { impl PartialOrd<Duration> for DayTimeDuration {
#[inline]
fn partial_cmp(&self, other: &Duration) -> Option<Ordering> { fn partial_cmp(&self, other: &Duration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(other) Duration::from(*self).partial_cmp(other)
} }
} }
impl PartialOrd<DayTimeDuration> for Duration { impl PartialOrd<DayTimeDuration> for Duration {
#[inline]
fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> { fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other)) self.partial_cmp(&Self::from(*other))
} }
} }
impl PartialOrd<YearMonthDuration> for DayTimeDuration { impl PartialOrd<YearMonthDuration> for DayTimeDuration {
#[inline]
fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> { fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(&Duration::from(*other)) Duration::from(*self).partial_cmp(&Duration::from(*other))
} }
} }
impl PartialOrd<DayTimeDuration> for YearMonthDuration { impl PartialOrd<DayTimeDuration> for YearMonthDuration {
#[inline]
fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> { fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(&Duration::from(*other)) Duration::from(*self).partial_cmp(&Duration::from(*other))
} }
@ -546,6 +583,7 @@ impl PartialOrd<DayTimeDuration> for YearMonthDuration {
impl Neg for DayTimeDuration { impl Neg for DayTimeDuration {
type Output = Self; type Output = Self;
#[inline]
fn neg(self) -> Self { fn neg(self) -> Self {
Self { Self {
seconds: self.seconds.neg(), seconds: self.seconds.neg(),
@ -561,9 +599,9 @@ mod tests {
fn from_str() { fn from_str() {
let min = Duration::new( let min = Duration::new(
i64::MIN + 1, i64::MIN + 1,
Decimal::min_value().checked_add(Decimal::step()).unwrap(), Decimal::MIN.checked_add(Decimal::step()).unwrap(),
); );
let max = Duration::new(i64::MAX, Decimal::max_value()); let max = Duration::new(i64::MAX, Decimal::MAX);
assert_eq!( assert_eq!(
YearMonthDuration::from_str("P1Y").unwrap().to_string(), YearMonthDuration::from_str("P1Y").unwrap().to_string(),
@ -701,6 +739,10 @@ mod tests {
Duration::from_str("P0Y0M10D").unwrap(), Duration::from_str("P0Y0M10D").unwrap(),
Duration::from_str("PT240H").unwrap() Duration::from_str("PT240H").unwrap()
); );
assert_ne!(
Duration::from_str("P1M").unwrap(),
Duration::from_str("P30D").unwrap()
);
} }
#[test] #[test]

@ -1,14 +1,14 @@
use crate::{Boolean, Double, Integer};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher};
use std::num::ParseFloatError; use std::num::ParseFloatError;
use std::ops::{Add, Div, Mul, Neg, Sub}; use std::ops::{Add, Div, Mul, Neg, Sub};
use std::str::FromStr; use std::str::FromStr;
/// [XML Schema `float` datatype](https://www.w3.org/TR/xmlschema11-2/#float) implementation. /// [XML Schema `float` datatype](https://www.w3.org/TR/xmlschema11-2/#float)
/// ///
/// The "==" implementation is identity, not equality /// Uses internally a [`f32`].
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default, PartialEq)]
#[repr(transparent)] #[repr(transparent)]
pub struct Float { pub struct Float {
value: f32, value: f32,
@ -51,26 +51,20 @@ impl Float {
self.value.round().into() self.value.round().into()
} }
/// Casts i64 into `Float`
#[inline] #[inline]
#[allow(clippy::cast_precision_loss)] pub fn is_naan(self) -> bool {
pub fn from_i64(value: i64) -> Self { self.value.is_nan()
Self {
value: value as f32,
}
} }
/// Casts `Float` into i64 without taking care of loss
#[inline] #[inline]
#[allow(clippy::cast_possible_truncation)] pub fn is_finite(self) -> bool {
pub fn to_i64(self) -> i64 { self.value.is_finite()
self.value as i64
} }
/// Creates a `bool` from a `Decimal` according to xsd:boolean cast constraints /// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline] #[inline]
pub fn to_bool(self) -> bool { pub fn is_identical_with(&self, other: &Self) -> bool {
self.value != 0. && !self.value.is_nan() self.value.to_ne_bytes() == other.value.to_ne_bytes()
} }
} }
@ -131,10 +125,32 @@ impl From<u16> for Float {
} }
} }
impl From<Boolean> for Float {
#[inline]
fn from(value: Boolean) -> Self {
if bool::from(value) { 1. } else { 0. }.into()
}
}
impl From<Integer> for Float {
#[inline]
fn from(value: Integer) -> Self {
(i64::from(value) as f32).into()
}
}
impl From<Double> for Float {
#[inline]
fn from(value: Double) -> Self {
Self {
value: f64::from(value) as f32,
}
}
}
impl FromStr for Float { impl FromStr for Float {
type Err = ParseFloatError; type Err = ParseFloatError;
/// Parses decimals lexical mapping
#[inline] #[inline]
fn from_str(input: &str) -> Result<Self, ParseFloatError> { fn from_str(input: &str) -> Result<Self, ParseFloatError> {
Ok(f32::from_str(input)?.into()) Ok(f32::from_str(input)?.into())
@ -154,15 +170,6 @@ impl fmt::Display for Float {
} }
} }
impl PartialEq for Float {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.value.to_ne_bytes() == other.value.to_ne_bytes()
}
}
impl Eq for Float {}
impl PartialOrd for Float { impl PartialOrd for Float {
#[inline] #[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@ -170,13 +177,6 @@ impl PartialOrd for Float {
} }
} }
impl Hash for Float {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.value.to_ne_bytes())
}
}
impl Neg for Float { impl Neg for Float {
type Output = Self; type Output = Self;
@ -228,14 +228,46 @@ mod tests {
#[test] #[test]
fn eq() { fn eq() {
assert_eq!(Float::from(0_f32), Float::from(0_f32)); assert_eq!(Float::from(0.), Float::from(0.));
assert_eq!(Float::from(f32::NAN), Float::from(f32::NAN)); assert_ne!(Float::from(f32::NAN), Float::from(f32::NAN));
assert_ne!(Float::from(-0.), Float::from(0.)); assert_eq!(Float::from(-0.), Float::from(0.));
}
#[test]
fn cmp() {
assert_eq!(
Float::from(0.).partial_cmp(&Float::from(0.)),
Some(Ordering::Equal)
);
assert_eq!(
Float::from(f32::INFINITY).partial_cmp(&Float::from(f32::MAX)),
Some(Ordering::Greater)
);
assert_eq!(
Float::from(f32::NEG_INFINITY).partial_cmp(&Float::from(f32::MIN)),
Some(Ordering::Less)
);
assert_eq!(Float::from(f32::NAN).partial_cmp(&Float::from(0.)), None);
assert_eq!(
Float::from(f32::NAN).partial_cmp(&Float::from(f32::NAN)),
None
);
assert_eq!(
Float::from(0.).partial_cmp(&Float::from(-0.)),
Some(Ordering::Equal)
);
}
#[test]
fn is_identical_with() {
assert!(Float::from(0.).is_identical_with(&Float::from(0.)));
assert!(Float::from(f32::NAN).is_identical_with(&Float::from(f32::NAN)));
assert!(!Float::from(-0.).is_identical_with(&Float::from(0.)));
} }
#[test] #[test]
fn from_str() -> Result<(), ParseFloatError> { fn from_str() -> Result<(), ParseFloatError> {
assert_eq!(Float::from(f32::NAN), Float::from_str("NaN")?); assert!(Float::from(f32::NAN).is_identical_with(&Float::from_str("NaN")?));
assert_eq!(Float::from(f32::INFINITY), Float::from_str("INF")?); assert_eq!(Float::from(f32::INFINITY), Float::from_str("INF")?);
assert_eq!(Float::from(f32::INFINITY), Float::from_str("+INF")?); assert_eq!(Float::from(f32::INFINITY), Float::from_str("+INF")?);
assert_eq!(Float::from(f32::NEG_INFINITY), Float::from_str("-INF")?); assert_eq!(Float::from(f32::NEG_INFINITY), Float::from_str("-INF")?);

@ -0,0 +1,302 @@
use crate::{Boolean, Decimal, DecimalOverflowError, Double, Float};
use std::fmt;
use std::num::ParseIntError;
use std::ops::Neg;
use std::str::FromStr;
/// [XML Schema `integer` datatype](https://www.w3.org/TR/xmlschema11-2/#integer)
///
/// Uses internally a [`i64`].
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Integer {
value: i64,
}
impl Integer {
#[inline]
pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
Self {
value: i64::from_be_bytes(bytes),
}
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 8] {
self.value.to_be_bytes()
}
/// [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add)
#[inline]
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
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<Self>) -> Option<Self> {
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<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_mul(rhs.into().value)?,
})
}
/// [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide)
#[inline]
pub fn checked_div(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_div(rhs.into().value)?,
})
}
#[inline]
pub fn checked_rem(&self, rhs: impl Into<Self>) -> Option<Self> {
Some(Self {
value: self.value.checked_rem(rhs.into().value)?,
})
}
#[inline]
pub fn checked_rem_euclid(&self, rhs: impl Into<Self>) -> Option<Self> {
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) -> Self {
Self {
value: self.value.abs(),
}
}
#[inline]
pub const fn is_negative(&self) -> bool {
self.value < 0
}
#[inline]
pub const fn is_positive(&self) -> bool {
self.value > 0
}
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[inline]
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
}
impl From<bool> for Integer {
#[inline]
fn from(value: bool) -> Self {
Self {
value: value.into(),
}
}
}
impl From<i8> for Integer {
#[inline]
fn from(value: i8) -> Self {
Self {
value: value.into(),
}
}
}
impl From<i16> for Integer {
#[inline]
fn from(value: i16) -> Self {
Self {
value: value.into(),
}
}
}
impl From<i32> for Integer {
#[inline]
fn from(value: i32) -> Self {
Self {
value: value.into(),
}
}
}
impl From<i64> for Integer {
#[inline]
fn from(value: i64) -> Self {
Self { value }
}
}
impl From<u8> for Integer {
#[inline]
fn from(value: u8) -> Self {
Self {
value: value.into(),
}
}
}
impl From<u16> for Integer {
#[inline]
fn from(value: u16) -> Self {
Self {
value: value.into(),
}
}
}
impl From<u32> for Integer {
#[inline]
fn from(value: u32) -> Self {
Self {
value: value.into(),
}
}
}
impl From<Boolean> for Integer {
#[inline]
fn from(value: Boolean) -> Self {
bool::from(value).into()
}
}
impl From<Integer> for i64 {
#[inline]
fn from(value: Integer) -> Self {
value.value
}
}
impl FromStr for Integer {
type Err = ParseIntError;
#[inline]
fn from_str(input: &str) -> Result<Self, ParseIntError> {
Ok(i64::from_str(input)?.into())
}
}
impl fmt::Display for Integer {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.value.fmt(f)
}
}
impl Neg for Integer {
type Output = Self;
#[inline]
fn neg(self) -> Self {
(-self.value).into()
}
}
impl TryFrom<Float> for Integer {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Float) -> Result<Self, DecimalOverflowError> {
Decimal::try_from(value)?.try_into()
}
}
impl TryFrom<Double> for Integer {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Double) -> Result<Self, DecimalOverflowError> {
Decimal::try_from(value)?.try_into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_float() {
assert_eq!(
Integer::try_from(Float::from(0.)).unwrap(),
Integer::from_str("0").unwrap()
);
assert_eq!(
Integer::try_from(Float::from(-0.)).unwrap(),
Integer::from_str("0").unwrap()
);
assert_eq!(
Integer::try_from(Float::from(-123.1)).unwrap(),
Integer::from_str("-123").unwrap()
);
assert!(Integer::try_from(Float::from(f32::NAN)).is_err());
assert!(Integer::try_from(Float::from(f32::INFINITY)).is_err());
assert!(Integer::try_from(Float::from(f32::NEG_INFINITY)).is_err());
assert!(Integer::try_from(Float::from(f32::MIN)).is_err());
assert!(Integer::try_from(Float::from(f32::MAX)).is_err());
assert!(
Integer::try_from(Float::from(1672507302466.))
.unwrap()
.checked_sub(Integer::from_str("1672507302466").unwrap())
.unwrap()
.abs()
< Integer::from(1_000_000)
);
}
#[test]
fn from_double() {
assert_eq!(
Integer::try_from(Double::from(0.0)).unwrap(),
Integer::from_str("0").unwrap()
);
assert_eq!(
Integer::try_from(Double::from(-0.0)).unwrap(),
Integer::from_str("0").unwrap()
);
assert_eq!(
Integer::try_from(Double::from(-123.1)).unwrap(),
Integer::from_str("-123").unwrap()
);
assert!(
Integer::try_from(Double::from(1672507302466.))
.unwrap()
.checked_sub(Integer::from_str("1672507302466").unwrap())
.unwrap()
.abs()
< Integer::from(1)
);
assert!(Integer::try_from(Double::from(f64::NAN)).is_err());
assert!(Integer::try_from(Double::from(f64::INFINITY)).is_err());
assert!(Integer::try_from(Double::from(f64::NEG_INFINITY)).is_err());
assert!(Integer::try_from(Double::from(f64::MIN)).is_err());
assert!(Integer::try_from(Double::from(f64::MAX)).is_err());
}
#[test]
fn from_decimal() {
assert_eq!(
Integer::try_from(Decimal::from(0)).unwrap(),
Integer::from_str("0").unwrap()
);
assert_eq!(
Integer::try_from(Decimal::from_str("-123.1").unwrap()).unwrap(),
Integer::from_str("-123").unwrap()
);
assert!(Integer::try_from(Decimal::MIN).is_err());
assert!(Integer::try_from(Decimal::MAX).is_err());
}
}

@ -0,0 +1,25 @@
#![doc = include_str!("../README.md")]
#![deny(unsafe_code)]
#![doc(test(attr(deny(warnings))))]
#![doc(html_favicon_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
#![doc(html_logo_url = "https://raw.githubusercontent.com/oxigraph/oxigraph/main/logo.svg")]
mod boolean;
mod date_time;
mod decimal;
mod double;
mod duration;
mod float;
mod integer;
mod parser;
pub use self::boolean::Boolean;
pub use self::date_time::{
Date, DateTime, DateTimeError, GDay, GMonth, GMonthDay, GYear, GYearMonth, Time,
};
pub use self::decimal::{Decimal, DecimalOverflowError, DecimalParseError};
pub use self::double::Double;
pub use self::duration::{DayTimeDuration, Duration, YearMonthDuration};
pub use self::float::Float;
pub use self::integer::Integer;
pub use self::parser::XsdParseError;

@ -1,5 +1,5 @@
use super::date_time::{DateTimeError, GDay, GMonth, GMonthDay, GYear, GYearMonth, TimezoneOffset}; use super::date_time::{DateTimeError, GDay, GMonth, GMonthDay, GYear, GYearMonth, TimezoneOffset};
use super::decimal::ParseDecimalError; use super::decimal::DecimalParseError;
use super::duration::{DayTimeDuration, YearMonthDuration}; use super::duration::{DayTimeDuration, YearMonthDuration};
use super::*; use super::*;
use nom::branch::alt; use nom::branch::alt;
@ -29,7 +29,7 @@ enum XsdParseErrorKind {
TooMuchData { count: usize }, TooMuchData { count: usize },
Overflow, Overflow,
ParseInt(ParseIntError), ParseInt(ParseIntError),
ParseDecimal(ParseDecimalError), ParseDecimal(DecimalParseError),
OutOfIntegerRange { value: u8, min: u8, max: u8 }, OutOfIntegerRange { value: u8, min: u8, max: u8 },
DateTime(DateTimeError), DateTime(DateTimeError),
} }
@ -108,8 +108,8 @@ impl From<ParseIntError> for XsdParseError {
} }
} }
impl From<ParseDecimalError> for XsdParseError { impl From<DecimalParseError> for XsdParseError {
fn from(error: ParseDecimalError) -> Self { fn from(error: DecimalParseError) -> Self {
Self { Self {
kind: XsdParseErrorKind::ParseDecimal(error), kind: XsdParseErrorKind::ParseDecimal(error),
} }

@ -131,8 +131,17 @@
)] )]
pub mod io; pub mod io;
pub mod model;
pub mod sparql; pub mod sparql;
mod storage; mod storage;
pub mod store; pub mod store;
mod xsd;
pub mod model {
//! Implements data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) using [OxRDF](https://crates.io/crates/oxrdf).
pub use oxrdf::{
dataset, graph, vocab, BlankNode, BlankNodeIdParseError, BlankNodeRef, Dataset, Graph,
GraphName, GraphNameRef, IriParseError, LanguageTagParseError, Literal, LiteralRef,
NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Subject,
SubjectRef, Term, TermParseError, TermRef, Triple, TripleRef,
};
}

@ -1,108 +0,0 @@
//! Implements data structures for [RDF 1.1 Concepts](https://www.w3.org/TR/rdf11-concepts/) using [OxRDF](https://crates.io/crates/oxrdf).
use crate::xsd::*;
use oxrdf::vocab::xsd;
pub use oxrdf::{
dataset, graph, vocab, BlankNode, BlankNodeIdParseError, BlankNodeRef, Dataset, Graph,
GraphName, GraphNameRef, IriParseError, LanguageTagParseError, Literal, LiteralRef, NamedNode,
NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, QuadRef, Subject, SubjectRef, Term,
TermParseError, TermRef, Triple, TripleRef,
};
impl From<Float> for Literal {
#[inline]
fn from(value: Float) -> Self {
Self::new_typed_literal(value.to_string(), xsd::FLOAT)
}
}
impl From<Double> for Literal {
#[inline]
fn from(value: Double) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DOUBLE)
}
}
impl From<Decimal> for Literal {
#[inline]
fn from(value: Decimal) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DECIMAL)
}
}
impl From<DateTime> for Literal {
#[inline]
fn from(value: DateTime) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DATE_TIME)
}
}
impl From<Time> for Literal {
#[inline]
fn from(value: Time) -> Self {
Self::new_typed_literal(value.to_string(), xsd::TIME)
}
}
impl From<Date> for Literal {
#[inline]
fn from(value: Date) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DATE)
}
}
impl From<GYearMonth> for Literal {
#[inline]
fn from(value: GYearMonth) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_YEAR_MONTH)
}
}
impl From<GYear> for Literal {
#[inline]
fn from(value: GYear) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_YEAR)
}
}
impl From<GMonthDay> for Literal {
#[inline]
fn from(value: GMonthDay) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_MONTH_DAY)
}
}
impl From<GMonth> for Literal {
#[inline]
fn from(value: GMonth) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_MONTH)
}
}
impl From<GDay> for Literal {
#[inline]
fn from(value: GDay) -> Self {
Self::new_typed_literal(value.to_string(), xsd::G_DAY)
}
}
impl From<Duration> for Literal {
#[inline]
fn from(value: Duration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DURATION)
}
}
impl From<YearMonthDuration> for Literal {
#[inline]
fn from(value: YearMonthDuration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::YEAR_MONTH_DURATION)
}
}
impl From<DayTimeDuration> for Literal {
#[inline]
fn from(value: DayTimeDuration) -> Self {
Self::new_typed_literal(value.to_string(), xsd::DAY_TIME_DURATION)
}
}

@ -9,12 +9,12 @@ use crate::sparql::plan::*;
use crate::sparql::service::ServiceHandler; use crate::sparql::service::ServiceHandler;
use crate::storage::numeric_encoder::*; use crate::storage::numeric_encoder::*;
use crate::storage::small_string::SmallString; use crate::storage::small_string::SmallString;
use crate::xsd::*;
use digest::Digest; use digest::Digest;
use md5::Md5; use md5::Md5;
use oxilangtag::LanguageTag; use oxilangtag::LanguageTag;
use oxiri::Iri; use oxiri::Iri;
use oxrdf::Variable; use oxrdf::Variable;
use oxsdatatypes::*;
use rand::random; use rand::random;
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
use sha1::Sha1; use sha1::Sha1;
@ -1203,7 +1203,7 @@ impl SimpleEvaluator {
PlanExpression::Abs(e) => { PlanExpression::Abs(e) => {
let e = self.expression_evaluator(e); let e = self.expression_evaluator(e);
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::IntegerLiteral(value) => Some(value.checked_abs()?.into()), EncodedTerm::IntegerLiteral(value) => Some(value.abs().into()),
EncodedTerm::DecimalLiteral(value) => Some(value.abs().into()), EncodedTerm::DecimalLiteral(value) => Some(value.abs().into()),
EncodedTerm::FloatLiteral(value) => Some(value.abs().into()), EncodedTerm::FloatLiteral(value) => Some(value.abs().into()),
EncodedTerm::DoubleLiteral(value) => Some(value.abs().into()), EncodedTerm::DoubleLiteral(value) => Some(value.abs().into()),
@ -1274,13 +1274,13 @@ impl SimpleEvaluator {
let starting_location: usize = let starting_location: usize =
if let EncodedTerm::IntegerLiteral(v) = starting_loc(tuple)? { if let EncodedTerm::IntegerLiteral(v) = starting_loc(tuple)? {
v.try_into().ok()? i64::from(v).try_into().ok()?
} else { } else {
return None; return None;
}; };
let length: Option<usize> = if let Some(length) = &length { let length: Option<usize> = if let Some(length) = &length {
if let EncodedTerm::IntegerLiteral(v) = length(tuple)? { if let EncodedTerm::IntegerLiteral(v) = length(tuple)? {
Some(v.try_into().ok()?) Some(i64::from(v).try_into().ok()?)
} else { } else {
return None; return None;
} }
@ -1738,17 +1738,13 @@ impl SimpleEvaluator {
} }
PlanExpression::BooleanCast(e) => { PlanExpression::BooleanCast(e) => {
let e = self.expression_evaluator(e); let e = self.expression_evaluator(e);
let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::BooleanLiteral(value) => Some(value.into()), EncodedTerm::BooleanLiteral(value) => Some(value.into()),
EncodedTerm::FloatLiteral(value) => Some(value.to_bool().into()), EncodedTerm::FloatLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_bool().into()), EncodedTerm::DoubleLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::IntegerLiteral(value) => Some((value != 0).into()), EncodedTerm::IntegerLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_bool().into()), EncodedTerm::DecimalLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_boolean_str(&value), EncodedTerm::SmallStringLiteral(value) => parse_boolean_str(&value),
EncodedTerm::BigStringLiteral { value_id } => {
parse_boolean_str(&dataset.get_str(&value_id).ok()??)
}
_ => None, _ => None,
}) })
} }
@ -1756,13 +1752,11 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e); let e = self.expression_evaluator(e);
let dataset = self.dataset.clone(); let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(f64::from(value).into()), EncodedTerm::FloatLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::DoubleLiteral(value) => Some(value.into()), EncodedTerm::DoubleLiteral(value) => Some(value.into()),
EncodedTerm::IntegerLiteral(value) => Some((value as f64).into()), EncodedTerm::IntegerLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_double().into()), EncodedTerm::DecimalLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::BooleanLiteral(value) => { EncodedTerm::BooleanLiteral(value) => Some(Double::from(value).into()),
Some(if value { 1_f64 } else { 0_f64 }.into())
}
EncodedTerm::SmallStringLiteral(value) => parse_double_str(&value), EncodedTerm::SmallStringLiteral(value) => parse_double_str(&value),
EncodedTerm::BigStringLiteral { value_id } => { EncodedTerm::BigStringLiteral { value_id } => {
parse_double_str(&dataset.get_str(&value_id).ok()??) parse_double_str(&dataset.get_str(&value_id).ok()??)
@ -1775,12 +1769,10 @@ impl SimpleEvaluator {
let dataset = self.dataset.clone(); let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(value.into()), EncodedTerm::FloatLiteral(value) => Some(value.into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_f32().into()), EncodedTerm::DoubleLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::IntegerLiteral(value) => Some((value as f32).into()), EncodedTerm::IntegerLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_float().into()), EncodedTerm::DecimalLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::BooleanLiteral(value) => { EncodedTerm::BooleanLiteral(value) => Some(Float::from(value).into()),
Some(if value { 1_f32 } else { 0_f32 }.into())
}
EncodedTerm::SmallStringLiteral(value) => parse_float_str(&value), EncodedTerm::SmallStringLiteral(value) => parse_float_str(&value),
EncodedTerm::BigStringLiteral { value_id } => { EncodedTerm::BigStringLiteral { value_id } => {
parse_float_str(&dataset.get_str(&value_id).ok()??) parse_float_str(&dataset.get_str(&value_id).ok()??)
@ -1792,11 +1784,15 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e); let e = self.expression_evaluator(e);
let dataset = self.dataset.clone(); let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(value.to_i64().into()), EncodedTerm::FloatLiteral(value) => Some(Integer::try_from(value).ok()?.into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_i64().into()), EncodedTerm::DoubleLiteral(value) => {
Some(Integer::try_from(value).ok()?.into())
}
EncodedTerm::IntegerLiteral(value) => Some(value.into()), EncodedTerm::IntegerLiteral(value) => Some(value.into()),
EncodedTerm::DecimalLiteral(value) => Some(i64::try_from(value).ok()?.into()), EncodedTerm::DecimalLiteral(value) => {
EncodedTerm::BooleanLiteral(value) => Some(i64::from(value).into()), Some(Integer::try_from(value).ok()?.into())
}
EncodedTerm::BooleanLiteral(value) => Some(Integer::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_integer_str(&value), EncodedTerm::SmallStringLiteral(value) => parse_integer_str(&value),
EncodedTerm::BigStringLiteral { value_id } => { EncodedTerm::BigStringLiteral { value_id } => {
parse_integer_str(&dataset.get_str(&value_id).ok()??) parse_integer_str(&dataset.get_str(&value_id).ok()??)
@ -1808,9 +1804,13 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e); let e = self.expression_evaluator(e);
let dataset = self.dataset.clone(); let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? { Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(Decimal::from_float(value).into()), EncodedTerm::FloatLiteral(value) => Some(Decimal::try_from(value).ok()?.into()),
EncodedTerm::DoubleLiteral(value) => Some(Decimal::from_double(value).into()), EncodedTerm::DoubleLiteral(value) => {
EncodedTerm::IntegerLiteral(value) => Some(Decimal::from(value).into()), Some(Decimal::try_from(value).ok()?.into())
}
EncodedTerm::IntegerLiteral(value) => {
Some(Decimal::try_from(value).ok()?.into())
}
EncodedTerm::DecimalLiteral(value) => Some(value.into()), EncodedTerm::DecimalLiteral(value) => Some(value.into()),
EncodedTerm::BooleanLiteral(value) => Some(Decimal::from(value).into()), EncodedTerm::BooleanLiteral(value) => Some(Decimal::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_decimal_str(&value), EncodedTerm::SmallStringLiteral(value) => parse_decimal_str(&value),
@ -1944,15 +1944,15 @@ impl SimpleEvaluator {
fn to_bool(term: &EncodedTerm) -> Option<bool> { fn to_bool(term: &EncodedTerm) -> Option<bool> {
match term { match term {
EncodedTerm::BooleanLiteral(value) => Some(*value), EncodedTerm::BooleanLiteral(value) => Some((*value).into()),
EncodedTerm::SmallStringLiteral(value) => Some(!value.is_empty()), EncodedTerm::SmallStringLiteral(value) => Some(!value.is_empty()),
EncodedTerm::BigStringLiteral { .. } => { EncodedTerm::BigStringLiteral { .. } => {
Some(false) // A big literal can't be empty Some(false) // A big literal can't be empty
} }
EncodedTerm::FloatLiteral(value) => Some(*value != Float::default()), EncodedTerm::FloatLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::DoubleLiteral(value) => Some(*value != Double::default()), EncodedTerm::DoubleLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::IntegerLiteral(value) => Some(*value != 0), EncodedTerm::IntegerLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::DecimalLiteral(value) => Some(*value != Decimal::default()), EncodedTerm::DecimalLiteral(value) => Some(Boolean::from(*value).into()),
_ => None, _ => None,
} }
} }
@ -1975,7 +1975,7 @@ fn to_string_id(dataset: &DatasetView, term: &EncodedTerm) -> Option<SmallString
| EncodedTerm::BigTypedLiteral { value_id, .. } => Some((*value_id).into()), | EncodedTerm::BigTypedLiteral { value_id, .. } => Some((*value_id).into()),
EncodedTerm::BooleanLiteral(value) => Some(build_string_id( EncodedTerm::BooleanLiteral(value) => Some(build_string_id(
dataset, dataset,
if *value { "true" } else { "false" }, if bool::from(*value) { "true" } else { "false" },
)), )),
EncodedTerm::FloatLiteral(value) => Some(build_string_id(dataset, &value.to_string())), EncodedTerm::FloatLiteral(value) => Some(build_string_id(dataset, &value.to_string())),
EncodedTerm::DoubleLiteral(value) => Some(build_string_id(dataset, &value.to_string())), EncodedTerm::DoubleLiteral(value) => Some(build_string_id(dataset, &value.to_string())),
@ -2285,30 +2285,30 @@ fn equals(a: &EncodedTerm, b: &EncodedTerm) -> Option<bool> {
EncodedTerm::FloatLiteral(a) => match b { EncodedTerm::FloatLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(a == b), EncodedTerm::FloatLiteral(b) => Some(a == b),
EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b), EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Float::from_i64(*b)), EncodedTerm::IntegerLiteral(b) => Some(*a == Float::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_float()), EncodedTerm::DecimalLiteral(b) => Some(*a == (*b).try_into().ok()?),
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
_ => Some(false), _ => Some(false),
}, },
EncodedTerm::DoubleLiteral(a) => match b { EncodedTerm::DoubleLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(*a == Double::from(*b)), EncodedTerm::FloatLiteral(b) => Some(*a == Double::from(*b)),
EncodedTerm::DoubleLiteral(b) => Some(a == b), EncodedTerm::DoubleLiteral(b) => Some(a == b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Double::from_i64(*b)), EncodedTerm::IntegerLiteral(b) => Some(*a == Double::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_double()), EncodedTerm::DecimalLiteral(b) => Some(*a == (*b).try_into().ok()?),
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
_ => Some(false), _ => Some(false),
}, },
EncodedTerm::IntegerLiteral(a) => match b { EncodedTerm::IntegerLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(Float::from_i64(*a) == *b), EncodedTerm::FloatLiteral(b) => Some(Float::from(*a) == *b),
EncodedTerm::DoubleLiteral(b) => Some(Double::from_i64(*a) == *b), EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b),
EncodedTerm::IntegerLiteral(b) => Some(a == b), EncodedTerm::IntegerLiteral(b) => Some(a == b),
EncodedTerm::DecimalLiteral(b) => Some(Decimal::from(*a) == *b), EncodedTerm::DecimalLiteral(b) => Some(Decimal::from(*a) == *b),
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
_ => Some(false), _ => Some(false),
}, },
EncodedTerm::DecimalLiteral(a) => match b { EncodedTerm::DecimalLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(a.to_float() == *b), EncodedTerm::FloatLiteral(b) => Some(Float::try_from(*a).ok()? == *b),
EncodedTerm::DoubleLiteral(b) => Some(a.to_double() == *b), EncodedTerm::DoubleLiteral(b) => Some(Double::try_from(*a).ok()? == *b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Decimal::from(*b)), EncodedTerm::IntegerLiteral(b) => Some(*a == Decimal::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(a == b), EncodedTerm::DecimalLiteral(b) => Some(a == b),
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
@ -2573,27 +2573,27 @@ fn partial_cmp_literals(
EncodedTerm::FloatLiteral(a) => match b { EncodedTerm::FloatLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(b), EncodedTerm::FloatLiteral(b) => a.partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b), EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Float::from_i64(*b)), EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Float::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_float()), EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&(*b).try_into().ok()?),
_ => None, _ => None,
}, },
EncodedTerm::DoubleLiteral(a) => match b { EncodedTerm::DoubleLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(&(*b).into()), EncodedTerm::FloatLiteral(b) => a.partial_cmp(&(*b).into()),
EncodedTerm::DoubleLiteral(b) => a.partial_cmp(b), EncodedTerm::DoubleLiteral(b) => a.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Double::from_i64(*b)), EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Double::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_double()), EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&(*b).try_into().ok()?),
_ => None, _ => None,
}, },
EncodedTerm::IntegerLiteral(a) => match b { EncodedTerm::IntegerLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Float::from_i64(*a).partial_cmp(b), EncodedTerm::FloatLiteral(b) => Float::from(*a).partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::from_i64(*a).partial_cmp(b), EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(b), EncodedTerm::IntegerLiteral(b) => a.partial_cmp(b),
EncodedTerm::DecimalLiteral(b) => Decimal::from(*a).partial_cmp(b), EncodedTerm::DecimalLiteral(b) => Decimal::from(*a).partial_cmp(b),
_ => None, _ => None,
}, },
EncodedTerm::DecimalLiteral(a) => match b { EncodedTerm::DecimalLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.to_float().partial_cmp(b), EncodedTerm::FloatLiteral(b) => Float::try_from(*a).ok()?.partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => a.to_double().partial_cmp(b), EncodedTerm::DoubleLiteral(b) => Double::try_from(*a).ok()?.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Decimal::from(*b)), EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Decimal::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(b), EncodedTerm::DecimalLiteral(b) => a.partial_cmp(b),
_ => None, _ => None,
@ -2736,7 +2736,7 @@ fn datatype(dataset: &DatasetView, value: &EncodedTerm) -> Option<EncodedTerm> {
enum NumericBinaryOperands { enum NumericBinaryOperands {
Float(Float, Float), Float(Float, Float),
Double(Double, Double), Double(Double, Double),
Integer(i64, i64), Integer(Integer, Integer),
Decimal(Decimal, Decimal), Decimal(Decimal, Decimal),
Duration(Duration, Duration), Duration(Duration, Duration),
YearMonthDuration(YearMonthDuration, YearMonthDuration), YearMonthDuration(YearMonthDuration, YearMonthDuration),
@ -2765,10 +2765,10 @@ impl NumericBinaryOperands {
Some(Self::Double(v1.into(), v2)) Some(Self::Double(v1.into(), v2))
} }
(EncodedTerm::FloatLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { (EncodedTerm::FloatLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => {
Some(Self::Float(v1, Float::from_i64(v2))) Some(Self::Float(v1, v2.into()))
} }
(EncodedTerm::FloatLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { (EncodedTerm::FloatLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(Self::Float(v1, v2.to_float())) Some(Self::Float(v1, v2.into()))
} }
(EncodedTerm::DoubleLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { (EncodedTerm::DoubleLiteral(v1), EncodedTerm::FloatLiteral(v2)) => {
Some(Self::Double(v1, v2.into())) Some(Self::Double(v1, v2.into()))
@ -2777,31 +2777,31 @@ impl NumericBinaryOperands {
Some(Self::Double(v1, v2)) Some(Self::Double(v1, v2))
} }
(EncodedTerm::DoubleLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { (EncodedTerm::DoubleLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => {
Some(Self::Double(v1, Double::from_i64(v2))) Some(Self::Double(v1, v2.into()))
} }
(EncodedTerm::DoubleLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { (EncodedTerm::DoubleLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(Self::Double(v1, v2.to_double())) Some(Self::Double(v1, v2.into()))
} }
(EncodedTerm::IntegerLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { (EncodedTerm::IntegerLiteral(v1), EncodedTerm::FloatLiteral(v2)) => {
Some(Self::Float(Float::from_i64(v1), v2)) Some(Self::Float(v1.into(), v2))
} }
(EncodedTerm::IntegerLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => { (EncodedTerm::IntegerLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => {
Some(Self::Double(Double::from_i64(v1), v2)) Some(Self::Double(v1.into(), v2))
} }
(EncodedTerm::IntegerLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { (EncodedTerm::IntegerLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => {
Some(Self::Integer(v1, v2)) Some(Self::Integer(v1, v2))
} }
(EncodedTerm::IntegerLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { (EncodedTerm::IntegerLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(Self::Decimal(Decimal::from(v1), v2)) Some(Self::Decimal(v1.into(), v2))
} }
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::FloatLiteral(v2)) => { (EncodedTerm::DecimalLiteral(v1), EncodedTerm::FloatLiteral(v2)) => {
Some(Self::Float(v1.to_float(), v2)) Some(Self::Float(v1.into(), v2))
} }
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => { (EncodedTerm::DecimalLiteral(v1), EncodedTerm::DoubleLiteral(v2)) => {
Some(Self::Double(v1.to_double(), v2)) Some(Self::Double(v1.into(), v2))
} }
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => { (EncodedTerm::DecimalLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => {
Some(Self::Decimal(v1, Decimal::from(v2))) Some(Self::Decimal(v1, v2.into()))
} }
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => { (EncodedTerm::DecimalLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(Self::Decimal(v1, v2)) Some(Self::Decimal(v1, v2))
@ -2810,13 +2810,13 @@ impl NumericBinaryOperands {
Some(Self::Duration(v1, v2)) Some(Self::Duration(v1, v2))
} }
(EncodedTerm::DurationLiteral(v1), EncodedTerm::YearMonthDurationLiteral(v2)) => { (EncodedTerm::DurationLiteral(v1), EncodedTerm::YearMonthDurationLiteral(v2)) => {
Some(Self::Duration(v1, (v2).into())) Some(Self::Duration(v1, v2.into()))
} }
(EncodedTerm::DurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(v2)) => { (EncodedTerm::DurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(v2)) => {
Some(Self::Duration(v1, (v2).into())) Some(Self::Duration(v1, v2.into()))
} }
(EncodedTerm::YearMonthDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => { (EncodedTerm::YearMonthDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(Self::Duration((v1).into(), v2)) Some(Self::Duration(v1.into(), v2))
} }
( (
EncodedTerm::YearMonthDurationLiteral(v1), EncodedTerm::YearMonthDurationLiteral(v1),
@ -2825,14 +2825,14 @@ impl NumericBinaryOperands {
( (
EncodedTerm::YearMonthDurationLiteral(v1), EncodedTerm::YearMonthDurationLiteral(v1),
EncodedTerm::DayTimeDurationLiteral(v2), EncodedTerm::DayTimeDurationLiteral(v2),
) => Some(Self::Duration((v1).into(), (v2).into())), ) => Some(Self::Duration(v1.into(), v2.into())),
(EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => { (EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(Self::Duration((v1).into(), v2)) Some(Self::Duration(v1.into(), v2))
} }
( (
EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(v1),
EncodedTerm::YearMonthDurationLiteral(v2), EncodedTerm::YearMonthDurationLiteral(v2),
) => Some(Self::Duration((v1).into(), (v2).into())), ) => Some(Self::Duration(v1.into(), v2.into())),
(EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(v2)) => { (EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(v2)) => {
Some(Self::DayTimeDuration(v1, v2)) Some(Self::DayTimeDuration(v1, v2))
} }

@ -2,10 +2,9 @@ use crate::storage::numeric_encoder::{EncodedQuad, EncodedTerm, EncodedTriple, S
use crate::storage::small_string::SmallString; use crate::storage::small_string::SmallString;
use crate::storage::StorageError; use crate::storage::StorageError;
use crate::store::CorruptionError; use crate::store::CorruptionError;
use crate::xsd::*; use oxsdatatypes::*;
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use std::mem::size_of; use std::mem::size_of;
use std::rc::Rc;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub const LATEST_STORAGE_VERSION: u64 = 1; pub const LATEST_STORAGE_VERSION: u64 = 1;
@ -314,100 +313,89 @@ impl<R: Read> TermReader for R {
value_id: StrHash::from_be_bytes(buffer), value_id: StrHash::from_be_bytes(buffer),
}) })
} }
TYPE_BOOLEAN_LITERAL_TRUE => Ok(EncodedTerm::BooleanLiteral(true)), TYPE_BOOLEAN_LITERAL_TRUE => Ok(true.into()),
TYPE_BOOLEAN_LITERAL_FALSE => Ok(EncodedTerm::BooleanLiteral(false)), TYPE_BOOLEAN_LITERAL_FALSE => Ok(false.into()),
TYPE_FLOAT_LITERAL => { TYPE_FLOAT_LITERAL => {
let mut buffer = [0; 4]; let mut buffer = [0; 4];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::FloatLiteral(Float::from_be_bytes(buffer))) Ok(Float::from_be_bytes(buffer).into())
} }
TYPE_DOUBLE_LITERAL => { TYPE_DOUBLE_LITERAL => {
let mut buffer = [0; 8]; let mut buffer = [0; 8];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DoubleLiteral(Double::from_be_bytes(buffer))) Ok(Double::from_be_bytes(buffer).into())
} }
TYPE_INTEGER_LITERAL => { TYPE_INTEGER_LITERAL => {
let mut buffer = [0; 8]; let mut buffer = [0; 8];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::IntegerLiteral(i64::from_be_bytes(buffer))) Ok(Integer::from_be_bytes(buffer).into())
} }
TYPE_DECIMAL_LITERAL => { TYPE_DECIMAL_LITERAL => {
let mut buffer = [0; 16]; let mut buffer = [0; 16];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::from_be_bytes(buffer))) Ok(Decimal::from_be_bytes(buffer).into())
} }
TYPE_DATE_TIME_LITERAL => { TYPE_DATE_TIME_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DateTimeLiteral(DateTime::from_be_bytes( Ok(DateTime::from_be_bytes(buffer).into())
buffer,
)))
} }
TYPE_TIME_LITERAL => { TYPE_TIME_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::TimeLiteral(Time::from_be_bytes(buffer))) Ok(Time::from_be_bytes(buffer).into())
} }
TYPE_DATE_LITERAL => { TYPE_DATE_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DateLiteral(Date::from_be_bytes(buffer))) Ok(Date::from_be_bytes(buffer).into())
} }
TYPE_G_YEAR_MONTH_LITERAL => { TYPE_G_YEAR_MONTH_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GYearMonthLiteral(GYearMonth::from_be_bytes( Ok(GYearMonth::from_be_bytes(buffer).into())
buffer,
)))
} }
TYPE_G_YEAR_LITERAL => { TYPE_G_YEAR_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GYearLiteral(GYear::from_be_bytes(buffer))) Ok(GYear::from_be_bytes(buffer).into())
} }
TYPE_G_MONTH_DAY_LITERAL => { TYPE_G_MONTH_DAY_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GMonthDayLiteral(GMonthDay::from_be_bytes( Ok(GMonthDay::from_be_bytes(buffer).into())
buffer,
)))
} }
TYPE_G_DAY_LITERAL => { TYPE_G_DAY_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GDayLiteral(GDay::from_be_bytes(buffer))) Ok(GDay::from_be_bytes(buffer).into())
} }
TYPE_G_MONTH_LITERAL => { TYPE_G_MONTH_LITERAL => {
let mut buffer = [0; 18]; let mut buffer = [0; 18];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GMonthLiteral(GMonth::from_be_bytes(buffer))) Ok(GMonth::from_be_bytes(buffer).into())
} }
TYPE_DURATION_LITERAL => { TYPE_DURATION_LITERAL => {
let mut buffer = [0; 24]; let mut buffer = [0; 24];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DurationLiteral(Duration::from_be_bytes( Ok(Duration::from_be_bytes(buffer).into())
buffer,
)))
} }
TYPE_YEAR_MONTH_DURATION_LITERAL => { TYPE_YEAR_MONTH_DURATION_LITERAL => {
let mut buffer = [0; 8]; let mut buffer = [0; 8];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::YearMonthDurationLiteral( Ok(YearMonthDuration::from_be_bytes(buffer).into())
YearMonthDuration::from_be_bytes(buffer),
))
} }
TYPE_DAY_TIME_DURATION_LITERAL => { TYPE_DAY_TIME_DURATION_LITERAL => {
let mut buffer = [0; 16]; let mut buffer = [0; 16];
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DayTimeDurationLiteral( Ok(DayTimeDuration::from_be_bytes(buffer).into())
DayTimeDuration::from_be_bytes(buffer),
))
} }
TYPE_TRIPLE => Ok(EncodedTerm::Triple(Rc::new(EncodedTriple { TYPE_TRIPLE => Ok(EncodedTriple {
subject: self.read_term()?, subject: self.read_term()?,
predicate: self.read_term()?, predicate: self.read_term()?,
object: self.read_term()?, object: self.read_term()?,
}))), }
.into()),
_ => Err(CorruptionError::msg("the term buffer has an invalid type id").into()), _ => Err(CorruptionError::msg("the term buffer has an invalid type id").into()),
} }
} }
@ -571,8 +559,11 @@ pub fn write_term(sink: &mut Vec<u8>, term: &EncodedTerm) {
sink.extend_from_slice(&datatype_id.to_be_bytes()); sink.extend_from_slice(&datatype_id.to_be_bytes());
sink.extend_from_slice(&value_id.to_be_bytes()); sink.extend_from_slice(&value_id.to_be_bytes());
} }
EncodedTerm::BooleanLiteral(true) => sink.push(TYPE_BOOLEAN_LITERAL_TRUE), EncodedTerm::BooleanLiteral(value) => sink.push(if bool::from(*value) {
EncodedTerm::BooleanLiteral(false) => sink.push(TYPE_BOOLEAN_LITERAL_FALSE), TYPE_BOOLEAN_LITERAL_TRUE
} else {
TYPE_BOOLEAN_LITERAL_FALSE
}),
EncodedTerm::FloatLiteral(value) => { EncodedTerm::FloatLiteral(value) => {
sink.push(TYPE_FLOAT_LITERAL); sink.push(TYPE_FLOAT_LITERAL);
sink.extend_from_slice(&value.to_be_bytes()) sink.extend_from_slice(&value.to_be_bytes())

@ -3,7 +3,7 @@
use crate::model::*; use crate::model::*;
use crate::storage::small_string::SmallString; use crate::storage::small_string::SmallString;
use crate::store::{CorruptionError, StorageError}; use crate::store::{CorruptionError, StorageError};
use crate::xsd::*; use oxsdatatypes::*;
use siphasher::sip128::{Hasher128, SipHasher24}; use siphasher::sip128::{Hasher128, SipHasher24};
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
@ -80,10 +80,10 @@ pub enum EncodedTerm {
value_id: StrHash, value_id: StrHash,
datatype_id: StrHash, datatype_id: StrHash,
}, },
BooleanLiteral(bool), BooleanLiteral(Boolean),
FloatLiteral(Float), FloatLiteral(Float),
DoubleLiteral(Double), DoubleLiteral(Double),
IntegerLiteral(i64), IntegerLiteral(Integer),
DecimalLiteral(Decimal), DecimalLiteral(Decimal),
DateTimeLiteral(DateTime), DateTimeLiteral(DateTime),
TimeLiteral(Time), TimeLiteral(Time),
@ -183,10 +183,10 @@ impl PartialEq for EncodedTerm {
}, },
) => value_id_a == value_id_b && datatype_id_a == datatype_id_b, ) => value_id_a == value_id_b && datatype_id_a == datatype_id_b,
(Self::BooleanLiteral(a), Self::BooleanLiteral(b)) => a == b, (Self::BooleanLiteral(a), Self::BooleanLiteral(b)) => a == b,
(Self::FloatLiteral(a), Self::FloatLiteral(b)) => a == b, (Self::FloatLiteral(a), Self::FloatLiteral(b)) => a.is_identical_with(b),
(Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => a == b, (Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => a.is_identical_with(b),
(Self::IntegerLiteral(a), Self::IntegerLiteral(b)) => a == b, (Self::IntegerLiteral(a), Self::IntegerLiteral(b)) => a.is_identical_with(b),
(Self::DecimalLiteral(a), Self::DecimalLiteral(b)) => a == b, (Self::DecimalLiteral(a), Self::DecimalLiteral(b)) => a.is_identical_with(b),
(Self::DateTimeLiteral(a), Self::DateTimeLiteral(b)) => a.is_identical_with(b), (Self::DateTimeLiteral(a), Self::DateTimeLiteral(b)) => a.is_identical_with(b),
(Self::TimeLiteral(a), Self::TimeLiteral(b)) => a.is_identical_with(b), (Self::TimeLiteral(a), Self::TimeLiteral(b)) => a.is_identical_with(b),
(Self::DateLiteral(a), Self::DateLiteral(b)) => a.is_identical_with(b), (Self::DateLiteral(a), Self::DateLiteral(b)) => a.is_identical_with(b),
@ -195,9 +195,13 @@ impl PartialEq for EncodedTerm {
(Self::GMonthDayLiteral(a), Self::GMonthDayLiteral(b)) => a.is_identical_with(b), (Self::GMonthDayLiteral(a), Self::GMonthDayLiteral(b)) => a.is_identical_with(b),
(Self::GMonthLiteral(a), Self::GMonthLiteral(b)) => a.is_identical_with(b), (Self::GMonthLiteral(a), Self::GMonthLiteral(b)) => a.is_identical_with(b),
(Self::GDayLiteral(a), Self::GDayLiteral(b)) => a.is_identical_with(b), (Self::GDayLiteral(a), Self::GDayLiteral(b)) => a.is_identical_with(b),
(Self::DurationLiteral(a), Self::DurationLiteral(b)) => a == b, (Self::DurationLiteral(a), Self::DurationLiteral(b)) => a.is_identical_with(b),
(Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => a == b, (Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => {
(Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => a == b, a.is_identical_with(b)
}
(Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => {
a.is_identical_with(b)
}
(Self::Triple(a), Self::Triple(b)) => a == b, (Self::Triple(a), Self::Triple(b)) => a == b,
(_, _) => false, (_, _) => false,
} }
@ -247,8 +251,8 @@ impl Hash for EncodedTerm {
datatype_id.hash(state); datatype_id.hash(state);
} }
Self::BooleanLiteral(value) => value.hash(state), Self::BooleanLiteral(value) => value.hash(state),
Self::FloatLiteral(value) => value.hash(state), Self::FloatLiteral(value) => value.to_be_bytes().hash(state),
Self::DoubleLiteral(value) => value.hash(state), Self::DoubleLiteral(value) => value.to_be_bytes().hash(state),
Self::IntegerLiteral(value) => value.hash(state), Self::IntegerLiteral(value) => value.hash(state),
Self::DecimalLiteral(value) => value.hash(state), Self::DecimalLiteral(value) => value.hash(state),
Self::DateTimeLiteral(value) => value.hash(state), Self::DateTimeLiteral(value) => value.hash(state),
@ -329,13 +333,13 @@ impl EncodedTerm {
impl From<bool> for EncodedTerm { impl From<bool> for EncodedTerm {
fn from(value: bool) -> Self { fn from(value: bool) -> Self {
Self::BooleanLiteral(value) Self::BooleanLiteral(value.into())
} }
} }
impl From<i64> for EncodedTerm { impl From<i64> for EncodedTerm {
fn from(value: i64) -> Self { fn from(value: i64) -> Self {
Self::IntegerLiteral(value) Self::IntegerLiteral(value.into())
} }
} }
@ -375,12 +379,24 @@ impl From<f64> for EncodedTerm {
} }
} }
impl From<Boolean> for EncodedTerm {
fn from(value: Boolean) -> Self {
Self::BooleanLiteral(value)
}
}
impl From<Double> for EncodedTerm { impl From<Double> for EncodedTerm {
fn from(value: Double) -> Self { fn from(value: Double) -> Self {
Self::DoubleLiteral(value) Self::DoubleLiteral(value)
} }
} }
impl From<Integer> for EncodedTerm {
fn from(value: Integer) -> Self {
Self::IntegerLiteral(value)
}
}
impl From<Decimal> for EncodedTerm { impl From<Decimal> for EncodedTerm {
fn from(value: Decimal) -> Self { fn from(value: Decimal) -> Self {
Self::DecimalLiteral(value) Self::DecimalLiteral(value)
@ -405,6 +421,36 @@ impl From<Date> for EncodedTerm {
} }
} }
impl From<GMonthDay> for EncodedTerm {
fn from(value: GMonthDay) -> Self {
Self::GMonthDayLiteral(value)
}
}
impl From<GDay> for EncodedTerm {
fn from(value: GDay) -> Self {
Self::GDayLiteral(value)
}
}
impl From<GMonth> for EncodedTerm {
fn from(value: GMonth) -> Self {
Self::GMonthLiteral(value)
}
}
impl From<GYearMonth> for EncodedTerm {
fn from(value: GYearMonth) -> Self {
Self::GYearMonthLiteral(value)
}
}
impl From<GYear> for EncodedTerm {
fn from(value: GYear) -> Self {
Self::GYearLiteral(value)
}
}
impl From<Duration> for EncodedTerm { impl From<Duration> for EncodedTerm {
fn from(value: Duration) -> Self { fn from(value: Duration) -> Self {
Self::DurationLiteral(value) Self::DurationLiteral(value)
@ -749,11 +795,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
} }
pub fn parse_boolean_str(value: &str) -> Option<EncodedTerm> { pub fn parse_boolean_str(value: &str) -> Option<EncodedTerm> {
match value { value.parse().map(EncodedTerm::BooleanLiteral).ok()
"true" | "1" => Some(EncodedTerm::BooleanLiteral(true)),
"false" | "0" => Some(EncodedTerm::BooleanLiteral(false)),
_ => None,
}
} }
pub fn parse_float_str(value: &str) -> Option<EncodedTerm> { pub fn parse_float_str(value: &str) -> Option<EncodedTerm> {

@ -1,13 +0,0 @@
pub mod date_time;
pub mod decimal;
mod double;
mod duration;
mod float;
mod parser;
pub use self::date_time::{Date, DateTime, GDay, GMonth, GMonthDay, GYear, GYearMonth, Time};
pub use self::decimal::Decimal;
pub use self::double::Double;
pub use self::duration::{DayTimeDuration, Duration, YearMonthDuration};
pub use self::float::Float;
pub use self::parser::XsdParseError;
Loading…
Cancel
Save