API design for parsing different types of data based on tag
<p>I have a C library, which is a bit clunky to use, and I am trying to wrap a Rust API around it in a safe, idiomatic, and ergonomic manner.</p>

<p>To make this question a little easier to follow, rather than going through all of the gory details of the C API, the below is a slightly nicer Rust version of the API, but which still needs wrapping to be ergonomic to use.</p>

<pre><code>struct Authorization {
    // private
}

struct Right {
    name: String,
    value: Vec&lt;u8&gt;,
    authorized: bool,
}

enum Flags {
    InteractionAllowed = (1 &lt;&lt; 0),
    ExtendRights = (1 &lt;&lt; 1),
    PartialRights = (1 &lt;&lt; 2),
    // ...etc...
}

enum Error {
    Denied,
    Cancelled,
    InteractionNotAllowed,
    // ...etc...
}

impl Authorization {
    fn new() -&gt; Self;
    fn authorize(&amp;mut self, rights: &amp;[Right], flags: Flags) -&gt; Result&lt;Vec&lt;Right&gt;, Error&gt;;
}
</code></pre>

<p>The idea is that you create this opaque authorization reference, and later can query for whether certain rights are currently authorized, or ask it to authorize those rights, possibly by prompting the user for a password or confirmation. Each right consists of a name and a value. The set of names is extensible; users can define their own rights. What the value consists of depends on the name; it could be any arbitrary serialized data.</p>

<p>The flags determine what kind of operation you are doing. If you pass no flags, it just checks whether the rights requested are currently authorized, returning <code>Ok(_)</code> if so. If you add the <code>ExtendRights</code> flag, it tries to give your process new rights if possible. If you add the <code>InteractionAllowed</code> flag, it may prompt the user for a password or confirmation before extending rights. If you add the <code>PartialRights</code> flag, it will succeed even if not all requested rights could be granted, and the returned <code>Vec&lt;Right&gt;</code> will indicated using the <code>authorized</code> flag which rights were granted.</p>

<p>Now, this API is a bit clunky to use, because you need to use string constants to refer to rights, serialize and deserialize any <code>value</code> data that may be associated with a right yourself with no type safety, and the real API actually contains an additional <code>environment</code> slice of name/value pairs that you need to pass in to provide some additional information.</p>

<p>To make this API friendlier, I've built up a builder API around it, and a trait that allows defining different types of rights and passing in associated data.</p>

<p>This looks like the following, for building up and checking </p>

<pre><code>pub trait Right {
    fn name(&amp;self) -&gt; &amp;str;
    fn value(&amp;self) -&gt; Vec&lt;u8&gt; {
        vec![]
    }
}

pub struct Execute;

impl Right for Execute {
    fn name(&amp;self) -&gt; &amp;str {
        "com.example.execute"
    }
}

pub struct MyCustomRight {
    authorized_dirs: Vec&lt;Path&gt;,
}

impl Right for MyCustomRight {
    fn name(&amp;self) -&gt; &amp;str {
        "com.example.custom"
    }
    fn value(&amp;self) -&gt; Vec&lt;u8&gt; {
        let mut buf = Vec::new();
        let mut serializer = some_serializer(&amp;mut buf);
        self.authorized_dirs.serialize(serializer);
        buf
    }
}

impl Builder {
    fn new() -&gt; Self;
    fn right&lt;R&gt;(&amp;mut self, right: R) -&gt; &amp;mut Self;
    fn extend_rights(&amp;mut self) -&gt; &amp;mut Self;
    fn interaction_allowed(&amp;mut self) -&gt; &amp;mut Self;
    fn check(&amp;mut self) -&gt; Result&lt;(), Error&gt;;
}
</code></pre>

<p>That allows you to use the API as so, if you just want to check if the given rights are authorized</p>

<pre><code>Builder::new()
    .interaction_allowed()
    .right(Execute)
    .right(MyCustomRight { authorized_dirs: vec![] })
    .check();
</code></pre>

<p>So far, for just checking if a given set of rights is authorized (possibly with interaction) this API is working well; the trait allows you to implement your own custom rights, and pass the right in as a data structure, and so be able to easily encapsulate the serialization.</p>

<p>Now, I want to extend the API to support the partial authorization case; instead of just an all or nothing check if a set of rights is allowed, I want to be able to pass in the <code>PartialRights</code> flag and get back some kind of set of rights that were able to be authorized, including deserialized versions of their associated values.</p>

<p>The problem is, how to best return one of a number of different types, which may be extensible by users of this API (so can't be an enum defined ahead of time, with a fixed set of types embedded in it).</p>

<p>I few possibilities I have thought of, but they all seem a little cumbersome in one way or another:</p>

<ul>
<li>For most of these, you could register a trait object when you called <code>right</code> to specify a right to query, that would be capable of deserializing the value to the appropriate type and doing something with it.</li>
<li>Return a vector of trait objects of the <code>Right</code> trait, extended with whatever operations you may want to do with the value. Seems like then you'd need to predict every possible thing users would want to do with the value, which would be cumbersome.</li>
<li>Return a vector of <code>Box&lt;Any&gt;</code>, and have the client try downcasting to each type of <code>Right</code> that they care about.</li>
<li>Add a version of the <code>right</code> call that takes a closure, which gets called with the <code>Right</code> returned with information about whether it was authorized.</li>
<li>Just take the rights passed in mutably, and set an <code>authorized</code> flag on them, so you would just define a variable for each right you cared about beforehand, pass it in, and check it after.</li>
<li>Have your own custom <code>enum</code>, and implement some way of converting all of the types that you care about to your custom <code>enum</code></li>
</ul>

<p>I'm just having trouble coming up with a good design that would allow downstream crates to define their own rights and associated data (including serialization and deserialization), and then getting something back that makes it easy to check which rights were authorized out of the set that you care about. Suggestions and sketches of how this API would work in practice are appreciated.</p>

<p>If I have tried to abstract away from the original problem too much in my above simplification of the API, <a href="https://gist.github.com/lambda/73b55a185bd090163da3" rel="nofollow">here is an excerpt of the raw C bindings, and my current wrapper around it</a>.</p>