tursodatabase / libsql-client-rs

libSQL Rust client library can be used to communicate with sqld natively over HTTP protocol with native Rust interface.
MIT License
75 stars 27 forks source link

feat: `ToSql` trait #33

Open DrewMcArthur opened 1 year ago

DrewMcArthur commented 1 year ago

implementations by other libraries for reference:

having a trait would be helpful in order to clarify which types can be passed to args!, and would make the crate more extensible

pub trait ToSql {
    fn to_sql(&self) -> Result<Value, Error>;
}

ideally would also implement this trait for commonly used types

DrewMcArthur commented 1 year ago

seems like i can just use Into<libsql_client::Value> in place of a trait - if that's accurate, feel free to close this, but hopefully it's helpful for someone down the road!

psarna commented 1 year ago

Yeah, the idea behind args! is that it can accept anything that implement Into<Value>. I suppose we could also add a try_args! for things that implement TryInto<Value>, which would make the interface accept Result as well -- then it will be functionally matching ToSql from all these other crates

DrewMcArthur commented 1 year ago

gotcha. i initially opened this issue when i was looking at implementing a connector for this library in axum-login, but Into<Value> (or TryInto) solves that for me.

now, i'm having trouble going the other way - from a Value to a Uuid, specifically. are there any examples with variables more complex than a str or usize? the docs seem pretty sparse imo.

for reference, here's my current attempt:

    fn from_db_row(row: libsql_client::Row) -> Result<Self, Errors> {
        let id: Uuid = row // (Row)
            .try_column::<String>("id") // Result<String, Error>
            .map_err(Errors::DbStoredUuidParsingError) // Result<String, Errors>
            .map(|v: String| Uuid::parse_str(v.as_str())) // Result<Result<Uuid, Error>, Errors>
            .unwrap() // Result<Uuid, Errors>
            .unwrap(); // Uuid
    // ...

but this gives me:

error[E0271]: type mismatch resolving `<String as TryFrom<&Value>>::Error == String`
  --> src/models/users.rs:19:27
   |
19 |             .try_column::<String>("id")
   |                           ^^^^^^ expected `String`, found `Infallible`
   |
note: required by a bound in `Row::try_column`
  --> /Users/drewmca/.cargo/git/checkouts/libsql-client-rs-57d771ddb99c75d4/5675782/src/lib.rs:72:45
   |
72 |     pub fn try_column<V: TryFrom<&'a Value, Error = String>>(
   |                                             ^^^^^^^^^^^^^^ required by this bound in `Row::try_column`

and i get the same thing if i don't specify the type for try_column. any hints?

DrewMcArthur commented 1 year ago

this seems to have worked for me.

impl User {
    fn from_db_row(row: libsql_client::Row) -> Result<Self, Errors> {
        let id: Uuid;
        let username: String;

        let val = &row.values[0];
        if let Value::Text { value } = val {
            id = Uuid::parse_str(value.as_str()).map_err(Errors::UuidParsingError)?;
        }

        let val = &row.values[1];
        if let Value::Text { value } = val {
            username = value.to_string();
        }

        Ok(Self { id, username })
    }
}

which i found in the turso docs