Convert all u64 values in JSON to string - rust

I want to write a function in Rust that can parse a JSON string and output a modified JSON with all the number fields converted to string. All other fields should be unchanged. Structure of the JSON string is not known in advance. Here's the desired function signature:
pub fn convert_all_numbers_to_string(json: &str) -> String
Examples:
Input:
{"foo": 18446744073709551615}
Output:
{"foo": "18446744073709551615"}
Input:
{
"some": [{
"deeply": {
"nested": [{
"path": 123
}]
}
}]
}
Output:
{
"some": [{
"deeply": {
"nested": [{
"path": "123"
}]
}
}]
}

Here you go:
pub fn convert_all_ints_to_strings(json: &str) -> Result<String, serde_json::Error> {
use serde_json::Value;
fn convert_recursively(json: &mut Value) {
match json {
Value::Number(n) if n.is_u64() || n.is_i64() => {
*json = Value::String(n.to_string());
}
Value::Array(a) => a.iter_mut().for_each(convert_recursively),
Value::Object(o) => o.values_mut().for_each(convert_recursively),
_ => (),
}
}
serde_json::from_str(json).map(|mut v: Value| {
convert_recursively(&mut v);
v.to_string()
})
}
Used in a main():
fn main() {
let inp = r#"
{
"some": [{
"deeply": {
"nested": [{
"path": 123
}]
}
}]
}
"#;
let outp = convert_all_ints_to_strings(inp).unwrap();
println!("{}", outp);
}
{"some":[{"deeply":{"nested":[{"path":"123"}]}}]}

Related

Detect that user presses spacebar GTK4

I am using gtk4-rs to make a GUI app for a music player. I would like to hit spacebar to play/pause songs, so I wanted to detect that a user hit spacebar in the app
I tried using EventControllerKey to detect the keypress, e.g. something like this
use gtk::prelude::*;
fn main() {
let application =
gtk::Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default());
application.connect_activate(build_ui);
application.run();
}
fn build_ui(application: &gtk::Application) {
let window = gtk::ApplicationWindow::new(application);
window.set_title(Some("First GTK Program"));
window.set_default_size(350, 70);
let evk = gtk::EventControllerKey::new(); //builder().
evk.connect_key_pressed(|a, b, c, d| {
println!("{:?} {} {} {}", a, b, c, d);
gtk::Inhibit(false)
});
window.add_controller(&evk);
let button = gtk::Button::with_label("Click me!");
window.set_child(Some(&button));
window.show();
}
And this intercepts many letters that I type, but not spacebar (which seems to try to perform an action of clicking the button, which I also do not want)
sample output, no spacebars
EventControllerKey { inner: TypedObjectRef { inner: 0x55e61ba114c0, type: GtkEventControllerKey } } Key { name: Some("f"), is_lower: true, is_upper: false } 41 (empty)
EventControllerKey { inner: TypedObjectRef { inner: 0x55e61ba114c0, type: GtkEventControllerKey } } Key { name: Some("s"), is_lower: true, is_upper: false } 39 (empty)
EventControllerKey { inner: TypedObjectRef { inner: 0x55e61ba114c0, type: GtkEventControllerKey } } Key { name: Some("a"), is_lower: true, is_upper: false } 38 (empty)
EventControllerKey { inner: TypedObjectRef { inner: 0x55e61ba114c0, type: GtkEventControllerKey } } Key { name: Some("d"), is_lower: true, is_upper: false } 40 (empty)
EventControllerKey { inner: TypedObjectRef { inner: 0x55e61ba114c0, type: GtkEventControllerKey } } Key { name: Some("f"), is_lower: true, is_upper: false } 41 (empty)

Erreur 400 bad Request Actix

