Check 2 keys for Vacant - rust

I am using the HashMap to store some of the value. Now I want to check whether HashMap contains keys, if not insert data else return HashMap is not empty. Below code explain that I am checking for product id, but I want to check for 2 keys product key and product url.
use std::collections::hash_map::Entry::Vacant;
pub struct Products<DB>
where
DB: DatabaseProvider,
{
database: DB,
products: HashMap<String, Product>,
}
pub struct Product {
pub product_id: String,
pub created: String,
pub product_description: String,
pub product_url: String,
}
pub async fn get_product_store_hashmap(&mut self) -> Result<()>
{
// calling the api here, once response is received, store in hashmap
let product = Product {
product_id: somedata,
created: somedata,
product_description: somedata,
product_url:somedata,
};
self.products.insert(product_id.clone(), product);
}
pub async fn insertProduct(&mut self, product:Product) -> Result<()> {
// How to check two keys are contains value.
if let Vacant(entry:Vacant::entry<(string,Product)) = self.product.entry(product_id) {
// insert the data
} else {
// retun no product id found
}
}

You can use map.contains_key to test.
Example:
fn insertProduct(map: &mut HashMap<String, Product>, product: Product) -> Result<()> {
if map.contains_key(product.product_id) || map.contains_key(product.product_url) {
Err()
} else {
map.insert(product.product_id, Product);
Ok(())
}
}

You can use Vacant and Occupied to check if key already exists or not and return result according using match
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Vacant, Occupied};
use std::io::ErrorKind;
fn main() {
let mut h: HashMap<&str, u8> = HashMap::new();
let value: &str = "a";
// h.entry(value).or_insert(1); // <- uncomment to get err result
let result = match h.entry(value) {
Vacant(entry) => {
entry.insert(1);
Ok(())
},
Occupied(_) => Err(ErrorKind::AlreadyExists)
};
println!("{:?}", result);
}
Playground

Related

How do I extract information about the type in a derive macro?

I am implementing a derive macro to reduce the amount of boilerplate I have to write for similar types.
I want the macro to operate on structs which have the following format:
#[derive(MyTrait)]
struct SomeStruct {
records: HashMap<Id, Record>
}
Calling the macro should generate an implementation like so:
impl MyTrait for SomeStruct {
fn foo(&self, id: Id) -> Record { ... }
}
So I understand how to generate the code using quote:
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: TokenStream) -> TokenStream {
...
let generated = quote!{
impl MyTrait for #struct_name {
fn foo(&self, id: #id_type) -> #record_type { ... }
}
}
...
}
But what is the best way to get #struct_name, #id_type and #record_type from the input token stream?
One way is to use the venial crate to parse the TokenStream.
use proc_macro2;
use quote::quote;
use venial;
#[proc_macro_derive(MyTrait)]
pub fn derive_answer_fn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Ensure it's deriving for a struct.
let s = match venial::parse_declaration(proc_macro2::TokenStream::from(item)) {
Ok(venial::Declaration::Struct(s)) => s,
Ok(_) => panic!("Can only derive this trait on a struct"),
Err(_) => panic!("Error parsing into valid Rust"),
};
let struct_name = s.name;
// Get the struct's first field.
let fields = s.fields;
let named_fields = match fields {
venial::StructFields::Named(named_fields) => named_fields,
_ => panic!("Expected a named field"),
};
let inners: Vec<(venial::NamedField, proc_macro2::Punct)> = named_fields.fields.inner;
if inners.len() != 1 {
panic!("Expected exactly one named field");
}
// Get the name and type of the first field.
let first_field_name = &inners[0].0.name;
let first_field_type = &inners[0].0.ty;
// Extract Id and Record from the type HashMap<Id, Record>
if first_field_type.tokens.len() != 6 {
panic!("Expected type T<R, S> for first named field");
}
let id = first_field_type.tokens[2].clone();
let record = first_field_type.tokens[4].clone();
// Implement MyTrait.
let generated = quote! {
impl MyTrait for #struct_name {
fn foo(&self, id: #id) -> #record { *self.#first_field_name.get(&id).unwrap() }
}
};
proc_macro::TokenStream::from(generated)
}

