ion-rust icon indicating copy to clipboard operation
ion-rust copied to clipboard

IonReader Associated Trait Bounds

Open almann opened this issue 2 years ago • 0 comments

Users generally will work only with IonReader<Item = StreamItem, Symbol = Symbol>, but within the implementation it may be useful to operate generically across different IonReader implementations. E.g., I have an abstraction that works across (or on some subset of):

  • IonReader<Item = StreamItem, Symbol = Symbol>
  • IonReader<Item = RawStreamItem, Symbol = RawSymbolToken>
  • IonReader<Item = SystemStreamItem, Symbol = Symbol>

Currently, it is awkward to work across this boundary since StreamItem, RawStreamItem, and SystemStreamItem have similar enumerations, but they are not actually related, so one cannot write generically across them.

// note the missing Copy/Clone--that is probably a bug
#[derive(Eq, PartialEq, Debug)]
pub enum StreamItem {
    Value(IonType),
    Null(IonType),
    Nothing,
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum RawStreamItem {
    VersionMarker(u8, u8),

    Value(IonType),
    Null(IonType),
    Nothing,
}

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum SystemStreamItem {
    VersionMarker(u8, u8),

    SymbolTableValue(IonType),
    SymbolTableNull(IonType),

    Value(IonType),
    Null(IonType),
    Nothing,
}

As we can see, there is structural similarity here. One option might be to have a trait that all IonReader::StreamItem must implement:

#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum CommonStreamItem {
    /// Something the common interface cannot "see."
    /// We could put all the system nested or flattened here if we want...
    Other,

    Value(IonType),
    Null(IonType),
    Nothing,
}

pub trait ReaderStreamItem: Debug + Eq + PartialEq + Copy + Clone {
    fn as_common(self) -> CommonItem;
}

To get something marginally useful for a common Symbol trait, we could do the following:

pub trait ReaderSymbol {
    fn text(&self) -> Option<&str>;
    fn local_sid(&self) -> Option<SymbolId>;
}

With the above I can now write:

fn do_something<R, I, S>(reader: &mut R)
  where
    R: IonReader<Item = I, Symbol = S>,
    I: ReaderStreamItem,
    S: ReaderSymbol
{
    // do some stuff generically on the reader where we don't care about symbols too much
    // note that constraining S to Symbol, makes this work only for User/System stream which may
    // be desirable...
}

almann avatar Apr 18 '23 16:04 almann