I'm a beginner with rust and i have some troube testing my api.
I try to post some data via curl but keep having error 400 bad Request.
curl -X POST -H "Content-Type: application/json" -d '{"id": "20", "first_name": "foo1", "last_name": "bar1", "address": "Bourghelles", "mail": "foo1#bar.com", "phone_number": "0620399062"}' http://127.0.0.1:8080/clients -v
pub async fn create_clients(pool: web::Data<SqlPool>, req: web::Json<Client>) -> Result<HttpResponse, Error> {
Ok(web::block(move || clients::create(pool, req))
.await
.map(|client| HttpResponse::Ok().json(client))
.map_err(|_| HttpResponse::InternalServerError())?)
}
#[derive(Queryable, Insertable, Serialize, Deserialize)]
pub struct Client {
pub id: i32,
pub first_name: String,
pub last_name: String,
pub address: String,
pub mail: String,
pub phone_number : String,
}
#[derive(Insertable, Serialize, Deserialize)]
#[table_name = "clients"]
pub struct NewClient<'a>{
pub id: i32,
pub first_name : &'a str,
pub last_name : &'a str,
pub address : &'a str,
pub mail : &'a str,
pub phone_number : &'a str,
}
pub fn create(pool: web::Data<SqlPool>, req: web::Json<Client>) -> Result<Client, diesel::result::Error>{
let new_client = NewClient {
id: req.id,
first_name: &req.first_name,
last_name: &req.last_name,
address: &req.address,
mail: &req.mail,
phone_number: &req.phone_number,
};
format!{"Hello world from create"};
let conn = pool.get().unwrap();
use crate::schema::clients::dsl::*;
let res = diesel::insert_into(clients).values(&new_client).execute(&conn)?;
return show(pool, 1);
}
async fn main() -> std::io::Result<()> {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let manager = ConnectionManager::<MysqlConnection>::new(database_url);
let pool = r2d2::Pool::builder()
.build(manager)
.expect("Failed to create pool.");
HttpServer::new(move || {
App::new()
.data(pool.clone())
.route("/clients", web::post().to(api::client::create_clients))
// .service(api::client::hello_world)
// .service(api::client::all_clients)
// .service(api::client::find_clients)
// .service(api::client::update_clients)
// .service(api::client::delete_clients)
// .service(api::house::all_houses)
// .service(api::house::find_houses)
// .service(api::house::create_houses)
// .service(api::house::update_houses)
// .service(api::house::delete_houses)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
I suspect that this is because you input "id": "20" and not "id": 20
The following test passes:
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Client {
pub id: i32,
pub first_name: String,
pub last_name: String,
pub address: String,
pub mail: String,
pub phone_number: String,
}
#[test]
fn deserialize_test() {
use serde_json::json;
let client_fail_json = json!(
{
"id": "20",
"first_name": "foo1",
"last_name": "bar1",
"address": "Bourghelles",
"mail": "foo1#bar.com",
"phone_number": "0620399062"
}
);
let client_fail: Result<Client, _> = serde_json::from_value(client_fail_json);
assert!(client_fail.is_err());
let client_json = json!(
{
"id": 20,
"first_name": "foo1",
"last_name": "bar1",
"address": "Bourghelles",
"mail": "foo1#bar.com",
"phone_number": "0620399062"
}
);
let client: Result<Client, _> = serde_json::from_value(client_json);
client.unwrap();
}

Passing complex types to anchor rpc fails

I modified the basic tutorial example to pass structs but when i run "anchor test" it fails with TypeError: Blob.encode[data] requires (length 4) Buffer as src
I could not find anything on the internet when I searched for this error.
The code is attached. Please let me know if I got it wrong somehow.
Thanks,
Lib.rs
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_1 {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
pub fn update(ctx: Context<Update>, data: u64) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
pub fn add_signatory(ctx: Context<Update>, signatory: Signatory) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
//my_account.data = data;
my_account.signatories.push(signatory);
Ok(())
}
pub fn add_signatories(ctx: Context<Update>, signatories: Vec<Signatory>) -> Result<()> {
let my_account = &mut ctx.accounts.my_account;
//my_account.data = data;
my_account.signatories = signatories;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init, payer = user, space = 100)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {
#[account(mut)]
pub my_account: Account<'info, MyAccount>,
}
#[account]
pub struct MyAccount {
pub data: u64,
pub project_id: u64,
pub project_name: Vec<u8>,
pub signatories: Vec<Signatory>,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Signatory {
pub name: Vec<u8>,
pub public_key: Pubkey,
}
basic-1.js
const assert = require("assert");
const anchor = require("#project-serum/anchor");
const { SystemProgram } = anchor.web3;
describe("basic-1", () => {
// Use a local provider.
const provider = anchor.AnchorProvider.local();
// Configure the client to use the local cluster.
anchor.setProvider(provider);
it("Creates and initializes an account in a single atomic transaction (simplified)", async () => {
// #region code-simplified
// The program to execute.
const program = anchor.workspace.Basic1;
// The Account to create.
const myAccount = anchor.web3.Keypair.generate();
// Create the new account and initialize it with the program.
// #region code-simplified
await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [myAccount],
});
// #endregion code-simplified
// Fetch the newly created account from the cluster.
const account = await program.account.myAccount.fetch(myAccount.publicKey);
// Check it's state was initialized.
assert.ok(account.data.eq(new anchor.BN(1234)));
// Store the account for the next test.
_myAccount = myAccount;
});
it("Updates a previously created account", async () => {
const myAccount = _myAccount;
// #region update-test
// The program to execute.
const program = anchor.workspace.Basic1;
// Invoke the update rpc.
await program.rpc.update(new anchor.BN(4321), {
accounts: {
myAccount: myAccount.publicKey,
},
});
// Fetch the newly updated account.
const account = await program.account.myAccount.fetch(myAccount.publicKey);
// Check it's state was mutated.
assert.ok(account.data.eq(new anchor.BN(4321)));
// #endregion update-test
});
it("add a single signatory", async () => {
const myAccount = _myAccount;
// #region update-test
// The program to execute.
const program = anchor.workspace.Basic1;
const pubkey1 = anchor.web3.Keypair.generate();
const pubkey2 = anchor.web3.Keypair.generate();
const signatory1 =
{
name: "matt",
public_key: pubkey1,
};
// Invoke the update rpc.
await program.rpc.addSignatory(signatory1, {
accounts: {
myAccount: myAccount.publicKey,
},
});
// Fetch the newly updated account.
const account = await program.account.myAccount.fetch(myAccount.publicKey);
// Check it's state was mutated.
assert.ok(account.data.eq(new anchor.BN(4321)));
// #endregion update-test
});
it("add multiple signatories", async () => {
const myAccount = _myAccount;
// #region update-test
// The program to execute.
const program = anchor.workspace.Basic1;
const pubkey1 = anchor.web3.Keypair.generate();
const pubkey2 = anchor.web3.Keypair.generate();
const signatories1 = [
{
name: "matt",
public_key: pubkey1,
},
{
name: "smith",
public_key: pubkey2,
},
];
// Invoke the update rpc.
await program.rpc.addSignatories(signatories1, {
accounts: {
myAccount: myAccount.publicKey,
},
});
// Fetch the newly updated account.
const account = await program.account.myAccount.fetch(myAccount.publicKey);
// Check it's state was mutated.
assert.ok(account.data.eq(new anchor.BN(4321)));
// #endregion update-test
});
});
anchor test output
abcd#abcd:~/code/anchor/examples/tutorial/basic-1$ anchor test
BPF SDK: /home/abcd/.local/share/solana/install/releases/1.10.8/solana-release/bin/sdk/bpf
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
Finished release [optimized] target(s) in 0.17s
cargo-build-bpf child: /home/abcd/.local/share/solana/install/releases/1.10.8/solana-release/bin/sdk/bpf/dependencies/bpf-tools/llvm/bin/llvm-readelf --dyn-symbols /home/abcd/code/anchor/examples/tutorial/basic-1/target/deploy/basic_1.so
To deploy this program:
$ solana program deploy /home/abcd/code/anchor/examples/tutorial/basic-1/target/deploy/basic_1.so
The program address will default to this keypair (override with --program-id):
/home/abcd/code/anchor/examples/tutorial/basic-1/target/deploy/basic_1-keypair.json
Found a 'test' script in the Anchor.toml. Running it as a test suite!
Running test suite: "/home/abcd/code/anchor/examples/tutorial/basic-1/Anchor.toml"
yarn run v1.22.18
$ /home/abcd/code/anchor/examples/tutorial/node_modules/.bin/mocha -t 1000000 tests/
basic-1
✔ Creates and initializes an account in a single atomic transaction (simplified) (250ms)
✔ Updates a previously created account (411ms)
1) add a single signatory
2) add multiple signatories
2 passing (698ms)
2 failing
1) basic-1
add a single signatory:
TypeError: Blob.encode[data] requires (length 4) Buffer as src
at Blob.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:2321:13)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at WrappedLayout.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/#project-serum/borsh/dist/lib/index.js:67:28)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at BorshInstructionCoder._encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/coder/borsh/instruction.js:91:28)
at BorshInstructionCoder.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/coder/borsh/instruction.js:76:21)
at /home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/index.js:41:100
at ix (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/instruction.js:50:23)
at txFn (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/transaction.js:16:20)
at Object.rpc [as addSignatory] (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/rpc.js:9:24)
at Context.<anonymous> (tests/basic-1.js:82:23)
at processImmediate (node:internal/timers:466:21)
2) basic-1
add multiple signatories:
TypeError: Blob.encode[data] requires (length 4) Buffer as src
at Blob.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:2321:13)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at WrappedLayout.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/#project-serum/borsh/dist/lib/index.js:67:28)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at /home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1113:25
at Array.reduce (<anonymous>)
at Sequence.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1112:22)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at WrappedLayout.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/#project-serum/borsh/dist/lib/index.js:67:28)
at Structure.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/node_modules/buffer-layout/lib/Layout.js:1263:26)
at BorshInstructionCoder._encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/coder/borsh/instruction.js:91:28)
at BorshInstructionCoder.encode (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/coder/borsh/instruction.js:76:21)
at /home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/index.js:41:100
at ix (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/instruction.js:50:23)
at txFn (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/transaction.js:16:20)
at Object.rpc [as addSignatories] (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/rpc.js:9:24)
at Context.<anonymous> (tests/basic-1.js:120:23)
at processImmediate (node:internal/timers:466:21)
error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
basic_1.json
{
"version": "0.1.0",
"name": "basic_1",
"instructions": [
{
"name": "initialize",
"accounts": [
{
"name": "myAccount",
"isMut": true,
"isSigner": true
},
{
"name": "user",
"isMut": true,
"isSigner": true
},
{
"name": "systemProgram",
"isMut": false,
"isSigner": false
}
],
"args": [
{
"name": "data",
"type": "u64"
}
]
},
{
"name": "update",
"accounts": [
{
"name": "myAccount",
"isMut": true,
"isSigner": false
}
],
"args": [
{
"name": "data",
"type": "u64"
}
]
},
{
"name": "addSignatory",
"accounts": [
{
"name": "myAccount",
"isMut": true,
"isSigner": false
}
],
"args": [
{
"name": "signatory",
"type": {
"defined": "Signatory"
}
}
]
},
{
"name": "addSignatories",
"accounts": [
{
"name": "myAccount",
"isMut": true,
"isSigner": false
}
],
"args": [
{
"name": "signatories",
"type": {
"vec": {
"defined": "Signatory"
}
}
}
]
}
],
"accounts": [
{
"name": "MyAccount",
"type": {
"kind": "struct",
"fields": [
{
"name": "data",
"type": "u64"
},
{
"name": "projectId",
"type": "u64"
},
{
"name": "projectName",
"type": "bytes"
},
{
"name": "signatories",
"type": {
"vec": {
"defined": "Signatory"
}
}
}
]
}
}
],
"types": [
{
"name": "Signatory",
"type": {
"kind": "struct",
"fields": [
{
"name": "name",
"type": "bytes"
},
{
"name": "publicKey",
"type": "publicKey"
}
]
}
}
],
"metadata": {
"address": "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
}
}
UPDATE 1
After changing the type to String as suggested by Jon C below - the test runs.
However if I call the addSignatory method twice it fails with the error "Error: AnchorError caused by account: my_account. Error Code: AccountDidNotSerialize. Error Number: 3004. Error Message: Failed to serialize the account.".
Updated basic-1.js
it("add a single signatory", async () => {
const myAccount = _myAccount;
// #region update-test
// The program to execute.
const program = anchor.workspace.Basic1;
const pubkey1 = anchor.web3.Keypair.generate();
const pubkey2 = anchor.web3.Keypair.generate();
const signatory1 =
{
name: "matt",
public_key: pubkey1,
};
// Invoke the update rpc.
await program.rpc.addSignatory(signatory1, {
accounts: {
myAccount: myAccount.publicKey,
},
});
// Fetch the newly updated account.
const account = await program.account.myAccount.fetch(myAccount.publicKey);
//assert.ok(account.signatories.len().eq(new anchor.BN(1)));
assert.equal(account.signatories.length, 1);
const signatory2 =
{
name: "smith",
public_key: pubkey2,
};
// Invoke the update rpc.
await program.rpc.addSignatory(signatory2, {
accounts: {
myAccount: myAccount.publicKey,
},
});
// Fetch the newly updated account.
const account2 = await program.account.myAccount.fetch(myAccount.publicKey);
//assert.ok(account.signatories.len().eq(new anchor.BN(1)));
assert.equal(account2.signatories.length, 2);
// Check it's state was mutated.
assert.ok(account.data.eq(new anchor.BN(4321)));
// #endregion update-test
});
anchor test output
1) basic-1
add a single signatory:
Error: AnchorError caused by account: my_account. Error Code: AccountDidNotSerialize. Error Number: 3004. Error Message: Failed to serialize the account.
at Function.parse (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/error.js:138:20)
at translateError (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/error.js:224:37)
at Object.rpc [as addSignatory] (/home/abcd/code/anchor/examples/tutorial/node_modules/#project-serum/anchor/dist/cjs/program/namespace/rpc.js:18:53)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async Context.<anonymous> (tests/basic-1.js:101:5)
Update 2
So I figured some more things out, Thanks #JonC for the hint which helped me move forward.
To call the rust methods you need to change the method name to
snakecase in javascript so add_signatory will be called as addSignatory from JS.
You need to add quotes around strings in JS before passing as arguments to the rust method. So you need to ensure that string variables are padded with '"' in the JS code.
I am still not sure why this will not work but sending public keys as Strings from JS to Rust causes anchor test to fail with "Account cannot be serialized" error like described here. Still trying to figure this out.
The name field in your signatory is defined as a Vec<u8> on the backend, but you're passing in a string from the frontend. Try updating the Signatory type to:
#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
pub struct Signatory {
pub name: String,
pub public_key: Pubkey,
}
That way, the serializer will know to properly encode the name as a string, and not as bytes.

diesel: inner_join forward query

I am learning to use inner_join for forward query
pub async fn hello(pool: web::Data<DbPool>) -> HttpResult {
let conn = pool.get().map_err(ErrorInternalServerError)?;
let data: Vec<(models::Book, models::User)> = books::table
.inner_join(users::table)
.load::<(models::Book, models::User)>(&conn)
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(data))
}
I get the correct data
[
[{
"id": 1,
"name": "js",
"user_id": 1
}, {
"id": 1,
"username": "admin"
}],
[{
"id": 2,
"name": "rust",
"user_id": 1
}, {
"id": 1,
"username": "admin"
}]
]
But the structure of this json data is not what I want
I want this nested structure
[{
"id": 1,
"name": "js",
"user": {
"id": 1,
"username": "admin"
}
}, {
"id": 2,
"name": "rust",
"user": {
"id": 1,
"username": "admin"
}
}]
I don't know how to convert, is there a best practice
More information
schema:
table! {
books (id) {
id -> Unsigned<Bigint>,
name -> Varchar,
user_id -> Unsigned<Bigint>,
}
}
table! {
users (id) {
id -> Unsigned<Bigint>,
username -> Varchar,
}
}
joinable!(books -> users (user_id));
allow_tables_to_appear_in_same_query!(
books,
users,
);
models:
#[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, Debug, Clone)]
#[belongs_to(User, foreign_key = "user_id")]
#[table_name = "books"]
pub struct Book {
#[serde(skip_deserializing)]
pub id: PK,
pub name: String,
pub user_id: PK,
}
#[derive(Identifiable, Queryable, Serialize, Deserialize, Debug, Clone)]
#[table_name = "users"]
pub struct User {
pub id: PK,
pub username: String,
}
diesel version
diesel = { version = "1.4.4", features = ["mysql", "r2d2", "chrono", "numeric"] }
your real problem is with serde crate
if you have data type like this (String, i32, ...) serde will create array in output see following code
fn main() {
let val = ("Hello", 123, 2.5);
let result = serde_json::to_string(&val).unwrap();
println!("{}", result); // ["Hello",123,2.5]
}
so if you wanna solve it you can do something like this
first make your custom model
struct User {
id: i32,
username: String,
}
struct ResponseModel {
id: i32,
name: String,
user: User,
}
now implement From trait for ResponseModel
impl From<(models::Book, models::User)> for ResponseModel {
fn from(values: (models::Book, models::User)) -> Self {
Self {
id: values.0.id,
name: values.0.name,
user: User {
id: values.1.id,
username: values.1.username,
},
}
}
}
now change hello fn like this
pub async fn hello(pool: web::Data<DbPool>) -> HttpResult {
let conn = pool.get().map_err(ErrorInternalServerError)?;
let data: Vec<ResponseModel> = books::table
.inner_join(users::table)
.load::<(models::Book, models::User)>(&conn)
.map(|x| x.into_iter().map(ResponseModel::from).collect())
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(data))
}
An easy way is to use #[serde(flatten)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BookWithUser<B, U> {
#[serde(flatten)]
book: B,
user: U,
}
impl<B, U> From<(B, U)> for BookWithUser<B, U> {
fn from((b, u): (B, U)) -> Self {
Self { book: b, user: u }
}
}
#[derive(Identifiable, Associations, Queryable, Serialize, Deserialize, Debug, Clone)]
#[belongs_to(models::user::User, foreign_key = "user_id")]
#[table_name = "books"]
pub struct Book {
...
}
then use like this
#[get("/with_user")]
pub async fn list_with_user(pool: web::Data<DbPool>) -> HttpResult {
use schema::*;
let res = web::block(move || -> Result<_, DbError> {
let conn = pool.get()?;
let res = books::table
.inner_join(users::table)
.load::<(mods::book::Book, mods::user::User)>(&conn)
.map(|x| x.into_iter().map(mods::book::BookWithUser::from))?
.collect::<Vec<_>>();
Ok(res)
})
.await?
.map_err(ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(res))
}