How to use map to filter value of field into an iterable of struct

I would like to filter into an iterable that contain a struct of Person with map() instruction but I don't know if it's possible on this way?
I would like to know which is the bast way to complete this task properly in Rust.
// This my structure of objet Person
#[derive(Debug, Clone)]
struct Person {
product_id: i64,
nom: String,
prenom: String,
email: Option<String>,
actif: bool,
}
// Main function which create a person and add this person into a HashMap<i64, Person>
fn main() {
// create list of person
let mut personnes = Personnes::new();
// Create one person
let person = Person {
product_id: 1,
nom: String::from("TestNom"),
prenom: String::from("TestPrenom"),
email: Some("test#mail.com".to_string()),
actif: true,
};
// Add person into my iterable
personnes.add(person);
// Add few persons...
// Imagine multiple Person and find person who actif = true
persons_actives(&personnes);
}
// Something like that :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes
.inner
.iter()
.map(|person.actif| person.actif == true)
.collect();
}
// But it's maybe impossible in this way ?
I tried :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes.inner.iter().filter(|person| person.actif == true).collect();
}
and :
fn persons_actives(personnes: &Personnes) {
let persons_actives = personnes.inner.iter().find(|person| person.actif == true).collect();
}
but i have the same error :
"no field actif on type &(&i64, &Person)"
You can iterate through your HashMap values, then filter() and collect() the result into a Vector.
use std::collections::HashMap;
#[derive(Debug)]
struct Person {
is_active: bool
//other params
}
fn main() {
// define your map
let mut map :HashMap<i32,Person> = HashMap::new();
// init a Person
let person: Person = Person{
is_active: true
// set other params
};
// insert it to map
map.insert(1,person);
// filter person.is_active == true
let active_persons : Vec<_> = map.values().filter(|person| person.is_active==true).collect();
// result
println!("{:?}",active_persons);
}
Now you have your desired result in active_persons.
Thanks for your reply,
I have tested this one :
let active_persons : Vec<_> = personnes.inner.values().filter(|&person| if_else!(person.actif, true, false)).collect();
with ternary-rs library for the if_else syntax.
Your right i shold have noticed the structiure of Personnes which is :
struct Personnes {
inner: HashMap<i64, Person>,
}
But i have keep your Vec<_> Structure for the active person.
I created two persons, one is active and the other not and it's worked !
Thanks a lot !

How do I use an impl function in the main function in rust?

