Add english variant of decimal parser

korrat
Jun 6, 2024, 6:40 AM
RQIRG5BD5P2AVZMOBENQPDOIYMUYIDKW5E25LLMJZ27WCOLBYU7AC

Dependencies

  • [2] AQ6A6PSU Hide unnecessary public method
  • [3] CFT26V62 Improve proptest for german-decimal
  • [4] 5XUAFMCK Enforce decimal precision for ebase importer
  • [5] J7MCZEIR Fix clippy warnings in generators
  • [6] DWUVE75B Handle foreign currency purchases in DKB importer
  • [7] BDLVPDJZ Add a importer account for BW bank portfolios
  • [8] D6LJRTWX Add importers for ebase accounts
  • [9] OAOH3QOZ Extract metadata into separate crate
  • [10] MDRERLJH Upgrade dependencies
  • [11] 362NCCMX Add importer for Apple Store
  • [12] XQHYMSDY Add importer for Union Investment transactions
  • [13] A35XMWPS Restructure workspace
  • [14] 6A5YLGWV Add an importer for the VR-Bank CSV format
  • [15] U5SMZFJX Update dependencies
  • [16] E2DDQ4MX Define inheritable lints configuration
  • [17] PCHAKXNM Add an importer for Fidor account statements
  • [18] D6UTHZA4 add a simple writer for saving a set of directives to a tree of files
  • [19] RCS5VP3A Add an importer for PayPal account statements
  • [20] YDK6X6PP add a library of important types for beancount
  • [21] YNK6C2RE Add importer for CAMT exports by BW Bank
  • [22] I2P2FTLE add basic parser for german decimals
  • [23] GUNQ3BZU Add alias for special type of credit
  • [24] 4WYI5U7Y Upgrade dependencies
  • [25] A46ZUUSW Record effective dates for card transactions in DKB importer
  • [26] PWHXDFPW Rename importer
  • [27] QRIJE4AQ add a simple pretty printer for beancount directives
  • [28] 7URE4HPL Add an importer for DKB account statements
  • [29] 2JBFREZG enable additional warnings
  • [30] 7VFBUJ6P add basic property test for decimal parser
  • [31] PUM5OY34 Remove unused dependencies
  • [32] UO34MAAG Refactor CSV-based Importers
  • [*] UESS5YZE migrate dependencies into workspace manifest
  • [*] ILUKKAPN Implement Arbitrary for core data types behind a feature flag