How to handle multiple enums from Binary data type in single match statement in Rust

My main objective is to write the buy() function here. The function can receive either Cars or Bike enum in a Binary format.
Following is my code:
use cosmwasm_std::{from_binary, to_binary, Binary};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
enum Cars {
Audi { id: u32, number_plate: u32 },
Bmw { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
enum Bikes {
Yamaha { id: u32, number_plate: u32 },
Bajaj { id: u32, number_plate: u32 },
}
fn buy(prop: Binary) {
match from_binary(&prop).unwrap() {
Cars::Audi { id, number_plate } => {
println!("{:?}", (id, number_plate));
}
_ => {}
}
match from_binary(&prop).unwrap() {
Bikes::Yamaha { id, number_plate } => {
println!("{:?}", (id, number_plate));
}
_ => {}
}
}
fn main() {
let x = Cars::Audi {
id: 0,
number_plate: 1,
};
let y = Bikes::Yamaha {
id: 0,
number_plate: 1,
};
buy(to_binary(&x).unwrap());
buy(to_binary(&y).unwrap());
}
When I am trying to do it this way, its giving me this error:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseErr
{ target_type: "some_testing::Bikes", msg: "unknown variant `Audi`, expected
`Yamaha` or `Bajaj`" }', src/main.rs:23:30
What is the appropriate way of doing this?
The easiest solution would be to just merge the two traits to sidestep the issue entirely. However, I'm guessing that may not be an option for you or it would not make much conceptual sense for the types.
The way to resolve your issue is to match Ok(_) as well as the enum variable instead of unwrapping. Unwrapping will cause it to panic if it was unable to parse the data to one of your enum variants. You still will need to have two if statements though since the two values being parsed are unrelated types. Here I use if let statements instead of match statements to make it a little more concise since it only matched a single variant.
fn buy(prop: Binary) {
if let Ok(Cars::Audi { id, number_plate }) = from_binary(&prop) {
println!("{:?}", (id, number_plate));
}
if let Ok(Bikes::Yamaha { id, number_plate }) = from_binary(&prop) {
println!("{:?}", (id, number_plate));
}
}
Usually you would go with serdes Untagged enum (see documentation). You can see that it works in buy_json.
However in this library it fails with internal error: entered unreachable code. I've checked the code and found the following statement:
/// Unsupported. We rely on typed deserialization methods, even if a JSON
/// has enough type information to detect types in many cases.
///
/// See https://serde.rs/impl-deserialize.html to learn more about the differentiation
/// between deserialize_{type} and deserialize_any.
You can workaround that with something similar to buy_workaround.
use cosmwasm_std::{from_binary, to_binary, Binary, StdError};
use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_string};
#[derive(Serialize, Deserialize)]
enum Cars {
Audi { id: u32, number_plate: u32 },
Bmw { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
enum Bikes {
Yamaha { id: u32, number_plate: u32 },
Bajaj { id: u32, number_plate: u32 },
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum Wrappy {
Car(Cars),
Bike(Bikes),
}
fn buy(prop: Binary) {
match from_binary::<Wrappy>(&prop) {
Ok(ok) => match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Binary Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Binary Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
},
Err(e) => println!("{:?}", e),
}
}
fn buy_workaround(prop: Binary) {
if let Some(ok) = get_wrapper(&prop) {
match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Binary Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Binary Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
}
}
}
fn get_wrapper(prop: &Binary) -> Option<Wrappy> {
match from_binary::<Cars>(prop) {
Ok(car) => Some(Wrappy::Car(car)),
Err(StdError::ParseErr { .. }) => match from_binary::<Bikes>(prop) {
Ok(bike) => Some(Wrappy::Bike(bike)),
Err(_) => None,
},
_ => None,
}
}
fn buy_json(prop: &str) {
match from_str::<Wrappy>(prop) {
Ok(ok) => match ok {
Wrappy::Car(car) => match car {
Cars::Audi { id, number_plate } => println!("Json Audi {:?}", (id, number_plate)),
_ => {
println!("Other car")
}
},
Wrappy::Bike(bike) => match bike {
Bikes::Yamaha { id, number_plate } => {
println!("Json Yamaha {:?}", (id, number_plate))
}
_ => {
println!("Other bike")
}
},
},
Err(e) => println!("{:?}", e),
}
}
fn main() {
let x = Cars::Audi {
id: 0,
number_plate: 1,
};
let y = Bikes::Yamaha {
id: 0,
number_plate: 1,
};
let json = to_string(&x).unwrap();
println!("{}", json);
buy_json(&json);
buy_json(&to_string(&y).unwrap());
println!(
"Binary format: {:?}",
String::from_utf8_lossy(to_binary(&x).unwrap().as_slice())
);
buy_workaround(to_binary(&x).unwrap());
buy_workaround(to_binary(&y).unwrap());
buy(to_binary(&x).unwrap());
buy(to_binary(&y).unwrap());
}

Resources