I want to use a function in the main function in a rust program that I am building to help me learn rust and come up with an error: self value is a keyword only available in methods with a self parameterrustc(E0424). What can I fix in my code so that this error does not happen?
pub use crate::user_account::user_account;
use rand::Rng;
#[allow(dead_code)]
pub trait UserInfo {
fn user_info(&mut self);
fn acc_no(&mut self);
fn yes(self);
fn bank_new_user(self);
}
pub struct NewUser {
age: String,
new_user: String,
account: String,
account_number: i32,
routing_number: i32,
select: String,
}
impl UserInfo for NewUser {
fn user_info(&mut self) {
self.age = String::new();
self.new_user = String::new();
println!("What is your name?");
print!("Name: ");
std::io::stdin().read_line(&mut self.new_user);
println!(" ");
println!("Hello {}, What is your age? ", self.new_user);
std::io::stdin().read_line(&mut self.age);
let age2: String = self.age.trim().into();
}
fn acc_no(&mut self) {
println!(
"We will generate a new account number \
and routing number for you."
);
self.account_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account number is {}", self.account_number);
self.routing_number = rand::thread_rng().gen_range(10000000..99999999);
println!("Your account routing number is {}", self.routing_number);
}
fn yes(self) {
NewUser::user_info(&mut self);
NewUser::acc_no(&mut self);
}
//function I want to use in main.
fn bank_new_user(self) {
self.account = String::new();
println!("Would you like to make a new account with us today?");
loop {
println!(
" yes: continue to application, no: continue browsing , \
or exit: to exit"
);
self.account.clear();
std::io::stdin()
.read_line(&mut self.account)
.expect("please type yes, no or exit.");
let account = self.account.trim();
match account {
"yes" => {
self.yes();
break;
}
"no" => {
println!("You do not need an account to continue browsing.");
println!("Have a wonderful day and thank you for considering Mars Banking!");
break;
}
"exit" => {
println!(
"Thank you for choosing Mars Banking for your banking needs!\
Have a wonderful day!"
);
break;
}
_ => {
println!("Error! Enter yes, no, or exit.")
}
}
}
}
}
pub mod new_user;
mod settings;
mod user_account;
pub use crate::settings::settings;
pub use crate::user_account::user_account;
use new_user::NewUser;
use new_user::UserInfo;
fn main() {
loop{
let mut select = String::new();
println!("Welcome to Mars Banking!");
println!("What would you like to do today?");
println!("Create a new account: 1\nLogin: 2\nSettings: 3\nExit: 4");
select.clear();
std::io::stdin().read_line(&mut select);
let select = select.trim();
match select {
//Here is where the error happens.
"1" => NewUser::bank_new_user(self),
"2" => user_account(),
"3" => settings(),
"4" => break,
_ => {}
}
}
}
The conventional pattern for this sort of constructor is a static method that doesn't take a self argument, like this:
impl NewUser {
fn bank_new_user() {
let mut new_user = NewUser { /* initialize the fields */ };
// Edit or use new_user as necessary
}
}
you can see an example of this here, in the methods defined for Point:
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
notice how niether origin nor new take self as an argument.

How to split and loop over strings inside a match in rust

