Sending polars Dataframe via juniper GraphQL - rust

I am trying to send the Polars dataframe over juniper GraphQL, but #[juniper::graphql_object] is complaining about trail bound
the trait bound `Result<HashMap<std::string::String, polars::prelude::DataFrame>, FieldError>: IntoResolvable<'_, __S, _, ()>` is not satisfied
the following other types implement trait `IntoResolvable<'a, S, T, C>`:
<Result<(&'a <T as GraphQLValue<S2>>::Context, T), FieldError<S1>> as IntoResolvable<'a, S2, T, C>>
<Result<T, E> as IntoResolvable<'a, S, T, C>>
<Result<std::option::Option<(&'a <T as GraphQLValue<S2>>::Context, T)>, FieldError<S1>> as IntoResolvable<'a, S2, std::option::Option<T>, C>>
Here is my Rust code.
use juniper::{EmptySubscription,
FieldResult,
RootNode,
EmptyMutation};
use polars::prelude::DataFrame;
use std::collections::HashMap;
pub struct QueryRoot;
#[juniper::graphql_object]
impl QueryRoot {
fn fetch_data() -> FieldResult<HashMap<String, DataFrame>> {
// ...
let data: HashMap<String, DataFrame> = fetch_my_data();
Ok(data)
}
}
pub type Schema = RootNode<'static, QueryRoot, EmptyMutation, EmptySubscription>;
pub fn create_schema() -> Schema {
Schema::new(QueryRoot {}, EmptyMutation::new(), EmptySubscription::new())
}
I am guessing that juniper cannot recognize polar's Dataframe struct as graphqlobject. I am wondering if anyone knows how to fix this compile error. I also tried with serde_json, but no luck so far.

Related

Generic async trait that returns the implemented Struct

I'm getting blocked on what I think it's a simple problem. I'm still learning Rust, and I want to do the following:
I want to create an async trait (using async-trait) that will instantiate a DB connection instance and it will return the struct that is implementing that trait.
mongo.rs
#[async_trait]
pub trait DB {
async fn init<T, E>() -> Result<T, E>;
}
Then: favorites.rs (See the implementation of the DB trait down below)
use async_trait::async_trait;
use mongodb::Collection;
use rocket::form::FromForm;
use rocket::serde::ser::StdError;
use serde::{Deserialize, Serialize};
use std::error::Error;
use uuid::Uuid;
pub struct FavoritesDB {
collection: Collection<Favorite>,
}
#[derive(Debug)]
pub enum FavoritesError {
UnknownError(Box<dyn Error>),
}
// Conflicts with the one down below
// impl From<Box<dyn Error>> for FavoritesError {
// fn from(err: Box<dyn Error>) -> FavoritesError {
// FavoritesError::UnknownError(err)
// }
// }
impl From<Box<dyn StdError>> for FavoritesError {
fn from(err: Box<dyn StdError>) -> FavoritesError {
FavoritesError::UnknownError(err)
}
}
#[async_trait]
impl mongo::DB for FavoritesDB {
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError> {
let main_db = mongo::init::<Favorite>("Favorites").await?;
let db = FavoritesDB {
collection: main_db.collection,
};
Ok(db)
}
}
There are a list of problems with this:
1)
error[E0574]: expected struct, variant or union type, found type parameter `FavoritesDB`
--> src\db\favorites.rs:41:18
|
41 | let db = FavoritesDB {
| ^^^^^^^^^^^ not a struct, variant or union type
|
help: consider importing this struct instead
I've tried implementing From<Box<dyn tdError>> manually but it conflicts with what I have.
error[E0277]: `?` couldn't convert the error to `FavoritesError`
--> src\db\favorites.rs:40:65
|
40 | let main_db = mongo::init::<Favorite>("Favorites").await?;
| ^ the trait `From<Box<dyn StdError>>` is not implemented for `FavoritesError`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, Box<dyn StdError>>>` for `Result<FavoritesDB, FavoritesError>`
note: required by `from_residual`
--> C:\Users\asili\.rustup\toolchains\nightly-2021-11-15-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\ops\try_trait.rs:339:5
|
339 | fn from_residual(residual: R) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider further restricting this bound
|
39 | async fn init<FavoritesDB, FavoritesError + std::convert::From<std::boxed::Box<dyn std::error::Error>>>() -> Result<FavoritesDB, FavoritesError> {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some errors have detailed explanations: E0277, E0282, E0574.
For more information about an error, try `rustc --explain E0277`.
Just for more context, here's the DB struct and impl (Currently connecting to a local MongoDB) included in mongo.rs
pub struct Database<T> {
client: mongodb::Database,
pub collection: Collection<T>,
}
impl<T> Database<T> {
pub async fn init() -> Result<mongodb::Database, Box<dyn Error>> {
let mut client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
let db = client.database("rust-svelte");
return Ok(db);
}
}
pub async fn init<T>(collection: &str) -> Result<Database<T>, Box<dyn Error>> {
let client = Database::<T>::init().await?;
let collection = client.collection::<T>(collection);
let db = Database { client, collection };
Ok(db)
}
I've been searching for a few days over SO and the Rust community and my Google-Rust-Fu isn't good enough to spot what's the problem. Any ideas?
You've declared init to take 2 generic parameters: T and E.
This means that the code that calls init has to provide the concrete types to fill in those parameters. For example, if someone was using your library, it would be totally feasible for them to write init::<i64, ()>(), and your code should deal with that.
Because of that, when you define your impl DB for FavouritesDB, you write this:
async fn init<FavoritesDB, FavoritesError>() -> Result<FavoritesDB, FavoritesError>
This is no different to writing:
async fn init<T, E>() -> Result<T, E>
you've just given the type parameters different names that happen to match a struct that you're probably trying to use.
A better pattern might be an associated type. Instead of the caller deciding what the concrete types are (as is the case with generics), with associated types, the implementation of the trait on the type sets the type.
This is common with things like Iterator. Iterator has no generic parameters, but a single associated type Item. This is because it wouldn't make sense to be able to impl Iterator<String> for MyStruct and impl Iterator<i64> for MyStruct at the same time. Instead, we want to implement Iterator for a type once, and that implementation carries with it the definition of the types it expects.
So something like this (I've omitted the async-ness for brevity since it doesn't seem to be a factor here):
trait DB {
type InitOk;
type InitErr;
fn init() -> Result<Self::InitOk, Self::InitErr>;
}
impl Db for FavouritesDB {
type InitOk = FavouritesDB;
type InitErr = FavouritesError;
fn init() -> Result<Self::InitOk, Self::InitErr> {
// now you can reference FavouritesDB the struct, rather than the generic parameter
}
}
I'd also add you may want to not have the InitOk type, and just return Self, but that's up to you if you think you might want a struct to be able to create a different type.
For part 2, Rust assumes nothing (other than Sized) about generic parameters. If you want Rust to force a generic to have some property, you have to add a bound.
The compiler is telling you here that it can't use the ? operator to convert automatically, because it doesn't know that your error type has a From<Box<dyn Error>> implementation.
If you know that every error type is going to implement that, you can add it as a bound on the associated type, like this:
trait DB {
type InitOk;
type InitErr: From<Box<dyn Error>>;
// ...
}

Substrate Mutate throws an error when updating a member

I Have a substrate pallet implementation as follows
#[pallet::storage]
#[pallet::getter(fn get_payload)]
pub(super) type Payload<T: Config> = StorageMap<
_,
Blake2_128Concat,
Vec<u8>,
Messages<T>
>;
#[derive(Clone, Decode, Encode, Eq, PartialEq, Debug)]
pub struct Messages<T: Config> {
pub meta: Option<Vec<u8>>,
pub header: Option<Vec<u8>>,
}
#[pallet::weight(0)]
pub fn update(
origin: OriginFor<T>,
key: Vec<u8>,
header: Vec<u8>,
) -> DispatchResultWithPostInfo {
let origin_account = ensure_signed(origin)?;
let mut payload = Payload::<T>::get(key.clone());
match payload {
Some(mut val) => {
println!("{:?}",val.header);
<Payload<T>>::mutate(val, val.header=header)
},
None => println!("Not found")
}
and in the update function, when I tried to mutate it throws the following error
the trait `EncodeLike<Vec<u8>>` is not implemented for `Messages<T>`
The struct which corresponds to the Messages derived from Encode and Decode. Documentation is unclear on how to fix this. How to fix this issue?
The error message is correct, Messages<T> doesn't encode like a Vec<u8> and somewhere you try to use Mesages<T> where it only takes something that encodes like a Vec<u8>.
In <Payload<T>>::mutate call: the first argument must be something that encode like Vec<u8> and you give a variable of type Messages<T, it is an error. instead you probably wants to write <Payload<T>>::mutate(key.clone, ..)

Casting unknown type to know type

So I have this function
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = unsafe {transmute(n)};
echo(&x.to_string());
}
It does not compile because cannot transmute between types of different sizes.
What I want with this code is the following: I have a HashMap which contains rendering functions for different types. Every type that may be rendered must implement my interface Typeable, which basically only returns a constant type_id for the type (I've just come across a type_id in std, and wonder if I could use that instead...). And using that type_id I can then look up the correct render function in my HashMap. So my code ensures, that render_i32 is only called for i32. This works fine.
Now all of this would be really easy in C where I'd just cast the value under the pointer. But in rust it does not appear to be so easy. I don't get at the i32 value. How would I get that?
Edit: Alternative Solutions to my own approach that are less type-unsafe but solve the following requirement are also welcome: clients (who use this library) should be able to add their own rendering functions for their own types...
Note that the rendering functions are not supposed to be statically defined once: different rendering functions might be used for the same type depending for example on a language setting.
I still don't get why you didn't use the conventional trait-impl approach, it seems to do what you wanted, except that function pointers don't have any common data structure holding them (it's probably less cache-friendly than HashMap's approach)
Playground
use std::iter;
// lib
fn echo_windows(s: &String) {
println!("C:/Users> {}", s)
}
fn echo_linux(s: &String) {
println!("$ {}", s)
}
trait Renderable {
fn render(&self, echo: &dyn Fn(&String));
}
// client
struct ClientType {
ch: char,
len: usize,
}
impl Renderable for ClientType {
fn render(&self, echo: &dyn Fn(&String)) {
let to_echo: String = iter::repeat(self.ch)
.take(self.len)
.collect();
echo(&to_echo);
}
}
fn main() {
ClientType{ ch: '#', len: 5 }.render(&echo_windows); // output: C:/Users> #####
ClientType{ ch: '!', len: 3 }.render(&echo_linux); // output: $ !!!
}
Maybe you can use the Any trait for your purpose:
use std::any::Any;
pub trait Typeable {
...
fn as_any(&self) -> &dyn Any;
}
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = n.as_any().downcast_ref::<i32>().unwrap();
echo(&x.to_string());
}
The downcast_ref::<i32>() method returns an Option<&i32>, so you can also check if the downcast is valid. You can even do this in a generic way:
fn render<T:'static + std::fmt::Display>(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &T = n.as_any().downcast_ref::<T>().unwrap();
echo(&x.to_string());
}

RestPath trait for ArrayVec<> specialisation

I have my types defined and then I keep them in something like ArrayVec<[MyType, 16]> (from arrayvec crate) variables (members of a structure). Restson has the RestPath trait, allow us to define a path used to form a URI when performing a REST query.
However, due to the restriction that only local traits can be implemented for arbitrary types (AKA the orphan rule) I can't use it straightforwardly for ArrayVec<[MyType, 16]>.
I somehow overcame the problem by implementing the trait for ~specialization~ instantiation of the following enum:
enum ModelArray<T> {
Array(T)
}
and then decorticate the instance of T using:
type MyArray = ModelArray<ArrayVec<[MyType; 16]>>;
let encapsulated_array: MyArray = client.get(()).unwrap();
let ModelArray::<ArrayVec<[MyType; 16]>>::Array(myarray) = encapsulated_array;
This works as minimal example, but I suffer from the fact I cannot call client.get(()).unwrap() directly to the member of other structure.
I'm surprised full specialization of a generic type isn't treated by Rust as local type and orphan rule still applies. Why?
Are there other nice ways to overcome the limitation and would let me nicely assign result of Restson's get() into members of a structure?
Working code:
extern crate restson;
extern crate arrayvec;
extern crate serde_derive;
use restson::RestPath;
use arrayvec::ArrayVec;
#[derive(Deserialize, Debug)]
struct MyType {
id: u16,
data: u32,
}
struct DataModel {
my_data: ArrayVec<[MyType; 16]>
}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum ModelArray<T> {
Array(T)
}
impl RestPath<u16> for MyType {
fn get_path(id: u16) -> Result<String, Error> {
Ok(format!("data/MyType/{}", id))
}
}
impl RestPath<()> for ModelArray<ArrayVec<[MyType; 16]>> {
fn get_path(_: ()) -> Result<String, Error> {
Ok(String::from("data/MyType"))
}
}
use restson::RestClient;
pub fn load_data() {
let mut client = RestClient::new(&format!("http://{}", "localhost:8080")).unwrap();
let element: Type = client.get(24).unwrap();
println!("Room: {:?}", elementh);
type ModelArray = ModelArray<ArrayVec<[MyType; 16]>>;
let encapsulated: ModelArray = client.get(()).unwrap();
let ModelArray::<ArrayVec<[MyType; 16]>>::Array(elements) = encapsulated;
println!("Room: {:?}", elements[0]);
}
On Rust Playground (lack of restson crate wouldn't allow you to build)
Respective complete code: on GitHubGist

Understanding trait bound error in Diesel

I want to write a function that will insert a type into a database where the database connection parameter is generic, so that it can work on multiple backends.
I came up with the following function to insert an object using a generic connection:
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
If I don't include the SupportsDefaultKeyword constraint, the function will not compile. When calling it with a SqliteConnection as the connection parameter, I get the following error:
database::create_label(&db_conn, &label);
^^^^^^^^^^^^^^^^^^^^^^ the trait
'diesel::backend::SupportsDefaultKeyword' is not implemented for
'diesel::sqlite::Sqlite'
This would imply that inserting data with a SqliteConnection does not work. That's obviously not the case, and furthermore changing create_label such that it takes a SqliteConnection directly works just fine.
pub fn create_label(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(&label)
.into(schema::label::table)
.execute(connection);
}
Why is it that the generic function requires the SupportsDefaultKeyword constraint and the function taking SqliteConnection does not?
Here is a minimal example illustrating the problem. As per the comments, line 60 of main.rs will not compile with the error from above, whereas line 61 does compile:
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
mod schema {
table! {
labels {
id -> Integer,
name -> VarChar,
}
}
}
mod model {
use schema::labels;
#[derive(Debug, Identifiable, Insertable)]
#[table_name = "labels"]
pub struct Label {
pub id: i32,
pub name: String,
}
}
use diesel::ExecuteDsl;
use diesel::Connection;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
C::Backend: diesel::backend::Backend,
C::Backend: diesel::backend::SupportsDefaultKeyword,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn create_label_sqlite(connection: &SqliteConnection, label: &model::Label) {
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}
pub fn establish_connection() -> SqliteConnection {
let url = "test.db";
SqliteConnection::establish(&url).expect(&format!("Error connecting to {}", url))
}
fn main() {
let label = model::Label {
id: 1,
name: String::from("test"),
};
let conn = establish_connection();
create_label(&conn, &label); /* Does not compile */
create_label_sqlite(&conn, &label); /*Compiles */
}
[dependencies]
diesel = { version = "0.16.0", features = ["sqlite"] }
diesel_codegen = "0.16.0"
The Diesel function execute has multiple concrete implementations. The two that are relevant here are:
impl<'a, T, U, Op, Ret, Conn, DB> ExecuteDsl<Conn, DB> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
Conn: Connection<Backend = DB>,
DB: Backend + SupportsDefaultKeyword,
InsertStatement<T, &'a [U], Op, Ret>: ExecuteDsl<Conn>,
impl<'a, T, U, Op, Ret> ExecuteDsl<SqliteConnection> for BatchInsertStatement<T, &'a [U], Op, Ret>
where
InsertStatement<T, &'a U, Op, Ret>: ExecuteDsl<SqliteConnection>,
T: Copy,
Op: Copy,
Ret: Copy,
As you can see from these two, the implementation for SQLite is special-cased. I don't know enough about the details of Diesel to know why, but I'd guess that SQLite is missing the default keyword.
You can instead reformulate the requirements for any connection that works with that particular statement:
use diesel::query_builder::insert_statement::InsertStatement;
pub fn create_label<C>(connection: &C, label: &model::Label)
where
C: Connection,
for<'a> InsertStatement<schema::labels::table, &'a model::Label>: ExecuteDsl<C>,
{
diesel::insert(label)
.into(schema::labels::table)
.execute(connection)
.expect("nope");
}

Resources