Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  iso8601.rs   Sprache: unbekannt

 
//! Parse parts of an ISO 8601-formatted value.

use num_conv::prelude::*;

use crate::convert::*;
use crate::error;
use crate::error::ParseFromDescription::{InvalidComponent, InvalidLiteral};
use crate::format_description::well_known::iso8601::EncodedConfig;
use crate::format_description::well_known::Iso8601;
use crate::parsing::combinator::rfc::iso8601::{
    day, dayk, dayo, float, hour, min, month, week, year, ExtendedKind,
};
use crate::parsing::combinator::{ascii_char, sign};
use crate::parsing::{Parsed, ParsedItem};

impl<const CONFIG: EncodedConfig> Iso8601<CONFIG> {
    // Basic: [year][month][day]
    // Extended: [year]["-"][month]["-"][day]
    // Basic: [year][dayo]
    // Extended: [year]["-"][dayo]
    // Basic: [year]["W"][week][dayk]
    // Extended: [year]["-"]["W"][week]["-"][dayk]
    /// Parse a date in the basic or extended format. Reduced precision is permitted.
    pub(crate) fn parse_date<'a>(
        parsed: &'a mut Parsed,
        extended_kind: &'a mut ExtendedKind,
    ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
        move |input| {
            // Same for any acceptable format.
            let ParsedItem(mut input, year) = year(input).ok_or(InvalidComponent("year"))?;
            *extended_kind = match ascii_char::<b'-'>(input) {
                Some(ParsedItem(new_input, ())) => {
                    input = new_input;
                    ExtendedKind::Extended
                }
                None => ExtendedKind::Basic, // no separator before mandatory month/ordinal/week
            };

            let parsed_month_day = (|| {
                let ParsedItem(mut input, month) = month(input).ok_or(InvalidComponent("month"))?;
                if extended_kind.is_extended() {
                    input = ascii_char::<b'-'>(input)
                        .ok_or(InvalidLiteral)?
                        .into_inner();
                }
                let ParsedItem(input, day) = day(input).ok_or(InvalidComponent("day"))?;
                Ok(ParsedItem(input, (month, day)))
            })();
            let mut ret_error = match parsed_month_day {
                Ok(ParsedItem(input, (month, day))) => {
                    *parsed = parsed
                        .with_year(year)
                        .ok_or(InvalidComponent("year"))?
                        .with_month(month)
                        .ok_or(InvalidComponent("month"))?
                        .with_day(day)
                        .ok_or(InvalidComponent("day"))?;
                    return Ok(input);
                }
                Err(err) => err,
            };

            // Don't check for `None`, as the error from year-month-day will always take priority.
            if let Some(ParsedItem(input, ordinal)) = dayo(input) {
                *parsed = parsed
                    .with_year(year)
                    .ok_or(InvalidComponent("year"))?
                    .with_ordinal(ordinal)
                    .ok_or(InvalidComponent("ordinal"))?;
                return Ok(input);
            }

            let parsed_week_weekday = (|| {
                let input = ascii_char::<b'W'>(input)
                    .ok_or((false, InvalidLiteral))?
                    .into_inner();
                let ParsedItem(mut input, week) =
                    week(input).ok_or((true, InvalidComponent("week")))?;
                if extended_kind.is_extended() {
                    input = ascii_char::<b'-'>(input)
                        .ok_or((true, InvalidLiteral))?
                        .into_inner();
                }
                let ParsedItem(input, weekday) =
                    dayk(input).ok_or((true, InvalidComponent("weekday")))?;
                Ok(ParsedItem(input, (week, weekday)))
            })();
            match parsed_week_weekday {
                Ok(ParsedItem(input, (week, weekday))) => {
                    *parsed = parsed
                        .with_iso_year(year)
                        .ok_or(InvalidComponent("year"))?
                        .with_iso_week_number(week)
                        .ok_or(InvalidComponent("week"))?
                        .with_weekday(weekday)
                        .ok_or(InvalidComponent("weekday"))?;
                    return Ok(input);
                }
                Err((false, _err)) => {}
                // This error is more accurate than the one from year-month-day.
                Err((true, err)) => ret_error = err,
            }

            Err(ret_error.into())
        }
    }

    // Basic: ["T"][hour][min][sec]
    // Extended: ["T"][hour][":"][min][":"][sec]
    // Reduced precision: components after [hour] (including their preceding separator) can be
    // omitted. ["T"] can be omitted if there is no date present.
    /// Parse a time in the basic or extended format. Reduced precision is permitted.
    pub(crate) fn parse_time<'a>(
        parsed: &'a mut Parsed,
        extended_kind: &'a mut ExtendedKind,
        date_is_present: bool,
    ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
        move |mut input| {
            if date_is_present {
                input = ascii_char::<b'T'>(input)
                    .ok_or(InvalidLiteral)?
                    .into_inner();
            }

            let ParsedItem(mut input, hour) = float(input).ok_or(InvalidComponent("hour"))?;
            match hour {
                (hour, None) => parsed.set_hour_24(hour).ok_or(InvalidComponent("hour"))?,
                (hour, Some(fractional_part)) => {
                    *parsed = parsed
                        .with_hour_24(hour)
                        .ok_or(InvalidComponent("hour"))?
                        .with_minute((fractional_part * Second::per(Minute) as f64) as _)
                        .ok_or(InvalidComponent("minute"))?
                        .with_second(
                            (fractional_part * Second::per(Hour) as f64 % Minute::per(Hour) as f64)
                                as _,
                        )
                        .ok_or(InvalidComponent("second"))?
                        .with_subsecond(
                            (fractional_part * Nanosecond::per(Hour) as f64
                                % Nanosecond::per(Second) as f64) as _,
                        )
                        .ok_or(InvalidComponent("subsecond"))?;
                    return Ok(input);
                }
            };

            if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
                extended_kind
                    .coerce_extended()
                    .ok_or(InvalidComponent("minute"))?;
                input = new_input;
            };

            let mut input = match float(input) {
                Some(ParsedItem(input, (minute, None))) => {
                    extended_kind.coerce_basic();
                    parsed
                        .set_minute(minute)
                        .ok_or(InvalidComponent("minute"))?;
                    input
                }
                Some(ParsedItem(input, (minute, Some(fractional_part)))) => {
                    // `None` is valid behavior, so don't error if this fails.
                    extended_kind.coerce_basic();
                    *parsed = parsed
                        .with_minute(minute)
                        .ok_or(InvalidComponent("minute"))?
                        .with_second((fractional_part * Second::per(Minute) as f64) as _)
                        .ok_or(InvalidComponent("second"))?
                        .with_subsecond(
                            (fractional_part * Nanosecond::per(Minute) as f64
                                % Nanosecond::per(Second) as f64) as _,
                        )
                        .ok_or(InvalidComponent("subsecond"))?;
                    return Ok(input);
                }
                // colon was present, so minutes are required
                None if extended_kind.is_extended() => {
                    return Err(error::Parse::ParseFromDescription(InvalidComponent(
                        "minute",
                    )));
                }
                None => {
                    // Missing components are assumed to be zero.
                    *parsed = parsed
                        .with_minute(0)
                        .ok_or(InvalidComponent("minute"))?
                        .with_second(0)
                        .ok_or(InvalidComponent("second"))?
                        .with_subsecond(0)
                        .ok_or(InvalidComponent("subsecond"))?;
                    return Ok(input);
                }
            };

            if extended_kind.is_extended() {
                match ascii_char::<b':'>(input) {
                    Some(ParsedItem(new_input, ())) => input = new_input,
                    None => {
                        *parsed = parsed
                            .with_second(0)
                            .ok_or(InvalidComponent("second"))?
                            .with_subsecond(0)
                            .ok_or(InvalidComponent("subsecond"))?;
                        return Ok(input);
                    }
                }
            }

            let (input, second, subsecond) = match float(input) {
                Some(ParsedItem(input, (second, None))) => (input, second, 0),
                Some(ParsedItem(input, (second, Some(fractional_part)))) => (
                    input,
                    second,
                    round(fractional_part * Nanosecond::per(Second) as f64) as _,
                ),
                None if extended_kind.is_extended() => {
                    return Err(error::Parse::ParseFromDescription(InvalidComponent(
                        "second",
                    )));
                }
                // Missing components are assumed to be zero.
                None => (input, 0, 0),
            };
            *parsed = parsed
                .with_second(second)
                .ok_or(InvalidComponent("second"))?
                .with_subsecond(subsecond)
                .ok_or(InvalidComponent("subsecond"))?;

            Ok(input)
        }
    }

    // Basic: [±][hour][min] or ["Z"]
    // Extended: [±][hour][":"][min] or ["Z"]
    // Reduced precision: [±][hour] or ["Z"]
    /// Parse a UTC offset in the basic or extended format. Reduced precision is supported.
    pub(crate) fn parse_offset<'a>(
        parsed: &'a mut Parsed,
        extended_kind: &'a mut ExtendedKind,
    ) -> impl FnMut(&[u8]) -> Result<&[u8], error::Parse> + 'a {
        move |input| {
            if let Some(ParsedItem(input, ())) = ascii_char::<b'Z'>(input) {
                *parsed = parsed
                    .with_offset_hour(0)
                    .ok_or(InvalidComponent("offset hour"))?
                    .with_offset_minute_signed(0)
                    .ok_or(InvalidComponent("offset minute"))?
                    .with_offset_second_signed(0)
                    .ok_or(InvalidComponent("offset second"))?;
                return Ok(input);
            }

            let ParsedItem(input, sign) = sign(input).ok_or(InvalidComponent("offset hour"))?;
            let mut input = hour(input)
                .and_then(|parsed_item| {
                    parsed_item.consume_value(|hour| {
                        parsed.set_offset_hour(if sign == b'-' {
                            -hour.cast_signed()
                        } else {
                            hour.cast_signed()
                        })
                    })
                })
                .ok_or(InvalidComponent("offset hour"))?;

            if extended_kind.maybe_extended() {
                if let Some(ParsedItem(new_input, ())) = ascii_char::<b':'>(input) {
                    extended_kind
                        .coerce_extended()
                        .ok_or(InvalidComponent("offset minute"))?;
                    input = new_input;
                };
            }

            match min(input) {
                Some(ParsedItem(new_input, min)) => {
                    input = new_input;
                    parsed
                        .set_offset_minute_signed(if sign == b'-' {
                            -min.cast_signed()
                        } else {
                            min.cast_signed()
                        })
                        .ok_or(InvalidComponent("offset minute"))?;
                }
                None => {
                    // Omitted offset minute is assumed to be zero.
                    parsed.set_offset_minute_signed(0);
                }
            }

            // If `:` was present, the format has already been set to extended. As such, this call
            // will do nothing in that case. If there wasn't `:` but minutes were
            // present, we know it's the basic format. Do not use `?` on the call, as
            // returning `None` is valid behavior.
            extended_kind.coerce_basic();

            Ok(input)
        }
    }
}

/// Round wrapper that uses hardware implementation if `std` is available, falling back to manual
/// implementation for `no_std`
fn round(value: f64) -> f64 {
    #[cfg(feature = "std")]
    {
        value.round()
    }
    #[cfg(not(feature = "std"))]
    {
        round_impl(value)
    }
}

#[cfg(not(feature = "std"))]
#[allow(clippy::missing_docs_in_private_items)]
fn round_impl(value: f64) -> f64 {
    debug_assert!(value.is_sign_positive() && !value.is_nan());

    let f = value % 1.;
    if f < 0.5 { value - f } else { value - f + 1. }
}

[ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge