Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider adding impl TryFrom<Fact> for String #198

Open
wez opened this issue Dec 20, 2023 · 1 comment
Open

Consider adding impl TryFrom<Fact> for String #198

wez opened this issue Dec 20, 2023 · 1 comment

Comments

@wez
Copy link

wez commented Dec 20, 2023

I wanted to do this:

let hostnames: Vec<String> = a.query("data($name) <- hostname($name)")?;

but:

error[E0277]: the trait bound `std::string::String: From<biscuit_auth::builder::Fact>` is not satisfied
   --> src/tokens.rs:270:44
    |
270 |             let hostnames: Vec<String> = a.query("data($name) <- hostname($name)")?;
    |                                            ^^^^^ the trait `From<biscuit_auth::builder::Fact>` is not implemented for `std::string::String`
    |

I found that I can do this using tuple syntax and then converting, but it is somewhat verbose and since the order of the generics puts T as the second parameter, it becomes impossible to try to make this into a one-liner because all of the required generics are not easily named in a one liner:

let hostnames : Vec<String> = {
    let data: Vec<(String,)> = a.query("data($name) <- hostname($name)")?;
    data.into_iter().map(|t| t.0).collect()
};

I also tried making a newtype wrapper around string to handle the conversion, but it does not solve the need for an additional remapping of the array into the underlying type.

I suppose this request generalizes to allowing non-tuple variants of the other types that work with the (T,) syntax.

See also:

@wez
Copy link
Author

wez commented Dec 21, 2023

I've thrown together a couple of helper functions to make this a little more ergonomic in my application, sharing here in case it is helpful, and/or influences the query API here in this crate.

I'm sorting the results because that matters for determinism and matching in tests, but I can see how that may not always be 100% desirable.

let hostnames: Vec<String> =
    query_facts_scalar(&mut a, "data($name) <- hostname($name)")?;
/// Query facts and return them in a stable order.
fn query_facts<T, R, E>(authorizer: &mut Authorizer, query: R) -> Result<Vec<T>, Token>
where
    R: TryInto<Rule>,
    T: TryFrom<Fact, Error = E> + Ord,
    E: Into<Token>,
    Token: From<<R as TryInto<Rule>>::Error>,
{
    let mut data = authorizer.query(query)?;
    data.sort();
    Ok(data)
}

/// Query a set of scalar facts and return them in a stable order.
fn query_facts_scalar<T, R, E>(authorizer: &mut Authorizer, query: R) -> Result<Vec<T>, Token>
where
    R: TryInto<Rule>,
    E: Into<Token>,
    Token: From<<R as TryInto<Rule>>::Error>,
    (T,): TryFrom<Fact, Error = E> + Ord,
{
    let mut data: Vec<(T,)> = query_facts(authorizer, query)?;
    let data: Vec<T> = data.into_iter().map(|t| t.0).collect();
    Ok(data)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant