icu4x icon indicating copy to clipboard operation
icu4x copied to clipboard

`DataProvider` for `ForkByKeyProvider`

Open robertbastian opened this issue 3 years ago • 8 comments

Many of our APIs take ResourceProviders, but we don't currently have a way to combine two ResourceProviders into one that implements both.

robertbastian avatar May 17 '22 14:05 robertbastian

Every provider is either an AnyProvider or a BufferProvider. We can currently freely combine providers given that they are in the same class. However, it would be useful if we could do it directly on ResourceProvider.

sffc avatar May 17 '22 17:05 sffc

Not true. Data transformers, which are the motivation for this, aren't either, and neither is the StaticDataProvider that crabbake generates.

robertbastian avatar May 17 '22 17:05 robertbastian

Data transformers and the CrabBake provider are both AnyProvider-compatible. They should use impl_dyn_provider! which adds the AnyProvider impl.

sffc avatar May 17 '22 18:05 sffc

https://unicode-org.github.io/icu4x-docs/doc/icu_provider/index.html#types-of-data-providers

All nontrivial data providers can fit into one of two classes.

Type 1: Those whose data originates as structured Rust objects Type 2: Those whose data originates as unstructured [u8] buffers

sffc avatar May 17 '22 18:05 sffc

Data transformers and the CrabBake provider are both AnyProvider-compatible. They should use impl_dyn_provider! which adds the AnyProvider impl.

I don't think that'd be right. If we generate a crabbake provider without a key that we need, we'd get a compile time error with ResourceProvider but a runtime error with AnyProvider.

robertbastian avatar May 18 '22 10:05 robertbastian

Duplicate of #3935

robertbastian avatar Oct 24 '23 13:10 robertbastian

Not sure it's a full duplicate; #3935 is for the specific case of provider::Baked whereas this is for the more general case of ForkByKeyProvider.

sffc avatar Oct 24 '23 21:10 sffc

Thought: can something like this work?

struct ForkProvider<A, B> {
    a: A,
    b: B,
}

impl<M> DataProvider<M> for ForkProvider where A: DataProvider<M>, B: DataProvider<M> {
    fn load() {
        match type_id::<M> {
            type_id::<Fork1> => self.a.load().upcast().downcast(),
            type_id::<Fork2> => ...
            type_id::<Fork3> => ...
            _ => self.b.load()
        }
    }
}

@robertbastian noted that it only works if A and B all implement DataProvider<M> for the same types.

We could generate fake impls that always return a DataError, but that seems like a footgun.

sffc avatar Mar 15 '24 16:03 sffc