How to access JS object properties in Wasm (Rust)? - rust

I'm using wasm bindgen and I have following function :
#[wasm_bindgen]
pub fn obj(o: &JsValue){
console::log_1(o);
}
and in js I call this function obj({name: "john"});
and it works fine, but when i try to console::log_1(o.name);
it gives error unknown field pointing at name

JsValue does not have a field name. To get this field you have to declare the JS object.
Variant 1
Add serde to your dependencies:
serde = "^1.0.101"
serde_derive = "^1.0.101"
Rust code:
extern crate serde;
#[derive(Serialize, Deserialize)]
pub struct User {
pub name: String,
}
#[wasm_bindgen]
pub fn obj(o: &JsValue){
let user: User = o.into_serde().unwrap();
console::log_1(user.name);
}
Variant 2
Another way is to use wasm-bindgen directly but I never used it. It should work like this I think:
#[wasm_bindgen]
pub struct User {
pub name: String,
}
#[wasm_bindgen]
pub fn obj(o: User){
console::log_1(o.name);
}

Related

Serialize a remote struct with private String

I need to serialize a struct from a remote crate and all of the fields in the struct are private. There are getter's implemented in the remote struct to get those values. I am following this guidance and got it to work just fine for primitive types. However, I'm struggling with how to implement this for non-primitive types (ie: String) that the remote struct contains.
Below is a small piece of what I've implemented to frame the issue. The DataWrapper struct simply wraps Data, where Data is the remote struct.
#[derive(Serialize)]
pub struct DataWrapper {
#[serde(with = "DataDef")]
pub data: Data,
}
#[derive(Serialize)]
#[serde(remote = "remote_crate::data::Data")]
pub struct DataDef {
#[serde(getter = "Data::image_id")] // This works
image_id: u16,
#[serde(getter = "Data::description")] // This does not work
description: String,
}
The error I get when compiling this is
#[derive(Serialize)]
^^^^^^^^^ expected struct `std::string::String`, found `&str`
This makes sense, since the getter Data::description returns &str rather than a String. But, I'm not seeing a way in my code to coerce this so the compiler is happy.
If I change DataDef::description to be &str instead of String, then I have to implement lifetimes. But, when I do that, the compiler then says the remote "struct takes 0 lifetime arguments".
Appreciate any tips on how I can serialize this and other non-primitive types.
One approach you could do, so that you have full control of the serialization. Is to have the data wrapper be a copy of the struct fields you need, instead of the entire remote struct. Then you can implement From<remote_crate::data::Data> for DataWrapper and use serde without trying to coerce types.
#[derive(Serialize)]
pub struct Data {
image_id: u16,
description: String,
}
impl From<remote_crate::data::Data> for Data {
fn from(val: remote_crate::data::Data) -> Self {
Self {
image_id: val.image_id,
description: val.description.to_string(),
}
}
}
// Then you could use it like this:
// let my_data: Data = data.into();
I couldn't get it to work with an &str, but if you're OK with an allocation, you can write a custom getter:
mod local {
use super::remote::RemoteData;
use serde::{Deserialize, Serialize};
fn get_owned_description(rd: &RemoteData) -> String {
rd.description().into()
}
#[derive(Serialize)]
#[serde(remote = "RemoteData")]
pub struct DataDef {
#[serde(getter = "get_owned_description")]
description: String,
}
}
mod remote {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct RemoteData {
description: String,
}
impl RemoteData {
pub fn description(&self) -> &str {
&self.description
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e9c7c0b069d7e16b6faac2fa2b840c72

How to store Rust object without Default in GObject

I am basically following the gtk-rs book to create a simple app. I have the following rust struct, which I want to display in a ListView. This struct is basically read-only and should not be edited, so I do not want to expose it's members as properties and I also do not want to create it from GTK itself (it's created inside the rust-only business logic). I would like to expose the entire struct as one property.
pub struct Video {
pub number: u32,
pub encoding: Encoding,
pub chapters: Vec<Chapter>,
}
pub struct Chapter {
pub number: u32,
pub filename: String,
pub thumbnail_filename: Option<String>,
pub preview_video_filename: Option<String>,
}
How can I wrap this inside a GObject? I'm trying to create the following wrapper:
mod imp {
#[derive(Default)]
pub struct VideoObject {
pub data: Rc<RefCell<Video>>,
}
#[glib::object_subclass]
impl ObjectSubclass for VideoObject {
const NAME: &'static str = "VideoObject";
type Type = super::VideoObject;
}
impl ObjectImpl for VideoObject {}
}
pub const VIDEO_PROPERTY: &str = "video";
glib::wrapper! {
pub struct VideoObject(ObjectSubclass<imp::VideoObject>);
}
impl VideoObject {
pub fn new(video: Video) -> Self {
Object::new(&[]).expect("Could not create VideoObject")
}
}
This fails because Video does not implement Default. I thought about wrapping it inside an Option like data: Rc<RefCell<Option<Video>>>, which compiles. Unfortunately, then I'm stuck on how to set it as a property.
I guess one way would be to use a ParamSpecPointer, box and leak Video and then pass it before, but this strucks me as unsafe and ugly...
Is there a better way to do it?
You can directly access the "imp" object and set fields manually. No need to transform everything into GObject properties:
impl VideoObject {
pub fn new(video: Video) -> Self {
let object = Object::new(&[]).unwrap();
let imp = imp::VideoObject::from_instance(&object);
imp.data.replace(Some(video));
object
}
}

Import a library struct in tests suite

i'm trying to acces a struct from the lib i'm creating to perform some unit tests.
here is a sample of the code:
src/token_deserializer.rs
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Token {
pub payment_data: PaymentData,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentData {
pub data: String,
pub signature: String,
pub header: String,
pub version: String,
}
src/lib.rs
use serde_json::from_str;
use token_deserializer::Token;
pub mod token_deserializer;
pub fn deserialize_token(token: &str) -> Token{
let object: Token = from_str(token).expect("JSON was not well-formatted");
return object;
}
tests/mytest.rs
#[cfg(test)]
mod tests {
use std::fs::File;
use apple_pay_token_decryptor::deserialize_token;
use apple_pay_token_decryptor::Token;
#[test]
fn test_token_deserialization(){
let mut file = File::open("text.json").unwrap();
let mut data = String::new();
let object: Token = deserialize_token(&data);
}
}
Here i can't manage to import Token for my test => error[E0603]: struct `Token` is private
Do i need to import it in lib.rs or something else ?
Inside your test, you refer to apple_pay_token_decryptor::Token. You may think it is resolved to the pub struct Token, but in fact, it is not. Instead, it refers to this use in src/lib.rs:
use token_deserializer::Token;
And while both the struct and the token_deserializer module are public - this import is private (it is not pub use, i.e. a reexport). But it's still there. And thus the compiler complains about you using a private type.
To fix, that you either have to reexport Token - i.e. make that pub use token_deserializer::Token;, or fix the test to import the struct directly from token_deserializer:
use apple_pay_token_decryptor::token_deserializer::Token;

How can I only generate #[derive(Clone)] by bindgen?

I am using bindgen to generate code with the following config.
let mut builder = bindgen::Builder::default()
.clang_arg("-std=c++11")
.clang_arg("-x")
.clang_arg("c++")
.clang_arg("-Wno-pragma-once-outside-header")
.layout_tests(false)
.derive_copy(true)
.enable_cxx_namespaces()
.default_enum_style(EnumVariation::Rust {
non_exhaustive: false,
});
However I find that the generated code like
#[derive(Debug, Copy, Clone)]
pub struct RawCppPtr {
pub ptr: root::DB::RawVoidPtr,
pub type_: root::DB::RawCppPtrType,
}
I wonder if there are any ways that I can only generate #[derive(Clone)] without generating [derive(Copy)]?

Diesel's attribute `primary_key` is currently unknown to the compiler

I'm trying to use the #[primarykey()] macro in Diesel but am getting an error that it is unknown. From what I have found, adding #![feature(primary_key)] should solve the issue, but it doesn't.
lib.rs
#[macro_use]
extern crate diesel;
extern crate dotenv;
pub mod schema;
pub mod models;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;
pub fn establish_connection() -> PgConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
PgConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
models.rs
#![feature(primary_key)]
extern crate diesel;
#[derive(Queryable, Debug)]
#[primary_key(user_id)]
pub struct User {
pub user_id: i32,
pub email: String,
pub password: String,
pub bio: String,
pub verified: bool,
}
I've also tried adding #![feature(primary_key)] to the top of lib.rs without any luck.
Using Rust 1.26.0-nightly (80785a547 2018-03-30)
The primary_key attribute is only applicable when deriving Identifiable:
#[macro_use]
extern crate diesel;
mod schema {
table! {
users (user_id) {
user_id -> Int4,
email -> Text,
}
}
#[derive(Debug, Identifiable)]
#[primary_key(email)]
pub struct User {
pub user_id: i32,
pub email: String,
}
}
fn main() {}
I believe you could also just change your primary key in your schema definition (users (user_id)).

Resources