unicode-org / icu4x

Solving i18n for client-side and resource-constrained environments.
https://icu4x.unicode.org
Other
1.37k stars 176 forks source link

Design for the first version of the units conversion. #4370

Open younies opened 11 months ago

younies commented 11 months ago

I am proposing this design for the fist version of the units conversion.

First MeasureUnit:

  1. si prefix --> optional
  2. unit id
  3. power

Second: Converter

  1. Conversion Rate (including the offset)
  2. Convertibility
  1. static fn try_from_ids( id1: &str, id2: &str) --> Result<Self, DimensionError>
  2. static fn try_from_measunits( unit1: &MeasureUnit, unit2: &MeasureUnit) --> Result<Self, DimensionError>
  3. static fn extract_convertibility_from_ids( id1: &str, id2: &str) --> Result<Convertibility, DimensionError>
  4. static fn extract_convertibility_from_measunits( unit1: &MeasureUnit, unit2: &MeasureUnit --> Result<Convertibility, DimensionError>
  5. fn convert_f64(&self, value: f64) --> f64
  6. fn convert_BigRational(&self, value: BigRational) --> BigRational
  7. fn convert_FixedDecimal(&self, value: FixedDecimal) --> FixedDecimal --> postpone for now.

Third: Convertibility

  1. Convertible
  2. Reciprocal
  3. NotConvertible ??? --> do we need it ?

Fourth: DimensionError

  1. WrongCldrUnitId
  2. NotConvertible
younies commented 11 months ago

Discussion:

@sffc , for MeasureUnitParser, we can have MeasureUnitParser::parse_bytes(&self, bytes: &[u8]) --> Result<MeasureUnit> and also MeasureUnitParser::parse_str(&self, id : &str) --> Result<> --> this is an implementation for FromStr trait.

NOTE: we need a MeasureUnitParser that takes the units provider and parse the strings to return MeasureUnit.

Also, for the Converter, we need ConverterFactory

File a TODO issue to split the data between the MeasureUnitParser and ConverterFactory

and start with

  1. fn convert_f64(&self, value: f64) --> f64
  2. fn convert_big_rational(&self, value: BigRational) --> BigRational

LGTM tentatively : @younies , @sffc , @robertbastian , @Manishearth

sffc commented 6 months ago

Design the FFI for units conersion.

option 0

Convert(input: IcuRatio) --> IcuRatio.

IcuRatio::from_f64()
IcuRatio::to_f64()

IcuRatio::to_u64()

option 1

Converter::convert_f64(input: f64) --> f64.
Converter::convert_exact(input: IcuRatio) -> IcuRatio

conv.convert_exact(IcuRatio::fraction(7, 3))
conv.convert_exact(IcuRatio::from(7).pow(-4)))
conv.convert_f64(10e-4))
pub struct IcuRatio(pub BigRational);

option 2

No IcuRatio on FFI

// String syntax: [-]iii.fffE[-]eee
Converter::convert_decimal(input: string) -> Result<string>
Converter::convert_f64(input: f64) -> f64

Eliminated: the user might want to use IcuRatio across multiple call sites

option 3

Converter::convert_decimal(input: FixedDecimal) -> FixedDecimal
Converter::convert_f64(input: f64) -> f64

Eliminated: FixedDecimal is not efficient for math, and it can't represent ratios like 1/3

option 4

Converter::convert_f64(input: f64) -> f64.
Converter::convert_big_rational(input: BigRational) -> BigRational

conv.convert_big_rational(...)
conv.convert_big_rational(...)
conv.convert_f64(10e-4))

Expose subset of BigRational API over FFI as IcuRational (bikeshed)

Note: Ratio<BigInt> has a type alias to BigRational

LGTM: @sffc @robertbastian @younies @Manishearth