Change contents

  • replacement in importers/vr-bank/src/lib.rs at line 254
    [6.4927][6.4927:4991]()
    #[serde(rename = "Betrag", with = "german_decimal::serde")]
    [6.4927]
    [6.4991]
    #[serde(rename = "Betrag", with = "decimal_parsers::german::serde")]
  • replacement in importers/vr-bank/src/lib.rs at line 257
    [6.5013][6.5013:5089]()
    #[serde(rename = "Saldo nach Buchung", with = "german_decimal::serde")]
    [6.5013]
    [6.5089]
    #[serde(rename = "Saldo nach Buchung", with = "decimal_parsers::german::serde")]
  • replacement in importers/vr-bank/Cargo.toml at line 15
    [6.577][6.577:649]()
    german-decimal.path = "../../common/german-decimal"
    [6.577]
    [6.9892]
    decimal-parsers.workspace = true
  • replacement in importers/uniondepot/src/lib.rs at line 352
    [6.6588][6.6588:6656]()
    #[serde(rename = "FONDSPREIS", with = "german_decimal::serde")]
    [6.6588]
    [6.6656]
    #[serde(rename = "FONDSPREIS", with = "decimal_parsers::german::serde")]
  • replacement in importers/uniondepot/src/lib.rs at line 355
    [6.6681][6.6681:6746]()
    #[serde(rename = "ANTEILE", with = "german_decimal::serde")]
    [6.6681]
    [6.6746]
    #[serde(rename = "ANTEILE", with = "decimal_parsers::german::serde")]
  • replacement in importers/uniondepot/src/lib.rs at line 358
    [6.6768][6.6768:6833]()
    #[serde(rename = "VOLUMEN", with = "german_decimal::serde")]
    [6.6768]
    [6.6833]
    #[serde(rename = "VOLUMEN", with = "decimal_parsers::german::serde")]
  • replacement in importers/uniondepot/Cargo.toml at line 15
    [6.1233][6.1233:1305]()
    german-decimal.path = "../../common/german-decimal"
    [6.1233]
    [6.14299]
    decimal-parsers.workspace = true
  • replacement in importers/paypal/src/lib.rs at line 375
    [6.9777][6.9777:9841]()
    #[serde(rename = "Brutto", with = "german_decimal::serde")]
    [6.9777]
    [6.9841]
    #[serde(rename = "Brutto", with = "decimal_parsers::german::serde")]
  • replacement in importers/paypal/src/lib.rs at line 378
    [6.9862][6.9862:9928]()
    #[serde(rename = "Guthaben", with = "german_decimal::serde")]
    [6.9862]
    [6.9928]
    #[serde(rename = "Guthaben", with = "decimal_parsers::german::serde")]
  • replacement in importers/paypal/Cargo.toml at line 15
    [6.1812][6.1812:1884]()
    german-decimal.path = "../../common/german-decimal"
    [6.1812]
    [6.15186]
    decimal-parsers.workspace = true
  • replacement in importers/fidor/src/lib.rs at line 377
    [6.11784][6.11784:11846]()
    #[serde(rename = "Wert", with = "german_decimal::serde")]
    [6.11784]
    [6.11846]
    #[serde(rename = "Wert", with = "decimal_parsers::german::serde")]
  • replacement in importers/fidor/Cargo.toml at line 15
    [6.2456][6.2456:2528]()
    german-decimal.path = "../../common/german-decimal"
    [6.2456]
    [6.15447]
    decimal-parsers.workspace = true
  • replacement in importers/ebase/src/transactions.rs at line 571
    [4.55][4.55:120]()
    with = "german_decimal::serde::opt::with_precision::<6>"
    [4.55]
    [4.120]
    with = "decimal_parsers::german::serde::opt::with_precision::<6>"
  • replacement in importers/ebase/src/transactions.rs at line 577
    [6.16284][6.16284:16344]()
    with = "german_decimal::serde::with_precision::<2>"
    [6.16284]
    [6.16344]
    with = "decimal_parsers::german::serde::with_precision::<2>"
  • replacement in importers/ebase/src/transactions.rs at line 589
    [6.16554][6.16554:16614]()
    with = "german_decimal::serde::with_precision::<2>"
    [6.16554]
    [6.16614]
    with = "decimal_parsers::german::serde::with_precision::<2>"
  • replacement in importers/ebase/src/transactions.rs at line 598
    [6.16760][6.16760:16820]()
    with = "german_decimal::serde::with_precision::<2>"
    [6.16760]
    [6.16820]
    with = "decimal_parsers::german::serde::with_precision::<2>"
  • replacement in importers/ebase/src/transactions.rs at line 613
    [4.183][4.183:243]()
    with = "german_decimal::serde::with_precision::<6>"
    [4.183]
    [4.243]
    with = "decimal_parsers::german::serde::with_precision::<6>"
  • replacement in importers/ebase/src/transactions.rs at line 619
    [4.292][4.292:352]()
    with = "german_decimal::serde::with_precision::<6>"
    [4.292]
    [4.352]
    with = "decimal_parsers::german::serde::with_precision::<6>"
  • replacement in importers/ebase/src/balances.rs at line 237
    [6.20380][6.20380:20445]()
    #[serde(rename = "Anteile", with = "german_decimal::serde")]
    [6.20380]
    [6.20445]
    #[serde(rename = "Anteile", with = "decimal_parsers::german::serde")]
  • replacement in importers/ebase/src/balances.rs at line 240
    [6.20467][6.20467:20536]()
    #[serde(rename = "Anteilswert", with = "german_decimal::serde")]
    [6.20467]
    [6.20536]
    #[serde(rename = "Anteilswert", with = "decimal_parsers::german::serde")]
  • replacement in importers/ebase/Cargo.toml at line 15
    [6.3578][6.3578:3650]()
    german-decimal.path = "../../common/german-decimal"
    [6.3578]
    [6.33920]
    decimal-parsers.workspace = true
  • replacement in importers/dkb/src/lib.rs at line 544
    [6.11251][6.11251:11326]()
    #[serde(rename = "Betrag (EUR)", with = "german_decimal::serde::opt")]
    [6.11251]
    [6.11326]
    #[serde(rename = "Betrag (EUR)", with = "decimal_parsers::german::serde::opt")]
  • replacement in importers/dkb/src/lib.rs at line 717
    [6.15510][6.15510:15580]()
    let amount = german_decimal::parse(amount).map_err(|_| todo!())?;
    [6.15510]
    [6.15580]
    let amount = decimal_parsers::german::parse(amount).map_err(|_| todo!())?;
  • replacement in importers/dkb/Cargo.toml at line 14
    [6.4035][6.4035:4107]()
    german-decimal.path = "../../common/german-decimal"
    [6.4035]
    [6.16109]
    decimal-parsers.workspace = true
  • replacement in importers/bw-bank/src/portfolio.rs at line 247
    [6.26368][6.26368:26432]()
    #[serde(rename = "Stück", with = "german_decimal::serde")]
    [6.26368]
    [6.26432]
    #[serde(rename = "Stück", with = "decimal_parsers::german::serde")]
  • replacement in importers/bw-bank/src/portfolio.rs at line 366
    [6.8588][6.28137:28213]()
    let amount = german_decimal::parse(amount).map_err(E::custom)?;
    [6.8588]
    [6.28213]
    let amount = decimal_parsers::german::parse(amount).map_err(E::custom)?;
  • edit in importers/bw-bank/Cargo.toml at line 15
    [6.4827]
    [6.68288]
    decimal-parsers.workspace = true
  • edit in importers/bw-bank/Cargo.toml at line 17
    [6.68289][6.68289:68342]()
    german-decimal.path = "../../common/german-decimal"
  • replacement in importers/apple/src/transaction_history.rs at line 358
    [6.8427][6.31525:31581]()
    german_decimal::parse(v).map_err(E::custom)
    [6.8427]
    [6.31581]
    decimal_parsers::german::parse(v).map_err(E::custom)
  • replacement in importers/apple/Cargo.toml at line 15
    [6.5499][6.5499:5571]()
    german-decimal.path = "../../common/german-decimal"
    [6.5499]
    [6.18605]
    decimal-parsers.workspace = true
  • file move: german-decimal (d--r------)decimal-parsers (d--r------)
    [6.18]
    [6.46]
  • replacement in common/decimal-parsers/tests/properties/main.rs at line 6
    [6.133][3.12:124](),[3.124][6.243:297](),[6.243][6.243:297]()
    fn can_parse_arbitrary_localized_decimal(decimal in r"(-|\+)?[0-9]{1,3}(\.[0-9]{3}){0,3}(,[0-9]{1,10})?") {
    let result = german_decimal::parse(&decimal);
    [6.133]
    [6.297]
    fn can_parse_arbitrary_german_decimal(decimal in r"(-|\+)?[0-9]{1,3}(\.[0-9]{3}){0,3}(,[0-9]{1,10})?") {
    let result = decimal_parsers::german::parse(&decimal);
    if let Err(message) = result {
    eprintln!("{message}");
    prop_assert!(false);
    }
    }
    #[test]
    fn can_parse_arbitrary_english_decimal(decimal in r"(-|\+)?[0-9]{1,3}(,[0-9]{3}){0,3}(\.[0-9]{1,10})?") {
    let result = decimal_parsers::english::parse(&decimal);
  • edit in common/decimal-parsers/tests/properties/main.rs at line 23
    [6.423]
    // TODO more elaborate testing using decimal formatter
  • replacement in common/decimal-parsers/tests/basic.rs at line 1
    [6.100][6.101:131]()
    use rust_decimal_macros::dec;
    [6.100]
    [6.131]
    mod german {
    use decimal_parsers::german as german_decimal;
    use rust_decimal_macros::dec;
  • replacement in common/decimal-parsers/tests/basic.rs at line 5
    [6.132][6.132:226]()
    #[test]
    fn accepts_integer() {
    assert_eq!(german_decimal::parse("1").unwrap(), dec!(1))
    }
    [6.132]
    [6.226]
    #[test]
    fn accepts_integer() {
    assert_eq!(german_decimal::parse("1").unwrap(), dec!(1));
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 10
    [6.227][6.227:351]()
    #[test]
    fn accepts_decimals() {
    let parsed = german_decimal::parse("0,3").unwrap();
    assert_eq!(parsed, dec!(0.3))
    }
    [6.227]
    [6.351]
    #[test]
    fn accepts_decimals() {
    let parsed = german_decimal::parse("0,3").unwrap();
    assert_eq!(parsed, dec!(0.3));
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 16
    [6.352][6.352:487]()
    #[test]
    fn accepts_group_seperators() {
    let parsed = german_decimal::parse("2.500").unwrap();
    assert_eq!(parsed, dec!(2500))
    }
    [6.352]
    [6.487]
    #[test]
    fn accepts_group_seperators() {
    let parsed = german_decimal::parse("2.500").unwrap();
    assert_eq!(parsed, dec!(2500));
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 22
    [6.488][6.488:648]()
    #[test]
    fn accepts_group_seperators_and_decimals() {
    let parsed = german_decimal::parse("2.500,12345").unwrap();
    assert_eq!(parsed, dec!(2500.12345))
    }
    [6.488]
    [6.648]
    #[test]
    fn accepts_group_seperators_and_decimals() {
    let parsed = german_decimal::parse("2.500,12345").unwrap();
    assert_eq!(parsed, dec!(2500.12345));
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 28
    [6.649][6.649:790]()
    #[test]
    fn accepts_minus_sign() {
    assert_eq!(
    german_decimal::parse("-2.500,12345").unwrap(),
    dec!(-2500.12345)
    );
    }
    [6.649]
    [6.790]
    #[test]
    fn accepts_minus_sign() {
    assert_eq!(
    german_decimal::parse("-2.500,12345").unwrap(),
    dec!(-2500.12345)
    );
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 36
    [6.791][6.791:930]()
    #[test]
    fn accepts_plus_sign() {
    assert_eq!(
    german_decimal::parse("+2.500,12345").unwrap(),
    dec!(2500.12345)
    );
    }
    [6.791]
    [6.930]
    #[test]
    fn accepts_plus_sign() {
    assert_eq!(
    german_decimal::parse("+2.500,12345").unwrap(),
    dec!(2500.12345)
    );
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 44
    [6.931][6.931:1014]()
    #[test]
    fn rejects_empty_numbers() {
    german_decimal::parse("").unwrap_err();
    }
    [6.931]
    [6.1014]
    #[test]
    fn rejects_empty_numbers() {
    german_decimal::parse("").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 49
    [6.1015][6.1015:1112]()
    #[test]
    fn rejects_leading_separator() {
    german_decimal::parse(".500,12345").unwrap_err();
    }
    [6.1015]
    [6.1112]
    #[test]
    fn rejects_leading_separator() {
    german_decimal::parse(".500,12345").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 54
    [6.1113][6.1113:1206]()
    #[test]
    fn rejects_only_decimal_separator() {
    german_decimal::parse(",").unwrap_err();
    }
    [6.1113]
    [6.1206]
    #[test]
    fn rejects_only_decimal_separator() {
    german_decimal::parse(",").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 59
    [6.1207][6.1207:1292]()
    #[test]
    fn rejects_only_plus_sign() {
    german_decimal::parse("+").unwrap_err();
    }
    [6.1207]
    [6.1292]
    #[test]
    fn rejects_only_plus_sign() {
    german_decimal::parse("+").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 64
    [6.1293][6.1293:1379]()
    #[test]
    fn rejects_only_minus_sign() {
    german_decimal::parse("-").unwrap_err();
    }
    [6.1293]
    [6.1379]
    #[test]
    fn rejects_only_minus_sign() {
    german_decimal::parse("-").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 69
    [6.1380][6.1380:1475]()
    #[test]
    fn rejects_interior_minus_sign() {
    german_decimal::parse("12-345").unwrap_err();
    }
    [6.1380]
    [6.1475]
    #[test]
    fn rejects_interior_minus_sign() {
    german_decimal::parse("12-345").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 74
    [6.1476][6.1476:1570]()
    #[test]
    fn rejects_interior_plus_sign() {
    german_decimal::parse("12+345").unwrap_err();
    }
    [6.1476]
    [6.1570]
    #[test]
    fn rejects_interior_plus_sign() {
    german_decimal::parse("12+345").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 79
    [6.1571][6.1571:1649]()
    #[test]
    fn rejects_letters() {
    german_decimal::parse("a").unwrap_err();
    }
    [6.1571]
    [6.1649]
    #[test]
    fn rejects_letters() {
    german_decimal::parse("a").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 84
    [6.1650][6.1650:1728]()
    #[test]
    fn rejects_symbols() {
    german_decimal::parse("_").unwrap_err();
    }
    [6.1650]
    [6.1728]
    #[test]
    fn rejects_symbols() {
    german_decimal::parse("_").unwrap_err();
    }
  • replacement in common/decimal-parsers/tests/basic.rs at line 89
    [6.1729][6.1729:1838]()
    #[test]
    fn rejects_group_separators_in_decimals() {
    german_decimal::parse("2.500,123.456").unwrap_err();
    [6.1729]
    [6.1838]
    #[test]
    fn rejects_group_separators_in_decimals() {
    german_decimal::parse("2.500,123.456").unwrap_err();
    }
  • file deletion: serde.rs (---r------)
    [6.1856][6.3878:3910](),[6.3910][6.1:1]()
    use rust_decimal::Decimal;
    pub mod opt {
    use rust_decimal::Decimal;
    #[allow(non_camel_case_types)]
    pub struct with_precision<const PRECISION: u32>;
    impl<const PRECISION: u32> with_precision<PRECISION> {
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor<const PRECISION: u32>;
    impl<'de, const PRECISION: u32> serde::de::Visitor<'de> for Visitor<PRECISION> {
    type Value = Option<Decimal>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("an optional decimal number in german format")
    }
    fn visit_none<E>(self) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    Ok(None)
    }
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    super::with_precision::<PRECISION>::deserialize(deserializer).map(Some)
    }
    }
    deserializer.deserialize_option(Visitor::<PRECISION>)
    }
    }
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor;
    impl<'de> serde::de::Visitor<'de> for Visitor {
    type Value = Option<Decimal>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("an optional decimal number in german format")
    }
    fn visit_none<E>(self) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    Ok(None)
    }
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    super::deserialize(deserializer).map(Some)
    }
    }
    deserializer.deserialize_option(Visitor)
    }
    }
    #[allow(non_camel_case_types)]
    pub struct with_precision<const PRECISION: u32>;
    impl<const PRECISION: u32> with_precision<PRECISION> {
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor<const PRECISION: u32>;
    impl<'de, const PRECISION: u32> serde::de::Visitor<'de> for Visitor<PRECISION> {
    type Value = Decimal;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("a decimal number in german format")
    }
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    let mut amount = crate::parse(v).map_err(E::custom)?;
    amount.rescale(PRECISION);
    Ok(amount)
    }
    }
    deserializer.deserialize_str(Visitor::<PRECISION>)
    }
    }
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor;
    impl<'de> serde::de::Visitor<'de> for Visitor {
    type Value = Decimal;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("a decimal number in german format")
    }
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    crate::parse(v).map_err(E::custom)
    }
    }
    deserializer.deserialize_str(Visitor)
    }
  • file move: lib.rs (---r------)lib.rs (----------)
    [6.1856]
    [6.1889]
  • replacement in common/decimal-parsers/src/lib.rs at line 1
    [6.1889][6.2814:2918]()
    //! This is based on a copy of [`Decimal::from_str_exact`], with adaptations to handle german decimals.
    [6.1889]
    [6.1988]
    // TODO consider splitting this up
    macro_rules! make_module {
    ($name:ident, $decimal_separator:literal, $group_separator:literal) => {
    pub mod $name {
    #![doc = concat!(
    "A parser for [`Decimals`](Decimal) formatted with ",
    stringify!($name),
    " locale-specific grouping (`",
    stringify!($group_separator),
    "`) and decimal separators (`",
    stringify!($decimal_separator),
    "`)",
    )]
    use rust_decimal::Decimal;
    use rust_decimal::Error;
    const DECIMAL_SEPARATOR: u8 = $decimal_separator;
    const GROUP_SEPARATOR: u8 = $group_separator;
    // Determines potential overflow for 128 bit operations
    const OVERFLOW_U96: u128 = 1u128 << 96;
    const WILL_OVERFLOW_U64: u64 = u64::MAX / 10 - u8::MAX as u64;
    const BYTES_TO_OVERFLOW_U64: usize = 18; // We can probably get away with less
    pub mod serde {
    //! This module contains a deserialize function to be used with [`serde`]'s `with` attribute to enable
    //! locale-specific deserialization of [`Decimal`].
    use rust_decimal::Decimal;
    pub mod opt {
    //! This module contains a deserialize function to be used with [`serde`]'s `with` attribute to
    //! enable locale-specific deserialization of [`Option<Decimal>`].
    use rust_decimal::Decimal;
    #[allow(non_camel_case_types)]
    /// An adapter to enforce a given precision while deserializing a locale-specific
    /// [`Option<Decimal>`].
    pub struct with_precision<const PRECISION: u32>;
    impl<const PRECISION: u32> with_precision<PRECISION> {
    /// Deserializes an [`Option<Decimal>`] in the locale-specific format and enforces a given
    /// precision.
    ///
    /// The precision is enforced using [`Decimal::rescale`].
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails or the deserialized value cannot be parsed as a
    /// `Decimal` according to
    #[doc = concat!("[`crate::", stringify!($name), "::parse`].")]
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor<const PRECISION: u32>;
    impl<'de, const PRECISION: u32> serde::de::Visitor<'de> for Visitor<PRECISION> {
    type Value = Option<Decimal>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("an optional decimal number in german format")
    }
    fn visit_none<E>(self) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    Ok(None)
    }
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    super::with_precision::<PRECISION>::deserialize(deserializer).map(Some)
    }
    }
    deserializer.deserialize_option(Visitor::<PRECISION>)
    }
    }
    /// Deserializes an [`Option<Decimal>`] in the locale-specific format.
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails or the deserialized value cannot be parsed as a
    /// `Decimal` according to
    #[doc = concat!("[`crate::", stringify!($name), "::parse`].")]
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Decimal>, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor;
  • replacement in common/decimal-parsers/src/lib.rs at line 98
    [6.1989][6.1989:2041]()
    use rust_decimal::Decimal;
    use rust_decimal::Error;
    [6.1989]
    [6.37409]
    impl<'de> serde::de::Visitor<'de> for Visitor {
    type Value = Option<Decimal>;
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("an optional decimal number in german format")
    }
    fn visit_none<E>(self) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    Ok(None)
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 112
    [6.37410][6.37410:37425]()
    pub mod serde;
    [6.37410]
    [6.2041]
    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    super::deserialize(deserializer).map(Some)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 120
    [6.2042][6.2042:2280]()
    // Determines potential overflow for 128 bit operations
    const OVERFLOW_U96: u128 = 1u128 << 96;
    const WILL_OVERFLOW_U64: u64 = u64::MAX / 10 - u8::MAX as u64;
    const BYTES_TO_OVERFLOW_U64: usize = 18; // We can probably get away with less
    [6.2042]
    [6.2280]
    deserializer.deserialize_option(Visitor)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 124
    [6.2281][6.2281:2548]()
    #[inline]
    pub fn parse(value: &str) -> Result<Decimal, Error> {
    let bytes = value.as_bytes();
    if bytes.len() < BYTES_TO_OVERFLOW_U64 {
    parse_str_radix_10_dispatch::<false>(bytes)
    } else {
    parse_str_radix_10_dispatch::<true>(bytes)
    }
    }
    [6.2281]
    [6.2548]
    #[allow(non_camel_case_types)]
    /// An adapter to enforce a given precision while deserializing a locale-specific [`Decimal`].
    pub struct with_precision<const PRECISION: u32>;
  • replacement in common/decimal-parsers/src/lib.rs at line 128
    [6.2549][6.2549:2822]()
    #[inline]
    fn parse_str_radix_10_dispatch<const BIG: bool>(bytes: &[u8]) -> Result<Decimal, Error> {
    match bytes {
    [b, rest @ ..] => byte_dispatch_u64::<false, false, false, BIG, true>(rest, 0, 0, *b),
    [] => tail_error("invalid decimal: empty"),
    }
    }
    [6.2549]
    [6.2822]
    impl<const PRECISION: u32> with_precision<PRECISION> {
    /// Deserializes a [`Decimal`] in the locale-specific format and enforces a given precision.
    ///
    /// The precision is enforced using [`Decimal::rescale`].
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails or the deserialized value cannot be parsed as a
    /// `Decimal` according to
    #[doc = concat!("[`crate::", stringify!($name), "::parse`].")]
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor<const PRECISION: u32>;
  • replacement in common/decimal-parsers/src/lib.rs at line 144
    [6.2823][6.2823:2899]()
    #[inline]
    fn overflow_64(val: u64) -> bool {
    val >= WILL_OVERFLOW_U64
    }
    [6.2823]
    [6.2899]
    impl<'de, const PRECISION: u32> serde::de::Visitor<'de> for Visitor<PRECISION> {
    type Value = Decimal;
  • replacement in common/decimal-parsers/src/lib.rs at line 147
    [6.2900][6.2900:2910](),[6.2910][2.8:45](),[2.45][6.2951:2977](),[6.2951][6.2951:2977]()
    #[inline]
    fn overflow_128(val: u128) -> bool {
    val >= OVERFLOW_U96
    }
    [6.2900]
    [6.2977]
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("a decimal number in german format")
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 151
    [6.2978][6.2978:3010](),[6.3010][6.2919:3257](),[6.3257][6.3338:3840](),[6.3338][6.3338:3840]()
    /// Dispatch the next byte:
    ///
    /// * `SAW_DECIMAL_SEPARATOR` - a decimal point has been seen
    /// * `NEGATIVE` - we've encountered a `-` and the number is negative
    /// * `SAW_DIGIT` - a digit has been encountered (when HAS is false it's invalid)
    /// * `BIG` - a number that uses 96 bits instead of only 64 bits
    /// * `FIRST` - true if it is the first byte in the string
    #[inline]
    fn dispatch_next<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEGATIVE: bool,
    const SAW_DIGIT: bool,
    const BIG: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    if let Some((next, bytes)) = bytes.split_first() {
    byte_dispatch_u64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, SAW_DIGIT, BIG, false>(
    bytes, data64, scale, *next,
    )
    } else {
    handle_data::<NEGATIVE, SAW_DIGIT>(data64 as u128, scale)
    }
    }
    [6.2978]
    [6.3840]
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    let mut amount = super::parse(v).map_err(E::custom)?;
    amount.rescale(PRECISION);
    Ok(amount)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 161
    [6.3841][6.3841:4574]()
    #[inline(never)]
    fn non_digit_dispatch_u64<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEG: bool,
    const NON_EMPTY: bool,
    const BIG: bool,
    const FIRST: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    b: u8,
    ) -> Result<Decimal, Error> {
    match b {
    b'-' if FIRST && !NON_EMPTY => {
    dispatch_next::<false, true, false, BIG>(bytes, data64, scale)
    }
    b'+' if FIRST && !NON_EMPTY => {
    dispatch_next::<false, false, false, BIG>(bytes, data64, scale)
    }
    b'.' if !SAW_DECIMAL_SEPARATOR && NON_EMPTY => {
    handle_separator::<SAW_DECIMAL_SEPARATOR, NEG, BIG>(bytes, data64, scale)
    }
    b => tail_invalid_digit(b),
    }
    }
    [6.3841]
    [6.4574]
    deserializer.deserialize_str(Visitor::<PRECISION>)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 165
    [6.4575][6.4575:5266]()
    #[inline]
    fn byte_dispatch_u64<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEGATIVE: bool,
    const NON_EMPTY: bool,
    const BIG: bool,
    const FIRST: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    b: u8,
    ) -> Result<Decimal, Error> {
    match b {
    b'0'..=b'9' => {
    handle_digit_64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, BIG>(bytes, data64, scale, b - b'0')
    }
    b',' if !SAW_DECIMAL_SEPARATOR => {
    handle_point::<NEGATIVE, NON_EMPTY, BIG>(bytes, data64, scale)
    }
    b => non_digit_dispatch_u64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, NON_EMPTY, BIG, FIRST>(
    bytes, data64, scale, b,
    ),
    }
    }
    [6.4575]
    [6.5266]
    /// Deserializes a [`Decimal`] in the locale-specific format.
    ///
    /// # Errors
    ///
    /// Returns an error if deserialization fails or the deserialized value cannot be parsed as a `Decimal`
    /// according to
    #[doc = concat!("[`crate::", stringify!($name), "::parse`].")]
    pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
    where
    D: serde::Deserializer<'de>,
    {
    struct Visitor;
  • replacement in common/decimal-parsers/src/lib.rs at line 178
    [6.5267][6.5267:5642]()
    #[inline(never)]
    fn handle_digit_64<const SAW_DECIMAL_SEPARATOR: bool, const NEGATIVE: bool, const BIG: bool>(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    digit: u8,
    ) -> Result<Decimal, Error> {
    // we have already validated that we cannot overflow
    let data64 = data64 * 10 + digit as u64;
    let scale = if SAW_DECIMAL_SEPARATOR { scale + 1 } else { 0 };
    [6.5267]
    [6.5642]
    impl<'de> serde::de::Visitor<'de> for Visitor {
    type Value = Decimal;
  • replacement in common/decimal-parsers/src/lib.rs at line 181
    [6.5643][6.5643:6184]()
    if let Some((next, bytes)) = bytes.split_first() {
    let next = *next;
    if SAW_DECIMAL_SEPARATOR && BIG && scale >= 28 {
    Err(Error::Underflow)
    } else if BIG && overflow_64(data64) {
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEGATIVE>(data64 as u128, bytes, scale, next)
    } else {
    byte_dispatch_u64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, true, BIG, false>(
    bytes, data64, scale, next,
    )
    }
    } else {
    let data: u128 = data64 as u128;
    [6.5643]
    [6.6184]
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
    formatter.write_str("a decimal number in german format")
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 185
    [6.6185][6.6185:6244]()
    handle_data::<NEGATIVE, true>(data, scale)
    }
    }
    [6.6185]
    [6.6244]
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
    E: serde::de::Error,
    {
    super::parse(v).map_err(E::custom)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 193
    [6.6245][6.6245:6487]()
    #[inline(never)]
    fn handle_point<const NEG: bool, const NON_EMPTY: bool, const BIG: bool>(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    dispatch_next::<true, NEG, NON_EMPTY, BIG>(bytes, data64, scale)
    }
    [6.6245]
    [6.6487]
    deserializer.deserialize_str(Visitor)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 197
    [6.6488][6.6488:6758]()
    #[inline(never)]
    fn handle_separator<const SAW_DECIMAL_SEPARATOR: bool, const NEG: bool, const BIG: bool>(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    dispatch_next::<SAW_DECIMAL_SEPARATOR, NEG, true, BIG>(bytes, data64, scale)
    }
    [6.6488]
    [6.6758]
    #[inline]
    #[doc = concat!(
    "This function is based on [`Decimal::from_str_exact`], but adapted to use different grouping (`",
    stringify!($group_separator),
    "`) and decimal separators (`",
    stringify!($decimal_separator),
    "`)",
    )]
    /// # Errors
    ///
    /// Returns an error when the given string does not represent a valid `Decimal` or if the value cannot be
    /// represented with the supported precision, i.e., if it causes over- or underflow.
    pub fn parse(value: &str) -> Result<Decimal, Error> {
    let bytes = value.as_bytes();
    if bytes.len() < BYTES_TO_OVERFLOW_U64 {
    parse_str_radix_10_dispatch::<false>(bytes)
    } else {
    parse_str_radix_10_dispatch::<true>(bytes)
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 218
    [6.6759][6.6759:6852]()
    #[cold]
    fn tail_error(from: &'static str) -> Result<Decimal, Error> {
    Err(from.into())
    }
    [6.6759]
    [6.6852]
    #[inline]
    fn parse_str_radix_10_dispatch<const BIG: bool>(
    bytes: &[u8],
    ) -> Result<Decimal, Error> {
    match bytes {
    [b, rest @ ..] => {
    byte_dispatch_u64::<false, false, false, BIG, true>(rest, 0, 0, *b)
    }
    [] => tail_error("invalid decimal: empty"),
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 230
    [6.6853][6.6853:7176]()
    #[inline(never)]
    #[cold]
    fn tail_invalid_digit(digit: u8) -> Result<Decimal, Error> {
    match digit {
    b',' => tail_error("invalid decimal: two decimal points"),
    // b'_' => tail_error("Invalid decimal: must start lead with a number"),
    _ => tail_error("invalid decimal: unknown character"),
    }
    }
    [6.6853]
    [6.7176]
    #[inline]
    const fn overflow_64(val: u64) -> bool {
    val >= WILL_OVERFLOW_U64
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 235
    [6.7177][6.7177:7483]()
    #[inline(never)]
    #[cold]
    fn handle_full_128<const SAW_DECIMAL_SEPARATOR: bool, const NEG: bool>(
    mut data: u128,
    bytes: &[u8],
    scale: u8,
    next_byte: u8,
    ) -> Result<Decimal, Error> {
    let b = next_byte;
    match b {
    b'0'..=b'9' => {
    let digit = u32::from(b - b'0');
    [6.7177]
    [6.7483]
    #[inline]
    const fn overflow_128(val: u128) -> bool {
    val >= OVERFLOW_U96
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 240
    [6.7484][6.7484:7782]()
    // If the data is going to overflow then we should go into recovery mode
    let next = (data * 10) + digit as u128;
    if overflow_128(next) {
    if !SAW_DECIMAL_SEPARATOR {
    tail_error("invalid decimal: overflow from too many digits")
    [6.7484]
    [6.7782]
    /// Dispatch the next byte:
    ///
    /// * `SAW_DECIMAL_SEPARATOR` - a decimal point has been seen
    /// * `NEGATIVE` - we've encountered a `-` and the number is negative
    /// * `SAW_DIGIT` - a digit has been encountered (when HAS is false it's invalid)
    /// * `BIG` - a number that uses 96 bits instead of only 64 bits
    /// * `FIRST` - true if it is the first byte in the string
    #[inline]
    fn dispatch_next<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEGATIVE: bool,
    const SAW_DIGIT: bool,
    const BIG: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    if let Some((next, bytes)) = bytes.split_first() {
    byte_dispatch_u64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, SAW_DIGIT, BIG, false>(
    bytes, data64, scale, *next,
    )
  • replacement in common/decimal-parsers/src/lib.rs at line 263
    [6.7807][6.7807:7849]()
    Err(Error::Underflow)
    [6.7807]
    [6.7849]
    handle_data::<NEGATIVE, SAW_DIGIT>(u128::from(data64), scale)
  • replacement in common/decimal-parsers/src/lib.rs at line 265
    [6.7867][6.7867:7982]()
    } else {
    data = next;
    let scale = scale + SAW_DECIMAL_SEPARATOR as u8;
    [6.7867]
    [6.7982]
    }
    #[inline(never)]
    fn non_digit_dispatch_u64<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEG: bool,
    const NON_EMPTY: bool,
    const BIG: bool,
    const FIRST: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    b: u8,
    ) -> Result<Decimal, Error> {
    match b {
    b'-' if FIRST && !NON_EMPTY => {
    dispatch_next::<false, true, false, BIG>(bytes, data64, scale)
    }
    b'+' if FIRST && !NON_EMPTY => {
    dispatch_next::<false, false, false, BIG>(bytes, data64, scale)
    }
    GROUP_SEPARATOR if !SAW_DECIMAL_SEPARATOR && NON_EMPTY => {
    handle_separator::<SAW_DECIMAL_SEPARATOR, NEG, BIG>(bytes, data64, scale)
    }
    b => tail_invalid_digit(b),
    }
    }
    #[inline]
    fn byte_dispatch_u64<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEGATIVE: bool,
    const NON_EMPTY: bool,
    const BIG: bool,
    const FIRST: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    b: u8,
    ) -> Result<Decimal, Error> {
    match b {
    b'0'..=b'9' => handle_digit_64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, BIG>(
    bytes,
    data64,
    scale,
    b - b'0',
    ),
    DECIMAL_SEPARATOR if !SAW_DECIMAL_SEPARATOR => {
    handle_point::<NEGATIVE, NON_EMPTY, BIG>(bytes, data64, scale)
    }
    b => non_digit_dispatch_u64::<
    SAW_DECIMAL_SEPARATOR,
    NEGATIVE,
    NON_EMPTY,
    BIG,
    FIRST,
    >(bytes, data64, scale, b),
    }
    }
    #[inline(never)]
    fn handle_digit_64<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEGATIVE: bool,
    const BIG: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    digit: u8,
    ) -> Result<Decimal, Error> {
    // we have already validated that we cannot overflow
    let data64 = data64 * 10 + u64::from(digit);
    let scale = if SAW_DECIMAL_SEPARATOR { scale + 1 } else { 0 };
  • replacement in common/decimal-parsers/src/lib.rs at line 344
    [6.8087][6.8087:8149]()
    if SAW_DECIMAL_SEPARATOR && scale >= 28 {
    [6.8087]
    [6.8149]
    if SAW_DECIMAL_SEPARATOR && BIG && scale >= 28 {
  • edit in common/decimal-parsers/src/lib.rs at line 346
    [6.8195]
    [6.8195]
    } else if BIG && overflow_64(data64) {
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEGATIVE>(
    data64 as u128,
    bytes,
    scale,
    next,
    )
  • replacement in common/decimal-parsers/src/lib.rs at line 354
    [6.8224][6.8224:8320]()
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEG>(data, bytes, scale, next)
    [6.8224]
    [6.8320]
    byte_dispatch_u64::<SAW_DECIMAL_SEPARATOR, NEGATIVE, true, BIG, false>(
    bytes, data64, scale, next,
    )
  • replacement in common/decimal-parsers/src/lib.rs at line 359
    [6.8367][6.8367:8425]()
    handle_data::<NEG, true>(data, scale)
    [6.8367]
    [6.8425]
    let data: u128 = data64 as u128;
    handle_data::<NEGATIVE, true>(data, scale)
  • replacement in common/decimal-parsers/src/lib.rs at line 364
    [6.8457][6.8457:8758]()
    }
    b',' if !SAW_DECIMAL_SEPARATOR => {
    // This call won't tail?
    if let Some((next, bytes)) = bytes.split_first() {
    handle_full_128::<true, NEG>(data, bytes, scale, *next)
    } else {
    handle_data::<NEG, true>(data, scale)
    [6.8457]
    [6.8758]
    #[inline(never)]
    fn handle_point<const NEG: bool, const NON_EMPTY: bool, const BIG: bool>(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    dispatch_next::<true, NEG, NON_EMPTY, BIG>(bytes, data64, scale)
  • replacement in common/decimal-parsers/src/lib.rs at line 373
    [6.8772][6.8772:9027]()
    }
    b'.' => {
    if let Some((next, bytes)) = bytes.split_first() {
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEG>(data, bytes, scale, *next)
    } else {
    handle_data::<NEG, true>(data, scale)
    [6.8772]
    [6.9027]
    #[inline(never)]
    fn handle_separator<
    const SAW_DECIMAL_SEPARATOR: bool,
    const NEG: bool,
    const BIG: bool,
    >(
    bytes: &[u8],
    data64: u64,
    scale: u8,
    ) -> Result<Decimal, Error> {
    dispatch_next::<SAW_DECIMAL_SEPARATOR, NEG, true, BIG>(bytes, data64, scale)
  • edit in common/decimal-parsers/src/lib.rs at line 386
    [6.9041][6.9041:9095]()
    }
    b => tail_invalid_digit(b),
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 387
    [6.9096][6.9096:9210]()
    #[inline(never)]
    fn tail_empty() -> Result<Decimal, Error> {
    tail_error("invalid decimal: no digits found")
    }
    [6.9096]
    [6.9210]
    #[cold]
    fn tail_error(from: &'static str) -> Result<Decimal, Error> {
    Err(from.into())
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 392
    [6.9211][6.9211:9358]()
    #[inline]
    fn handle_data<const NEG: bool, const HAS: bool>(data: u128, scale: u8) -> Result<Decimal, Error> {
    debug_assert_eq!(data >> 96, 0);
    [6.9211]
    [6.9358]
    #[inline(never)]
    #[cold]
    fn tail_invalid_digit(digit: u8) -> Result<Decimal, Error> {
    match digit {
    DECIMAL_SEPARATOR => tail_error("invalid decimal: two decimal points"),
    // b'_' => tail_error("Invalid decimal: must start lead with a number"),
    _ => tail_error("invalid decimal: unknown character"),
    }
    }
  • replacement in common/decimal-parsers/src/lib.rs at line 402
    [6.9359][6.9359:9590]()
    if !HAS {
    tail_empty()
    } else {
    Ok(Decimal::from_parts(
    data as u32,
    (data >> 32) as u32,
    (data >> 64) as u32,
    NEG,
    scale as u32,
    ))
    }
    [6.9359]
    [6.9590]
    #[inline(never)]
    #[cold]
    fn handle_full_128<const SAW_DECIMAL_SEPARATOR: bool, const NEG: bool>(
    mut data: u128,
    bytes: &[u8],
    scale: u8,
    next_byte: u8,
    ) -> Result<Decimal, Error> {
    let b = next_byte;
    match b {
    b'0'..=b'9' => {
    let digit = u32::from(b - b'0');
    // If the data is going to overflow then we should go into recovery mode
    let next = (data * 10) + u128::from(digit);
    if overflow_128(next) {
    if SAW_DECIMAL_SEPARATOR {
    Err(Error::Underflow)
    } else {
    tail_error("invalid decimal: overflow from too many digits")
    }
    } else {
    data = next;
    let scale = scale + u8::from(SAW_DECIMAL_SEPARATOR);
    if let Some((next, bytes)) = bytes.split_first() {
    let next = *next;
    if SAW_DECIMAL_SEPARATOR && scale >= 28 {
    Err(Error::Underflow)
    } else {
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEG>(
    data, bytes, scale, next,
    )
    }
    } else {
    handle_data::<NEG, true>(data, scale)
    }
    }
    }
    DECIMAL_SEPARATOR if !SAW_DECIMAL_SEPARATOR => {
    // This call won't tail?
    if let Some((next, bytes)) = bytes.split_first() {
    handle_full_128::<true, NEG>(data, bytes, scale, *next)
    } else {
    handle_data::<NEG, true>(data, scale)
    }
    }
    GROUP_SEPARATOR => {
    if let Some((next, bytes)) = bytes.split_first() {
    handle_full_128::<SAW_DECIMAL_SEPARATOR, NEG>(data, bytes, scale, *next)
    } else {
    handle_data::<NEG, true>(data, scale)
    }
    }
    b => tail_invalid_digit(b),
    }
    }
    #[inline(never)]
    fn tail_empty() -> Result<Decimal, Error> {
    tail_error("invalid decimal: no digits found")
    }
    #[inline]
    fn handle_data<const NEG: bool, const HAS: bool>(
    data: u128,
    scale: u8,
    ) -> Result<Decimal, Error> {
    debug_assert_eq!(data >> 96, 0);
    if !HAS {
    tail_empty()
    } else {
    Ok(Decimal::from_parts(
    data as u32,
    (data >> 32) as u32,
    (data >> 64) as u32,
    NEG,
    scale as u32,
    ))
    }
    }
    }
    };
  • edit in common/decimal-parsers/src/lib.rs at line 486
    [6.9592]
    make_module!(english, b'.', b',');
    make_module!(german, b',', b'.');
  • file move: Cargo.toml (---r------)Cargo.toml (----------)
    [6.46]
    [6.9628]
  • replacement in common/decimal-parsers/Cargo.toml at line 2
    [6.9639][6.9639:9663]()
    name = "german-decimal"
    [6.9639]
    [6.3258]
    name = "decimal-parsers"
  • edit in common/decimal-parsers/Cargo.toml at line 4
    [6.3259]
    [6.37427]
    authors.workspace = true
  • edit in common/decimal-parsers/Cargo.toml at line 19
    [34.144]
    [lints]
    workspace = true
  • edit in Cargo.toml at line 6
    [6.7164][6.185:212](),[6.185][6.185:212]()
    "common/german-decimal",
  • edit in Cargo.toml at line 93
    [6.7421]
    [35.8316]
    decimal-parsers.path = "common/decimal-parsers"
  • edit in Cargo.lock at line 199
    [6.7875]
    [6.7875]
    "decimal-parsers",
  • edit in Cargo.lock at line 201
    [6.7894][6.7894:7913]()
    "german-decimal",
  • edit in Cargo.lock at line 221
    [6.72290]
    [6.920]
    "decimal-parsers",
  • edit in Cargo.lock at line 223
    [6.939][6.939:958]()
    "german-decimal",
  • edit in Cargo.lock at line 260
    [6.8390]
    [6.17918]
    "decimal-parsers",
  • edit in Cargo.lock at line 262
    [6.17937][6.17937:17956]()
    "german-decimal",
  • edit in Cargo.lock at line 280
    [6.8646]
    [6.8646]
    "decimal-parsers",
  • edit in Cargo.lock at line 282
    [6.8665][6.8665:8684]()
    "german-decimal",
  • edit in Cargo.lock at line 323
    [6.21339]
    [6.9126]
    "decimal-parsers",
  • edit in Cargo.lock at line 325
    [6.9145][6.9145:9164]()
    "german-decimal",
  • edit in Cargo.lock at line 340
    [6.9421]
    [6.9421]
    "decimal-parsers",
  • edit in Cargo.lock at line 342
    [6.9440][6.9440:9459]()
    "german-decimal",
  • edit in Cargo.lock at line 359
    [6.9754]
    [6.9754]
    "decimal-parsers",
  • edit in Cargo.lock at line 361
    [6.9773][6.9773:9792]()
    "german-decimal",
  • edit in Cargo.lock at line 375
    [6.10029]
    [6.21370]
    "decimal-parsers",
  • edit in Cargo.lock at line 377
    [6.21389][6.21389:21408]()
    "german-decimal",
  • edit in Cargo.lock at line 1031
    [5.7428]
    [5.7428]
    ]
    [[package]]
    name = "decimal-parsers"
    version = "0.0.0-dev.0"
    dependencies = [
    "proptest",
    "rust_decimal",
    "rust_decimal_macros",
    "serde",
  • edit in Cargo.lock at line 1395
    [5.16842][5.16842:16986]()
    ]
    [[package]]
    name = "german-decimal"
    version = "0.0.0-dev.0"
    dependencies = [
    "proptest",
    "rust_decimal",
    "rust_decimal_macros",
    "serde",