So let's say I have the following example:
struct Client {
email: String,
phone: String,
details: String
}
fn main() {
let mut clients: Vec<Client> = Vec::new();
clients.push(Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "custom details".to_string(),
});
clients.push(Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "other details".to_string(),
});
clients.push(Client {
email: "james#gmail.com".to_string(),
phone: "9876543210".to_string(),
details: "test".to_string(),
});
}
What would be the best (Rust idiomatic) way to count partial duplicates in this vector by checking email and phone in the Client? For instance - in the example above one duplicate would be found.
One option is to create a HashSet with (email, phone) for each client. As HashSet keep only unique elements, we can get the number of duplicates elements with the difference of the number of elements in clients an in the set:
use std::collections::HashMap;
struct Client {
email: String,
phone: String,
details: String,
}
fn main() {
let mut clients: Vec<Client> = Vec::new();
clients.push(Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "custom details".to_string(),
});
clients.push(Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "other details".to_string(),
});
clients.push(Client {
email: "james#gmail.com".to_string(),
phone: "9876543210".to_string(),
details: "test".to_string(),
});
// use as_str to get a `&str` from a String to avoid copying the string
let uniques: HashMap<_, _> = clients.iter()
.map(|c| (c.email.as_str(), c.phone.as_str()))
.collect();
let num_dups = clients.len() - uniques.len();
assert_eq!(1, num_dups);
}
You will often want to know which were the duplicates. In that case, a straight-forward extension of the HashSet solution can be used:
use std::collections::HashMap;
struct Client {
email: String,
phone: String,
details: String,
}
impl Client {
fn key<'a>(&'a self) -> (&'a str, &'a str) {
(&self.email, &self.phone)
}
}
fn main() {
let clients = vec![Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "custom details".to_string(),
},
Client {
email: "john#gmail.com".to_string(),
phone: "0123456789".to_string(),
details: "other details".to_string(),
},
Client {
email: "james#gmail.com".to_string(),
phone: "9876543210".to_string(),
details: "test".to_string(),
}];
let mut keyed = HashMap::new();
for c in &clients {
keyed.entry(c.key()).or_insert(vec![]).push(c)
}
for (k, v) in &keyed {
if v.len() > 1 {
println!("Key {:?} has {} duplicates!", k, v.len());
}
}
}
Note the use of a method on Client to keep the keying logic in one place, the use of vec![] to reduce the amount of explicit mutability needed, and there's no need to specify the type of clients as it can be inferred.
Related
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: >k::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)
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();
}
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))
}
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());
}
enum StudentNames {
Vlad,
Andrei,
Dragos,
}
struct Student {
student_name: StudentNames,
locker: Option<i32>,
}
fn main() {
let students = vec![
Student {
student_name: StudentNames::Vlad,
locker: Some(1),
},
Student {
student_name: StudentNames::Andrei,
locker: Some(2),
},
Student {
student_name: StudentNames::Dragos,
locker: None,
},
];
for student in students {
}
}
I am trying to return the student name by matching the locker variant for each student. Using a match against Some/None didn't work, when referencing by students.Student::locker. What am I doing wrong?