I am trying to update the vector, this my code so far.
use near_sdk::collections::Map;
use near_sdk::collections::Vector;
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
pub struct ProfileDetails {
profile_tags: Map<String, ProductList>,
}
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize, Debug)]
pub struct Product {
product_name: String,
product_details: String,
}
#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
pub struct ProductList {
products: Vector<Product>,
}
#[near_bindgen]
impl ProfileDetails {
pub fn set_profile(&mut self, product_name: String, product_details: String) {
let account_id = String::from("amiyatulu.test");
println!("{}", product_name);
let p = Product {
product_name,
product_details,
};
let id = account_id.clone().into_bytes();
let mut id_products = ProductList {
products: Vector::new(id),
};
id_products.products.push(&p);
self.profile_tags.insert(&account_id, &id_products);
}
pub fn push_product_to_profile(&mut self, product_name: String, product_details: String) {
let account_id = String::from("amiyatulu.test");
let p = Product {
product_name,
product_details,
};
let my_products_option = self.profile_tags.get(&account_id);
match my_products_option {
Some(mut my_products) => {
my_products.products.push(&p); //It doesn't update the state
self.profile_tags.insert(&account_id, &my_products);
println!("Hello myproducts push");
}
None => println!("Can't get the profile tag"),
}
}
The problem is this statement doesn't update the state of the blockchain.
my_products.products.push(&p); //It doesn't update the state
So, I have inserted the vector again in this statement.
self.profile_tags.insert(&account_id, &my_products);
Is this the right way? Does it cause repeated storage of ProductList's product vector? How to get the vector state and update it?
Complete code here
In Rust, the result of match
is frequently immutable. So in your example:
Some(mut my_products) => {
although having mut
, does not mean it's mutating (changing) the value that's ultimately associated with self
.
So your approach with self…insert
is correct. This is a common approach in Rust generally. There are exceptions to this rule in Rust that may highlight what's going on.
For instance, in the standard collection HashMap
there's get_mut
(as opposed to get
) which returns a mutable reference. You can see in the Rust docs that this returns Option<&mut V>
while the HashMap's get method returns Option<&V>
.
Now we can look at the docs for the NEAR Vector get
method to see that get
returns Option<T>
which is immutable.
So again, your approach looks good here to me.
In terms of modifying the Vector, I believe you're likely going to want to use the Set collection instead, but I dislike it when StackOverflow questions change the subject like that. So below I've provided a test you can add that demonstrates some of the Vector usage I believe you're looking for.
#[test]
pub fn modify_update_product_list() {
let context = get_context(vec![], false);
testing_env!(context);
// first product
let product_one = Product {
product_name: "toothbrush".to_string(),
product_details: "electric and sleek".to_string(),
};
let product_two = Product {
product_name: "soap".to_string(),
product_details: "smells like teen spirit".to_string(),
};
let product_three = Product {
product_name: "shampoo".to_string(),
product_details: "natural mint oils".to_string(),
};
let mut profile_detail = ProfileDetails::default();
let mut product_list = ProductList {
products: Vector::new(b"products".to_vec()),
};
println!("Corner Store: adding toothbrush and soap…");
product_list.products.push(&product_one);
product_list.products.push(&product_two);
let shop_name = "Corner Store".to_string();
profile_detail.profile_tags.insert(&shop_name, &product_list);
// Say we come back later to the saved state
// Get the product list from Corner Store
match profile_detail.profile_tags.get(&shop_name) {
Some(mut current_product_list) => {
// add shampoo product
println!("Corner Store: Inserting shampoo…");
current_product_list.products.push(&product_three);
profile_detail.profile_tags.insert(&shop_name, ¤t_product_list);
},
None => {
// if this were outside of tests we'd have:
// env::log(b"Didn't find shop name");
println!("Didn't find shop name")
}
}
let product_list = profile_detail.profile_tags.get(&shop_name);
assert!(product_list.is_some()); // can also have is_none()
println!("Corner Store: Listing products");
for product in product_list.unwrap().products.iter() {
println!("Corner Store: Product {:?}", product);
}
// Sold out of soap, remove it
println!("Corner Store: Removing index 1, which should be soap");
// Consider using something like Set
// https://docs.rs/near-sdk/0.10.0/near_sdk/collections/struct.Set.html
let current_product_list = profile_detail.profile_tags.get(&shop_name);
assert!(current_product_list.is_some(), "Couldn't find shop");
// Remove and get object at index 1
let soap = current_product_list.unwrap().products.swap_remove(1);
println!("Corner Store: Removed index 1 which was {:?}", soap);
}