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
- uses: actions/upload-artifact@v3
with:
name: pyoxigraph_x86_64_linux
name: pyoxigraph_wheel_x86_64_linux
path: target/wheels/*.whl
wheel_mac:

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

@ -26,6 +26,8 @@ jobs:
with:
submodules: true
- run: rustup update && rustup component add clippy
- run: cargo clippy
working-directory: ./lib/oxsdatatypes
- run: cargo clippy
working-directory: ./lib/oxrdf
- run: cargo clippy
@ -41,6 +43,8 @@ jobs:
with:
submodules: true
- 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
working-directory: ./lib/oxrdf
- run: cargo clippy -- -D warnings -D clippy::all

12
Cargo.lock generated

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

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

@ -34,11 +34,11 @@ rio_api = "0.8"
rio_turtle = "0.8"
rio_xml = "0.8"
hex = "0.4"
nom = "7"
siphasher = "0.3"
lazy_static = "1"
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"] }
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 }
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = "0.3"
getrandom = {version="0.2", features=["js"]}
[dev-dependencies]

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

@ -3,6 +3,8 @@ use crate::vocab::rdf;
use crate::vocab::xsd;
use crate::NamedNodeRef;
use oxilangtag::{LanguageTag, LanguageTagParseError};
#[cfg(feature = "oxsdatatypes")]
use oxsdatatypes::*;
use std::borrow::Cow;
use std::fmt;
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).
///
/// 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::fmt;
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_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 ".".
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -21,6 +21,7 @@ pub struct Decimal {
impl Decimal {
/// Constructs the decimal i / 10^n
#[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn new(i: i128, n: u32) -> Result<Self, DecimalOverflowError> {
let shift = (DECIMAL_PART_DIGITS as u32)
.checked_sub(n)
@ -155,39 +156,10 @@ impl Decimal {
self.value > 0
}
/// Creates a `Decimal` from a `Float` without taking care of precision
#[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
/// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
#[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]
pub fn to_bool(self) -> bool {
self.value != 0
pub fn is_identical_with(&self, other: &Self) -> bool {
self == other
}
#[inline]
@ -195,15 +167,9 @@ impl Decimal {
self.value / DECIMAL_PART_POW
}
#[cfg(test)]
pub(super) const fn min_value() -> Self {
Self { value: i128::MIN }
}
pub const MIN: Self = Self { value: i128::MIN };
#[cfg(test)]
pub(super) const fn max_value() -> Self {
Self { value: i128::MAX }
}
pub const MAX: Self = Self { value: i128::MAX };
#[cfg(test)]
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 {
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 {
type Err = ParseDecimalError;
type Err = DecimalParseError;
/// 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]+)
let input = input.as_bytes();
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 {
/// Formats the decimal following its canonical representation.
#[allow(clippy::cast_possible_truncation)]
@ -533,19 +513,66 @@ impl Neg for Decimal {
}
}
impl TryFrom<Decimal> for i64 {
type Error = DecimalOverflowError;
/// An error when parsing a [`Decimal`].
#[derive(Debug, Clone)]
pub struct DecimalParseError {
kind: DecimalParseErrorKind,
}
fn try_from(value: Decimal) -> Result<Self, DecimalOverflowError> {
value
.value
.checked_div(DECIMAL_PART_POW)
.ok_or(DecimalOverflowError)?
.try_into()
.map_err(|_| DecimalOverflowError)
#[derive(Debug, Clone)]
enum DecimalParseErrorKind {
Overflow,
Underflow,
UnexpectedChar,
UnexpectedEnd,
}
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)]
mod tests {
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(".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!(
Decimal::from_str(&Decimal::max_value().to_string()).unwrap(),
Decimal::max_value()
Decimal::from_str(&Decimal::MAX.to_string()).unwrap(),
Decimal::MAX
);
assert_eq!(
Decimal::from_str(
&Decimal::min_value()
&Decimal::MIN
.checked_add(Decimal::step())
.unwrap()
.to_string()
)
.unwrap(),
Decimal::min_value().checked_add(Decimal::step()).unwrap()
Decimal::MIN.checked_add(Decimal::step()).unwrap()
);
}
@ -609,18 +639,18 @@ mod tests {
#[test]
fn add() {
assert!(Decimal::min_value().checked_add(Decimal::step()).is_some());
assert!(Decimal::max_value().checked_add(Decimal::step()).is_none());
assert!(Decimal::MIN.checked_add(Decimal::step()).is_some());
assert!(Decimal::MAX.checked_add(Decimal::step()).is_none());
assert_eq!(
Decimal::max_value().checked_add(Decimal::min_value()),
Decimal::MAX.checked_add(Decimal::MIN),
Some(-Decimal::step())
);
}
#[test]
fn sub() {
assert!(Decimal::min_value().checked_sub(Decimal::step()).is_none());
assert!(Decimal::max_value().checked_sub(Decimal::step()).is_some());
assert!(Decimal::MIN.checked_sub(Decimal::step()).is_none());
assert!(Decimal::MAX.checked_sub(Decimal::step()).is_some());
}
#[test]
@ -715,4 +745,92 @@ mod tests {
assert_eq!(Decimal::from(i64::MIN).floor(), Decimal::from(i64::MIN));
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::fmt;
use std::hash::{Hash, Hasher};
use std::num::ParseFloatError;
use std::ops::{Add, Div, Mul, Neg, Sub};
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
#[derive(Debug, Clone, Copy, Default)]
/// Uses internally a [`f64`].
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[repr(transparent)]
pub struct Double {
value: f64,
@ -52,33 +51,20 @@ impl Double {
self.value.round().into()
}
/// Casts i64 into `Double`
#[inline]
#[allow(clippy::cast_precision_loss)]
pub fn from_i64(value: i64) -> Self {
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
pub fn is_naan(self) -> bool {
self.value.is_nan()
}
/// Casts `Double` into f32 without taking care of loss
#[inline]
#[allow(clippy::cast_possible_truncation)]
pub fn to_f32(self) -> f32 {
self.value as f32
pub fn is_finite(self) -> bool {
self.value.is_finite()
}
/// 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]
pub fn to_bool(self) -> bool {
self.value != 0. && !self.value.is_nan()
pub fn is_identical_with(&self, other: &Self) -> bool {
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 {
type Err = ParseFloatError;
/// Parses decimals lexical mapping
#[inline]
fn from_str(input: &str) -> Result<Self, ParseFloatError> {
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 {
#[inline]
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 {
type Output = Self;
@ -257,13 +240,45 @@ mod tests {
#[test]
fn eq() {
assert_eq!(Double::from(0_f64), Double::from(0_f64));
assert_eq!(Double::from(f64::NAN), Double::from(f64::NAN));
assert_ne!(Double::from(-0.), Double::from(0.));
assert_ne!(Double::from(f64::NAN), Double::from(f64::NAN));
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]
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::NEG_INFINITY), Double::from_str("-INF")?);

@ -7,7 +7,7 @@ use std::ops::Neg;
use std::str::FromStr;
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`.
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash, Default)]
@ -17,6 +17,7 @@ pub struct Duration {
}
impl Duration {
#[inline]
pub fn new(months: impl Into<i64>, seconds: impl Into<Decimal>) -> Self {
Self {
year_month: YearMonthDuration::new(months),
@ -24,6 +25,7 @@ impl Duration {
}
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 24]) -> Self {
Self {
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)
#[inline]
pub fn years(&self) -> i64 {
self.year_month.years()
}
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
#[inline]
pub fn months(&self) -> i64 {
self.year_month.months()
}
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration)
#[inline]
pub fn days(&self) -> i64 {
self.day_time.days()
}
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
#[inline]
pub fn hours(&self) -> i64 {
self.day_time.hours()
}
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
#[inline]
pub fn minutes(&self) -> i64 {
self.day_time.minutes()
}
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
#[inline]
pub fn seconds(&self) -> Decimal {
self.day_time.seconds()
}
#[inline]
pub(super) const fn all_months(&self) -> i64 {
self.year_month.all_months()
}
#[inline]
pub(super) const fn all_seconds(&self) -> Decimal {
self.day_time.all_seconds()
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 24] {
let mut bytes = [0; 24];
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)
#[inline]
pub fn checked_add(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into();
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)
#[inline]
pub fn checked_sub(&self, rhs: impl Into<Self>) -> Option<Self> {
let rhs = rhs.into();
Some(Self {
@ -93,11 +106,18 @@ impl Duration {
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 {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> {
Ok(DayTimeDuration::try_from(value)?.into())
}
@ -113,6 +133,7 @@ impl FromStr for Duration {
impl fmt::Display for Duration {
#[allow(clippy::many_single_char_names)]
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ym = self.year_month.months;
let mut ss = self.day_time.seconds;
@ -174,6 +195,7 @@ impl fmt::Display for Duration {
}
impl PartialOrd for Duration {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let first = DateTime::new(1969, 9, 1, 0, 0, 0.into(), None).ok()?;
let first_result = first
@ -205,6 +227,7 @@ impl PartialOrd for Duration {
impl Neg for Duration {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
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`
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -222,12 +245,14 @@ pub struct YearMonthDuration {
}
impl YearMonthDuration {
#[inline]
pub fn new(months: impl Into<i64>) -> Self {
Self {
months: months.into(),
}
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
Self {
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)
#[inline]
pub fn years(self) -> i64 {
self.months / 12
}
/// [fn:months-from-duration](https://www.w3.org/TR/xpath-functions/#func-months-from-duration)
#[inline]
pub fn months(self) -> i64 {
self.months % 12
}
/// [fn:days-from-duration](https://www.w3.org/TR/xpath-functions/#func-days-from-duration)
#[allow(clippy::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()
}
#[inline]
pub(super) const fn all_months(self) -> i64 {
self.months
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 8] {
self.months.to_be_bytes()
}
/// [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> {
let rhs = rhs.into();
Some(Self {
@ -285,15 +291,23 @@ impl YearMonthDuration {
}
/// [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> {
let rhs = rhs.into();
Some(Self {
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 {
#[inline]
fn from(value: YearMonthDuration) -> Self {
Self {
year_month: value,
@ -305,6 +319,7 @@ impl From<YearMonthDuration> for Duration {
impl TryFrom<Duration> for YearMonthDuration {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> {
if value.day_time == DayTimeDuration::default() {
Ok(value.year_month)
@ -323,6 +338,7 @@ impl FromStr for YearMonthDuration {
}
impl fmt::Display for YearMonthDuration {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.months == 0 {
write!(f, "P0M")
@ -333,24 +349,28 @@ impl fmt::Display for YearMonthDuration {
}
impl PartialEq<Duration> for YearMonthDuration {
#[inline]
fn eq(&self, other: &Duration) -> bool {
Duration::from(*self).eq(other)
}
}
impl PartialEq<YearMonthDuration> for Duration {
#[inline]
fn eq(&self, other: &YearMonthDuration) -> bool {
self.eq(&Self::from(*other))
}
}
impl PartialOrd<Duration> for YearMonthDuration {
#[inline]
fn partial_cmp(&self, other: &Duration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(other)
}
}
impl PartialOrd<YearMonthDuration> for Duration {
#[inline]
fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
@ -359,6 +379,7 @@ impl PartialOrd<YearMonthDuration> for Duration {
impl Neg for YearMonthDuration {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
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`
#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
@ -375,62 +396,59 @@ pub struct DayTimeDuration {
}
impl DayTimeDuration {
#[inline]
pub fn new(seconds: impl Into<Decimal>) -> Self {
Self {
seconds: seconds.into(),
}
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
Self {
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)
#[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn days(&self) -> i64 {
(self.seconds.as_i128() / 86400) as i64
}
/// [fn:hours-from-duration](https://www.w3.org/TR/xpath-functions/#func-hours-from-duration)
#[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn hours(&self) -> i64 {
((self.seconds.as_i128() % 86400) / 3600) as i64
}
/// [fn:minutes-from-duration](https://www.w3.org/TR/xpath-functions/#func-minutes-from-duration)
#[allow(clippy::cast_possible_truncation)]
#[inline]
pub fn minutes(&self) -> i64 {
((self.seconds.as_i128() % 3600) / 60) as i64
}
/// [fn:seconds-from-duration](https://www.w3.org/TR/xpath-functions/#func-seconds-from-duration)
#[inline]
pub fn seconds(&self) -> Decimal {
self.seconds.checked_rem(60).unwrap()
}
#[inline]
pub(super) const fn all_seconds(&self) -> Decimal {
self.seconds
}
#[inline]
pub fn to_be_bytes(self) -> [u8; 16] {
self.seconds.to_be_bytes()
}
/// [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> {
let rhs = rhs.into();
Some(Self {
@ -439,15 +457,23 @@ impl DayTimeDuration {
}
/// [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> {
let rhs = rhs.into();
Some(Self {
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 {
#[inline]
fn from(value: DayTimeDuration) -> Self {
Self {
year_month: YearMonthDuration::default(),
@ -459,6 +485,7 @@ impl From<DayTimeDuration> for Duration {
impl TryFrom<Duration> for DayTimeDuration {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: Duration) -> Result<Self, DecimalOverflowError> {
if value.year_month == YearMonthDuration::default() {
Ok(value.day_time)
@ -471,6 +498,7 @@ impl TryFrom<Duration> for DayTimeDuration {
impl TryFrom<StdDuration> for DayTimeDuration {
type Error = DecimalOverflowError;
#[inline]
fn try_from(value: StdDuration) -> Result<Self, DecimalOverflowError> {
Ok(Self {
seconds: Decimal::new(
@ -490,54 +518,63 @@ impl FromStr for DayTimeDuration {
}
impl fmt::Display for DayTimeDuration {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Duration::from(*self).fmt(f)
}
}
impl PartialEq<Duration> for DayTimeDuration {
#[inline]
fn eq(&self, other: &Duration) -> bool {
Duration::from(*self).eq(other)
}
}
impl PartialEq<DayTimeDuration> for Duration {
#[inline]
fn eq(&self, other: &DayTimeDuration) -> bool {
self.eq(&Self::from(*other))
}
}
impl PartialEq<YearMonthDuration> for DayTimeDuration {
#[inline]
fn eq(&self, other: &YearMonthDuration) -> bool {
Duration::from(*self).eq(&Duration::from(*other))
}
}
impl PartialEq<DayTimeDuration> for YearMonthDuration {
#[inline]
fn eq(&self, other: &DayTimeDuration) -> bool {
Duration::from(*self).eq(&Duration::from(*other))
}
}
impl PartialOrd<Duration> for DayTimeDuration {
#[inline]
fn partial_cmp(&self, other: &Duration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(other)
}
}
impl PartialOrd<DayTimeDuration> for Duration {
#[inline]
fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> {
self.partial_cmp(&Self::from(*other))
}
}
impl PartialOrd<YearMonthDuration> for DayTimeDuration {
#[inline]
fn partial_cmp(&self, other: &YearMonthDuration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(&Duration::from(*other))
}
}
impl PartialOrd<DayTimeDuration> for YearMonthDuration {
#[inline]
fn partial_cmp(&self, other: &DayTimeDuration) -> Option<Ordering> {
Duration::from(*self).partial_cmp(&Duration::from(*other))
}
@ -546,6 +583,7 @@ impl PartialOrd<DayTimeDuration> for YearMonthDuration {
impl Neg for DayTimeDuration {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
seconds: self.seconds.neg(),
@ -561,9 +599,9 @@ mod tests {
fn from_str() {
let min = Duration::new(
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!(
YearMonthDuration::from_str("P1Y").unwrap().to_string(),
@ -701,6 +739,10 @@ mod tests {
Duration::from_str("P0Y0M10D").unwrap(),
Duration::from_str("PT240H").unwrap()
);
assert_ne!(
Duration::from_str("P1M").unwrap(),
Duration::from_str("P30D").unwrap()
);
}
#[test]

@ -1,14 +1,14 @@
use crate::{Boolean, Double, Integer};
use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::num::ParseFloatError;
use std::ops::{Add, Div, Mul, Neg, Sub};
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
#[derive(Debug, Clone, Copy, Default)]
/// Uses internally a [`f32`].
#[derive(Debug, Clone, Copy, Default, PartialEq)]
#[repr(transparent)]
pub struct Float {
value: f32,
@ -51,26 +51,20 @@ impl Float {
self.value.round().into()
}
/// Casts i64 into `Float`
#[inline]
#[allow(clippy::cast_precision_loss)]
pub fn from_i64(value: i64) -> Self {
Self {
value: value as f32,
}
pub fn is_naan(self) -> bool {
self.value.is_nan()
}
/// Casts `Float` into i64 without taking care of loss
#[inline]
#[allow(clippy::cast_possible_truncation)]
pub fn to_i64(self) -> i64 {
self.value as i64
pub fn is_finite(self) -> bool {
self.value.is_finite()
}
/// 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]
pub fn to_bool(self) -> bool {
self.value != 0. && !self.value.is_nan()
pub fn is_identical_with(&self, other: &Self) -> bool {
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 {
type Err = ParseFloatError;
/// Parses decimals lexical mapping
#[inline]
fn from_str(input: &str) -> Result<Self, ParseFloatError> {
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 {
#[inline]
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 {
type Output = Self;
@ -228,14 +228,46 @@ mod tests {
#[test]
fn eq() {
assert_eq!(Float::from(0_f32), Float::from(0_f32));
assert_eq!(Float::from(f32::NAN), Float::from(f32::NAN));
assert_ne!(Float::from(-0.), Float::from(0.));
assert_eq!(Float::from(0.), Float::from(0.));
assert_ne!(Float::from(f32::NAN), Float::from(f32::NAN));
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]
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::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::decimal::ParseDecimalError;
use super::decimal::DecimalParseError;
use super::duration::{DayTimeDuration, YearMonthDuration};
use super::*;
use nom::branch::alt;
@ -29,7 +29,7 @@ enum XsdParseErrorKind {
TooMuchData { count: usize },
Overflow,
ParseInt(ParseIntError),
ParseDecimal(ParseDecimalError),
ParseDecimal(DecimalParseError),
OutOfIntegerRange { value: u8, min: u8, max: u8 },
DateTime(DateTimeError),
}
@ -108,8 +108,8 @@ impl From<ParseIntError> for XsdParseError {
}
}
impl From<ParseDecimalError> for XsdParseError {
fn from(error: ParseDecimalError) -> Self {
impl From<DecimalParseError> for XsdParseError {
fn from(error: DecimalParseError) -> Self {
Self {
kind: XsdParseErrorKind::ParseDecimal(error),
}

@ -131,8 +131,17 @@
)]
pub mod io;
pub mod model;
pub mod sparql;
mod storage;
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::storage::numeric_encoder::*;
use crate::storage::small_string::SmallString;
use crate::xsd::*;
use digest::Digest;
use md5::Md5;
use oxilangtag::LanguageTag;
use oxiri::Iri;
use oxrdf::Variable;
use oxsdatatypes::*;
use rand::random;
use regex::{Regex, RegexBuilder};
use sha1::Sha1;
@ -1203,7 +1203,7 @@ impl SimpleEvaluator {
PlanExpression::Abs(e) => {
let e = self.expression_evaluator(e);
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::FloatLiteral(value) => Some(value.abs().into()),
EncodedTerm::DoubleLiteral(value) => Some(value.abs().into()),
@ -1274,13 +1274,13 @@ impl SimpleEvaluator {
let starting_location: usize =
if let EncodedTerm::IntegerLiteral(v) = starting_loc(tuple)? {
v.try_into().ok()?
i64::from(v).try_into().ok()?
} else {
return None;
};
let length: Option<usize> = if let Some(length) = &length {
if let EncodedTerm::IntegerLiteral(v) = length(tuple)? {
Some(v.try_into().ok()?)
Some(i64::from(v).try_into().ok()?)
} else {
return None;
}
@ -1738,17 +1738,13 @@ impl SimpleEvaluator {
}
PlanExpression::BooleanCast(e) => {
let e = self.expression_evaluator(e);
let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::BooleanLiteral(value) => Some(value.into()),
EncodedTerm::FloatLiteral(value) => Some(value.to_bool().into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_bool().into()),
EncodedTerm::IntegerLiteral(value) => Some((value != 0).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_bool().into()),
EncodedTerm::FloatLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::DoubleLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::IntegerLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(Boolean::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_boolean_str(&value),
EncodedTerm::BigStringLiteral { value_id } => {
parse_boolean_str(&dataset.get_str(&value_id).ok()??)
}
_ => None,
})
}
@ -1756,13 +1752,11 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e);
let dataset = self.dataset.clone();
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::IntegerLiteral(value) => Some((value as f64).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_double().into()),
EncodedTerm::BooleanLiteral(value) => {
Some(if value { 1_f64 } else { 0_f64 }.into())
}
EncodedTerm::IntegerLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::BooleanLiteral(value) => Some(Double::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_double_str(&value),
EncodedTerm::BigStringLiteral { value_id } => {
parse_double_str(&dataset.get_str(&value_id).ok()??)
@ -1775,12 +1769,10 @@ impl SimpleEvaluator {
let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(value.into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_f32().into()),
EncodedTerm::IntegerLiteral(value) => Some((value as f32).into()),
EncodedTerm::DecimalLiteral(value) => Some(value.to_float().into()),
EncodedTerm::BooleanLiteral(value) => {
Some(if value { 1_f32 } else { 0_f32 }.into())
}
EncodedTerm::DoubleLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::IntegerLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::DecimalLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::BooleanLiteral(value) => Some(Float::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_float_str(&value),
EncodedTerm::BigStringLiteral { value_id } => {
parse_float_str(&dataset.get_str(&value_id).ok()??)
@ -1792,11 +1784,15 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e);
let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(value.to_i64().into()),
EncodedTerm::DoubleLiteral(value) => Some(value.to_i64().into()),
EncodedTerm::FloatLiteral(value) => Some(Integer::try_from(value).ok()?.into()),
EncodedTerm::DoubleLiteral(value) => {
Some(Integer::try_from(value).ok()?.into())
}
EncodedTerm::IntegerLiteral(value) => Some(value.into()),
EncodedTerm::DecimalLiteral(value) => Some(i64::try_from(value).ok()?.into()),
EncodedTerm::BooleanLiteral(value) => Some(i64::from(value).into()),
EncodedTerm::DecimalLiteral(value) => {
Some(Integer::try_from(value).ok()?.into())
}
EncodedTerm::BooleanLiteral(value) => Some(Integer::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_integer_str(&value),
EncodedTerm::BigStringLiteral { value_id } => {
parse_integer_str(&dataset.get_str(&value_id).ok()??)
@ -1808,9 +1804,13 @@ impl SimpleEvaluator {
let e = self.expression_evaluator(e);
let dataset = self.dataset.clone();
Rc::new(move |tuple| match e(tuple)? {
EncodedTerm::FloatLiteral(value) => Some(Decimal::from_float(value).into()),
EncodedTerm::DoubleLiteral(value) => Some(Decimal::from_double(value).into()),
EncodedTerm::IntegerLiteral(value) => Some(Decimal::from(value).into()),
EncodedTerm::FloatLiteral(value) => Some(Decimal::try_from(value).ok()?.into()),
EncodedTerm::DoubleLiteral(value) => {
Some(Decimal::try_from(value).ok()?.into())
}
EncodedTerm::IntegerLiteral(value) => {
Some(Decimal::try_from(value).ok()?.into())
}
EncodedTerm::DecimalLiteral(value) => Some(value.into()),
EncodedTerm::BooleanLiteral(value) => Some(Decimal::from(value).into()),
EncodedTerm::SmallStringLiteral(value) => parse_decimal_str(&value),
@ -1944,15 +1944,15 @@ impl SimpleEvaluator {
fn to_bool(term: &EncodedTerm) -> Option<bool> {
match term {
EncodedTerm::BooleanLiteral(value) => Some(*value),
EncodedTerm::BooleanLiteral(value) => Some((*value).into()),
EncodedTerm::SmallStringLiteral(value) => Some(!value.is_empty()),
EncodedTerm::BigStringLiteral { .. } => {
Some(false) // A big literal can't be empty
}
EncodedTerm::FloatLiteral(value) => Some(*value != Float::default()),
EncodedTerm::DoubleLiteral(value) => Some(*value != Double::default()),
EncodedTerm::IntegerLiteral(value) => Some(*value != 0),
EncodedTerm::DecimalLiteral(value) => Some(*value != Decimal::default()),
EncodedTerm::FloatLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::DoubleLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::IntegerLiteral(value) => Some(Boolean::from(*value).into()),
EncodedTerm::DecimalLiteral(value) => Some(Boolean::from(*value).into()),
_ => None,
}
}
@ -1975,7 +1975,7 @@ fn to_string_id(dataset: &DatasetView, term: &EncodedTerm) -> Option<SmallString
| EncodedTerm::BigTypedLiteral { value_id, .. } => Some((*value_id).into()),
EncodedTerm::BooleanLiteral(value) => Some(build_string_id(
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::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(b) => Some(a == b),
EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Float::from_i64(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_float()),
EncodedTerm::IntegerLiteral(b) => Some(*a == Float::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == (*b).try_into().ok()?),
_ if b.is_unknown_typed_literal() => None,
_ => Some(false),
},
EncodedTerm::DoubleLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(*a == Double::from(*b)),
EncodedTerm::DoubleLiteral(b) => Some(a == b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Double::from_i64(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == b.to_double()),
EncodedTerm::IntegerLiteral(b) => Some(*a == Double::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(*a == (*b).try_into().ok()?),
_ if b.is_unknown_typed_literal() => None,
_ => Some(false),
},
EncodedTerm::IntegerLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(Float::from_i64(*a) == *b),
EncodedTerm::DoubleLiteral(b) => Some(Double::from_i64(*a) == *b),
EncodedTerm::FloatLiteral(b) => Some(Float::from(*a) == *b),
EncodedTerm::DoubleLiteral(b) => Some(Double::from(*a) == *b),
EncodedTerm::IntegerLiteral(b) => Some(a == b),
EncodedTerm::DecimalLiteral(b) => Some(Decimal::from(*a) == *b),
_ if b.is_unknown_typed_literal() => None,
_ => Some(false),
},
EncodedTerm::DecimalLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Some(a.to_float() == *b),
EncodedTerm::DoubleLiteral(b) => Some(a.to_double() == *b),
EncodedTerm::FloatLiteral(b) => Some(Float::try_from(*a).ok()? == *b),
EncodedTerm::DoubleLiteral(b) => Some(Double::try_from(*a).ok()? == *b),
EncodedTerm::IntegerLiteral(b) => Some(*a == Decimal::from(*b)),
EncodedTerm::DecimalLiteral(b) => Some(a == b),
_ if b.is_unknown_typed_literal() => None,
@ -2573,27 +2573,27 @@ fn partial_cmp_literals(
EncodedTerm::FloatLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Float::from_i64(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_float()),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Float::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&(*b).try_into().ok()?),
_ => None,
},
EncodedTerm::DoubleLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.partial_cmp(&(*b).into()),
EncodedTerm::DoubleLiteral(b) => a.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Double::from_i64(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&b.to_double()),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Double::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(&(*b).try_into().ok()?),
_ => None,
},
EncodedTerm::IntegerLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => Float::from_i64(*a).partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::from_i64(*a).partial_cmp(b),
EncodedTerm::FloatLiteral(b) => Float::from(*a).partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::from(*a).partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(b),
EncodedTerm::DecimalLiteral(b) => Decimal::from(*a).partial_cmp(b),
_ => None,
},
EncodedTerm::DecimalLiteral(a) => match b {
EncodedTerm::FloatLiteral(b) => a.to_float().partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => a.to_double().partial_cmp(b),
EncodedTerm::FloatLiteral(b) => Float::try_from(*a).ok()?.partial_cmp(b),
EncodedTerm::DoubleLiteral(b) => Double::try_from(*a).ok()?.partial_cmp(b),
EncodedTerm::IntegerLiteral(b) => a.partial_cmp(&Decimal::from(*b)),
EncodedTerm::DecimalLiteral(b) => a.partial_cmp(b),
_ => None,
@ -2736,7 +2736,7 @@ fn datatype(dataset: &DatasetView, value: &EncodedTerm) -> Option<EncodedTerm> {
enum NumericBinaryOperands {
Float(Float, Float),
Double(Double, Double),
Integer(i64, i64),
Integer(Integer, Integer),
Decimal(Decimal, Decimal),
Duration(Duration, Duration),
YearMonthDuration(YearMonthDuration, YearMonthDuration),
@ -2765,10 +2765,10 @@ impl NumericBinaryOperands {
Some(Self::Double(v1.into(), 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)) => {
Some(Self::Float(v1, v2.to_float()))
Some(Self::Float(v1, v2.into()))
}
(EncodedTerm::DoubleLiteral(v1), EncodedTerm::FloatLiteral(v2)) => {
Some(Self::Double(v1, v2.into()))
@ -2777,31 +2777,31 @@ impl NumericBinaryOperands {
Some(Self::Double(v1, 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)) => {
Some(Self::Double(v1, v2.to_double()))
Some(Self::Double(v1, v2.into()))
}
(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)) => {
Some(Self::Double(Double::from_i64(v1), v2))
Some(Self::Double(v1.into(), v2))
}
(EncodedTerm::IntegerLiteral(v1), EncodedTerm::IntegerLiteral(v2)) => {
Some(Self::Integer(v1, 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)) => {
Some(Self::Float(v1.to_float(), v2))
Some(Self::Float(v1.into(), 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)) => {
Some(Self::Decimal(v1, Decimal::from(v2)))
Some(Self::Decimal(v1, v2.into()))
}
(EncodedTerm::DecimalLiteral(v1), EncodedTerm::DecimalLiteral(v2)) => {
Some(Self::Decimal(v1, v2))
@ -2810,13 +2810,13 @@ impl NumericBinaryOperands {
Some(Self::Duration(v1, 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)) => {
Some(Self::Duration(v1, (v2).into()))
Some(Self::Duration(v1, v2.into()))
}
(EncodedTerm::YearMonthDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(Self::Duration((v1).into(), v2))
Some(Self::Duration(v1.into(), v2))
}
(
EncodedTerm::YearMonthDurationLiteral(v1),
@ -2825,14 +2825,14 @@ impl NumericBinaryOperands {
(
EncodedTerm::YearMonthDurationLiteral(v1),
EncodedTerm::DayTimeDurationLiteral(v2),
) => Some(Self::Duration((v1).into(), (v2).into())),
) => Some(Self::Duration(v1.into(), v2.into())),
(EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DurationLiteral(v2)) => {
Some(Self::Duration((v1).into(), v2))
Some(Self::Duration(v1.into(), v2))
}
(
EncodedTerm::DayTimeDurationLiteral(v1),
EncodedTerm::YearMonthDurationLiteral(v2),
) => Some(Self::Duration((v1).into(), (v2).into())),
) => Some(Self::Duration(v1.into(), v2.into())),
(EncodedTerm::DayTimeDurationLiteral(v1), EncodedTerm::DayTimeDurationLiteral(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::StorageError;
use crate::store::CorruptionError;
use crate::xsd::*;
use oxsdatatypes::*;
use std::io::{Cursor, Read};
use std::mem::size_of;
use std::rc::Rc;
#[cfg(not(target_arch = "wasm32"))]
pub const LATEST_STORAGE_VERSION: u64 = 1;
@ -314,100 +313,89 @@ impl<R: Read> TermReader for R {
value_id: StrHash::from_be_bytes(buffer),
})
}
TYPE_BOOLEAN_LITERAL_TRUE => Ok(EncodedTerm::BooleanLiteral(true)),
TYPE_BOOLEAN_LITERAL_FALSE => Ok(EncodedTerm::BooleanLiteral(false)),
TYPE_BOOLEAN_LITERAL_TRUE => Ok(true.into()),
TYPE_BOOLEAN_LITERAL_FALSE => Ok(false.into()),
TYPE_FLOAT_LITERAL => {
let mut buffer = [0; 4];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::FloatLiteral(Float::from_be_bytes(buffer)))
Ok(Float::from_be_bytes(buffer).into())
}
TYPE_DOUBLE_LITERAL => {
let mut buffer = [0; 8];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DoubleLiteral(Double::from_be_bytes(buffer)))
Ok(Double::from_be_bytes(buffer).into())
}
TYPE_INTEGER_LITERAL => {
let mut buffer = [0; 8];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::IntegerLiteral(i64::from_be_bytes(buffer)))
Ok(Integer::from_be_bytes(buffer).into())
}
TYPE_DECIMAL_LITERAL => {
let mut buffer = [0; 16];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DecimalLiteral(Decimal::from_be_bytes(buffer)))
Ok(Decimal::from_be_bytes(buffer).into())
}
TYPE_DATE_TIME_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DateTimeLiteral(DateTime::from_be_bytes(
buffer,
)))
Ok(DateTime::from_be_bytes(buffer).into())
}
TYPE_TIME_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::TimeLiteral(Time::from_be_bytes(buffer)))
Ok(Time::from_be_bytes(buffer).into())
}
TYPE_DATE_LITERAL => {
let mut buffer = [0; 18];
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 => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GYearMonthLiteral(GYearMonth::from_be_bytes(
buffer,
)))
Ok(GYearMonth::from_be_bytes(buffer).into())
}
TYPE_G_YEAR_LITERAL => {
let mut buffer = [0; 18];
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 => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GMonthDayLiteral(GMonthDay::from_be_bytes(
buffer,
)))
Ok(GMonthDay::from_be_bytes(buffer).into())
}
TYPE_G_DAY_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GDayLiteral(GDay::from_be_bytes(buffer)))
Ok(GDay::from_be_bytes(buffer).into())
}
TYPE_G_MONTH_LITERAL => {
let mut buffer = [0; 18];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::GMonthLiteral(GMonth::from_be_bytes(buffer)))
Ok(GMonth::from_be_bytes(buffer).into())
}
TYPE_DURATION_LITERAL => {
let mut buffer = [0; 24];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DurationLiteral(Duration::from_be_bytes(
buffer,
)))
Ok(Duration::from_be_bytes(buffer).into())
}
TYPE_YEAR_MONTH_DURATION_LITERAL => {
let mut buffer = [0; 8];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::YearMonthDurationLiteral(
YearMonthDuration::from_be_bytes(buffer),
))
Ok(YearMonthDuration::from_be_bytes(buffer).into())
}
TYPE_DAY_TIME_DURATION_LITERAL => {
let mut buffer = [0; 16];
self.read_exact(&mut buffer)?;
Ok(EncodedTerm::DayTimeDurationLiteral(
DayTimeDuration::from_be_bytes(buffer),
))
Ok(DayTimeDuration::from_be_bytes(buffer).into())
}
TYPE_TRIPLE => Ok(EncodedTerm::Triple(Rc::new(EncodedTriple {
TYPE_TRIPLE => Ok(EncodedTriple {
subject: self.read_term()?,
predicate: self.read_term()?,
object: self.read_term()?,
}))),
}
.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(&value_id.to_be_bytes());
}
EncodedTerm::BooleanLiteral(true) => sink.push(TYPE_BOOLEAN_LITERAL_TRUE),
EncodedTerm::BooleanLiteral(false) => sink.push(TYPE_BOOLEAN_LITERAL_FALSE),
EncodedTerm::BooleanLiteral(value) => sink.push(if bool::from(*value) {
TYPE_BOOLEAN_LITERAL_TRUE
} else {
TYPE_BOOLEAN_LITERAL_FALSE
}),
EncodedTerm::FloatLiteral(value) => {
sink.push(TYPE_FLOAT_LITERAL);
sink.extend_from_slice(&value.to_be_bytes())

@ -3,7 +3,7 @@
use crate::model::*;
use crate::storage::small_string::SmallString;
use crate::store::{CorruptionError, StorageError};
use crate::xsd::*;
use oxsdatatypes::*;
use siphasher::sip128::{Hasher128, SipHasher24};
use std::fmt::Debug;
use std::hash::Hash;
@ -80,10 +80,10 @@ pub enum EncodedTerm {
value_id: StrHash,
datatype_id: StrHash,
},
BooleanLiteral(bool),
BooleanLiteral(Boolean),
FloatLiteral(Float),
DoubleLiteral(Double),
IntegerLiteral(i64),
IntegerLiteral(Integer),
DecimalLiteral(Decimal),
DateTimeLiteral(DateTime),
TimeLiteral(Time),
@ -183,10 +183,10 @@ impl PartialEq for EncodedTerm {
},
) => value_id_a == value_id_b && datatype_id_a == datatype_id_b,
(Self::BooleanLiteral(a), Self::BooleanLiteral(b)) => a == b,
(Self::FloatLiteral(a), Self::FloatLiteral(b)) => a == b,
(Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => a == b,
(Self::IntegerLiteral(a), Self::IntegerLiteral(b)) => a == b,
(Self::DecimalLiteral(a), Self::DecimalLiteral(b)) => a == b,
(Self::FloatLiteral(a), Self::FloatLiteral(b)) => a.is_identical_with(b),
(Self::DoubleLiteral(a), Self::DoubleLiteral(b)) => a.is_identical_with(b),
(Self::IntegerLiteral(a), Self::IntegerLiteral(b)) => a.is_identical_with(b),
(Self::DecimalLiteral(a), Self::DecimalLiteral(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::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::GMonthLiteral(a), Self::GMonthLiteral(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::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(b)) => a == b,
(Self::DayTimeDurationLiteral(a), Self::DayTimeDurationLiteral(b)) => a == b,
(Self::DurationLiteral(a), Self::DurationLiteral(b)) => a.is_identical_with(b),
(Self::YearMonthDurationLiteral(a), Self::YearMonthDurationLiteral(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,
(_, _) => false,
}
@ -247,8 +251,8 @@ impl Hash for EncodedTerm {
datatype_id.hash(state);
}
Self::BooleanLiteral(value) => value.hash(state),
Self::FloatLiteral(value) => value.hash(state),
Self::DoubleLiteral(value) => value.hash(state),
Self::FloatLiteral(value) => value.to_be_bytes().hash(state),
Self::DoubleLiteral(value) => value.to_be_bytes().hash(state),
Self::IntegerLiteral(value) => value.hash(state),
Self::DecimalLiteral(value) => value.hash(state),
Self::DateTimeLiteral(value) => value.hash(state),
@ -329,13 +333,13 @@ impl EncodedTerm {
impl From<bool> for EncodedTerm {
fn from(value: bool) -> Self {
Self::BooleanLiteral(value)
Self::BooleanLiteral(value.into())
}
}
impl From<i64> for EncodedTerm {
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 {
fn from(value: Double) -> Self {
Self::DoubleLiteral(value)
}
}
impl From<Integer> for EncodedTerm {
fn from(value: Integer) -> Self {
Self::IntegerLiteral(value)
}
}
impl From<Decimal> for EncodedTerm {
fn from(value: Decimal) -> Self {
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 {
fn from(value: Duration) -> Self {
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> {
match value {
"true" | "1" => Some(EncodedTerm::BooleanLiteral(true)),
"false" | "0" => Some(EncodedTerm::BooleanLiteral(false)),
_ => None,
}
value.parse().map(EncodedTerm::BooleanLiteral).ok()
}
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