1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

// Provider structs must be stable
#![allow(clippy::exhaustive_structs, clippy::exhaustive_enums)]

//! Data provider struct definitions for this ICU4X component.
//!
//! Read more about data providers: [`icu_provider`]

use icu_provider::prelude::*;
use num_bigint::BigInt;
use zerotrie::ZeroTrieSimpleAscii;
use zerovec::{ule::AsULE, VarZeroVec, ZeroVec};

use crate::measure::provider::single_unit::SingleUnit;
#[cfg(feature = "compiled_data")]
/// Baked data
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
/// </div>
pub use crate::provider::Baked;

use super::ratio::IcuRatio;

/// This type encapsulates all the constant data required for unit conversions.
///
/// <div class="stab unstable">
/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
/// including in SemVer minor releases. While the serde representation of data structs is guaranteed
/// to be stable, their Rust representation might not be. Use with caution.
/// </div>
#[icu_provider::data_struct(marker(UnitsInfoV1Marker, "units/info@1", singleton))]
#[derive(Clone, PartialEq, Debug)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_experimental::units::provider))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub struct UnitsInfoV1<'data> {
    // TODO: remove this field once we are using this map from `measure/provider::UnitsTrie`.
    /// Maps from unit name (e.g. foot) to it is conversion information.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub units_conversion_trie: ZeroTrieSimpleAscii<ZeroVec<'data, u8>>,

    /// Contains the conversion information, such as the conversion rate and the base unit.
    /// For example, the conversion information for the unit `foot` is `1 foot = 0.3048 meter`.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub convert_infos: VarZeroVec<'data, ConversionInfoULE>,
}

/// Represents the conversion information for a unit.
/// Which includes the base unit (the unit which the unit is converted to), the conversion factor, and the offset.
#[zerovec::make_varule(ConversionInfoULE)]
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_experimental::units::provider))]
#[cfg_attr(
    feature = "datagen",
    derive(serde::Serialize),
    zerovec::derive(Serialize)
)]
#[cfg_attr(
    feature = "serde",
    derive(serde::Deserialize),
    zerovec::derive(Deserialize)
)]
#[zerovec::derive(Debug)]
pub struct ConversionInfo<'data> {
    /// Contains the base unit (after parsing) which what the unit is converted to.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub basic_units: ZeroVec<'data, SingleUnit>,

    /// Represents the numerator of the conversion factor.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub factor_num: ZeroVec<'data, u8>,

    /// Represents the denominator of the conversion factor.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub factor_den: ZeroVec<'data, u8>,

    /// Represents the sign of the conversion factor.
    pub factor_sign: Sign,

    // TODO(#4311).
    /// Represents the numerator of the offset.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub offset_num: ZeroVec<'data, u8>,

    // TODO(#4311).
    /// Represents the denominator of the offset.
    #[cfg_attr(feature = "serde", serde(borrow))]
    pub offset_den: ZeroVec<'data, u8>,

    /// Represents the sign of the offset.
    pub offset_sign: Sign,

    /// Represents the exactness of the conversion factor.
    pub exactness: Exactness,
}

/// This enum is used to represent the sign of a constant value.
#[zerovec::make_ule(SignULE)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_experimental::units::provider))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum Sign {
    #[default]
    Positive = 0,
    Negative = 1,
}

/// This enum is used to represent the exactness of a factor
#[zerovec::make_ule(ExactnessULE)]
#[cfg_attr(feature = "datagen", derive(serde::Serialize, databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_experimental::units::provider))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum Exactness {
    #[default]
    Exact = 0,
    Approximate = 1,
}

impl ConversionInfoULE {
    /// Extracts the conversion factor as [`super::ratio::IcuRatio`].
    pub(crate) fn factor_as_ratio(&self) -> IcuRatio {
        let sign: num_bigint::Sign = Sign::from_unaligned(self.factor_sign).into();
        IcuRatio::from_big_ints(
            BigInt::from_bytes_le(sign, self.factor_num().as_ule_slice()),
            BigInt::from_bytes_le(num_bigint::Sign::Plus, self.factor_den().as_ule_slice()),
        )
    }

    /// Extracts the offset as [`super::ratio::IcuRatio`].
    pub(crate) fn offset_as_ratio(&self) -> IcuRatio {
        let sign: num_bigint::Sign = Sign::from_unaligned(self.offset_sign).into();
        IcuRatio::from_big_ints(
            BigInt::from_bytes_le(sign, self.offset_num().as_ule_slice()),
            BigInt::from_bytes_le(num_bigint::Sign::Plus, self.offset_den().as_ule_slice()),
        )
    }
}