Organizing Enum Dependent on Another Enum


#1

Trying to wrap my head around organizing my data structures for a particular problem. I am building a search tool that should work across multiple databases. Each database has well defined searchable fields, but these fields are unique to each database. I’d like my search query to have a field for the database and the query term. How can I ensure that only appropriate fields are allowed in the database?

enum DataBase {
    Foo,
    Bar
}

enum FooFields<'a> {
   FooField1(&'a str),
   FooField2(&'a str)
}

enum BarFields<'a> {
    BarField1(&'a str),
    BarField2(&'a str)
}

struct Query<'a> {
    db: DataBase,
    term: ??? // Needs to only allow relevant fields depending upon db value.
}

#2

I would write Query as:

enum Query<'a> {
    Foo {
        term: FooFields<'a>,
    },
    Bar {
        term: BarFields<'a>,
    },
}

#3
enum DataBase {
    Foo {fields: FooFields, db: Foo}
    Bar {fields: BarFields, db: Bar}
}

or

trait DatabaseTrait {
   type Fields;
}

struct Database<D: DatabaseTrait> {
    fields: D::Fields,
    db: D,
}

#4

@dtolnay this should do the trick but I’d like to be able to implement some functions for the Query struct, which I don’t know how to do if it’s just an enum.

@kornel can you expand on the second option you gave? For instance, how are the various fields then defined?


#5

The same way as you proposed. The trait is just used to make an association between a database type and its related fields type.


#6

If you want to stick to enums, I like @dtolnay’s suggestion.

If you want something more generic, here’s a similar approach to @kornel’s second option:

trait Db {}

trait Field {
    type Db: Db;
}

struct Foo;
impl Db for Foo {}

struct Bar;
impl Db for Bar {}

struct FooField1<'a>(&'a str);
struct FooField2;

impl<'a> Field for FooField1<'a> {
    type Db = Foo;
}

impl Field for FooField2 {
    type Db = Foo;
}

struct Query<F: Field> {
    db: F::Db,
    term: F,
}

fn main() {
    let q = Query {
        db: Foo,
        term: FooField1("field1"),
    };
}