I'm just starting to learn Rust and built a sample api project using Rocket and rusqlite. I want to get all of the items in my database through a get endpoint. My table has an id field and a string which is a list of ids seperated by ,. After getting this back I want to split this string and go back to the database for each entry and then create a struct for it. I'm having trouble figuring this out in a functional way for rust. Does anyone have any advice for how to proceed? I want to manipulate the data myself and avoid orm tools like diseil.
data.rs
use serde::{Deserialize, Serialize};
#[derive(Serialize)]
pub struct Item {
pub id: i32,
pub time_tp_prepare: i32,
pub name: String
}
#[derive(Serialize)]
pub struct Table {
pub id: i32,
pub items: Vec<Item>
}
#[derive(Serialize)]
pub struct StatusMessage {
pub message: String
}
database.rs
use rusqlite::Result;
pub struct ItemData {
pub id: i32,
pub time_tp_prepare: i32,
pub name: String
}
pub struct TableData {
pub id: i32,
pub itemIds: String
}
pub fn setup_db() -> Result<String, String>{
let db_connection = match rusqlite::Connection::open("data.sqlite") {
Ok(connection) => connection,
Err(_) => {
return Err("Cannot connect to database.".into());
}
};
match db_connection
.execute(
"create table if not exists item (
id integer primary key,
name varchar(64) not null,
preperation_time integer not null
);
create table if not exists restaurant_table (
id integer primary key,
items varchar(64) not null",
[]
) {
Ok(success) => Ok("Successfully created database tables.".into()),
Err(_) => return Err("Could not run create table sql".into())
}
}
main.rs
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use rocket_contrib::json::Json;
use rusqlite::Result;
mod database;
mod data;
#[get("/api/get-all-tables-v1")]
fn get_all_tables() -> Result<Json<data::Table>, String> {
let db_connection = match rusqlite::Connection::open("data.sqlite") {
Ok(connection) => connection,
Err(_) => {
return Err("Cannot connect to database.".into());
}
};
let mut statement = match db_connection.prepare("select id, items from restaurant_table;") {
Ok(statement) => statement,
Err(_) => return Err("Failed to prepare query.".into())
};
let results = statement.query_map([], |row| {
Ok(database::TableData {
id: row.get(0)?,
itemIds: row.get(1)?
})
});
match results {
Ok(rows) => {
///// This is where I'm stuck on what to do next //////
let collection: rusqlite::Result<Vec<data::Item>> = rows.collect();
match collection {
Ok(items) => Ok(Json(data::Table { items })),
Err(_) => Err("Could not collect items.".into())
}
},
Err(_) => Err("Failed to fetch items.".into())
}
}
fn main() {
match database::setup_db() {
Ok(_) => luanch_server(),
Err(error) => eprintln!("Program failed to start because of Error {}.", error)
}
}
fn luanch_server() {
rocket::ignite().mount("/", routes![get_all_tables]).launch();
}
I figured it out and I will post the code here in case anyone finds it in the future. It is not great code and I ended up not using it but it was a good learning experience.
for row in rows.into_iter().flatten() {
let ids = row.itemIds.split(",");
for id in ids {
let mut item_statement = db_connection
.prepare("select * from item where id = :id;")
.expect("Failed to prepare query.");
let mut item_rows = item_statement
.query_named(rusqlite::named_params!{ ":id": id })
.expect("Select item statement failed");
while let Some(item_row) = item_rows
.next()
.expect("Row Failed.") {
let item = data::Item {
id: row.get(0),
time_to_prepare: row.get(1),
name: row.get(2)
};
if !tables.contains(item.id) {
tables.insert(item);
}
}

How to access struct properties dynamically?

I am a Rust beginner and was wondering how to access a struct's fields dynamically:
use std::collections::HashMap;
struct User {
email: String,
name: String,
}
impl User {
fn new(attributes: &HashMap<String,String>) -> User {
let mut model = User {
email: "",
name: "",
};
for (attr_name,attr_value) in attributes {
// assign value "attr_value" to attribute "attr_name"
// no glue how to do this
// in php would be: $model->{$attr_name} = $attr_value;
model.*attr_name *= attr_value;
}
model;
}
}
fn main() {
let mut map: HashMap::new();
map.insert("email",String::from("foo#bar.de"));
map.insert("name",String::from("John doe"));
user_model = User::new(&map);
println!("{:?}",user_model);
}
How it is possible to initialize a struct by given HashMap?
Unless you change your User to contain a HashMap then Rust can't do that kind of "magic" (or it will require some proc macro usage, which is not beginner friendly).
Instead you can use a match, and match all the keys and update the User fields:
for (attr_name, attr_value) in attributes {
match attr_name {
"email" => model.email = attr_value.clone(),
"name" => model.name = attr_value.clone(),
_ => {}
}
}
However, instead of having empty Strings, I'd suggest using Option<String>.
struct User {
email: Option<String>,
name: Option<String>,
}
Then you can simplify your whole new method to just:
fn new(attributes: &HashMap<String, String>) -> User {
User {
email: attributes.get("email").cloned(),
name: attributes.get("name").cloned(),
}
}
Since you have some mixed String and &'static str usage, along with Debug not being implemented. Then here is the complete example:
use std::collections::HashMap;
#[derive(Debug)]
struct User {
email: Option<String>,
name: Option<String>,
}
impl User {
fn new(attributes: &HashMap<String, String>) -> User {
User {
email: attributes.get("email").cloned(),
name: attributes.get("name").cloned(),
}
}
}
fn main() {
let mut map = HashMap::new();
map.insert(String::from("email"), String::from("foo#bar.de"));
map.insert(String::from("name"), String::from("John doe"));
let user_model = User::new(&map);
println!("{:?}", user_model);
}

Resources