How to define route as a prop to a component | yew-route - rust

I have the following routes defined.
#[derive(Routable, PartialEq, Debug, Clone)]
pub enum Route {
#[at("/")]
Index,
#[at("about")]
About,
#[at("/contact")]
Contact,
#[at("/portfolio")]
Portfolio,
#[at("*")]
NotFound,
}
I want to pass a route, Route::Index for instance to the following component but has an error as shown in the comment
#[derive(Properties, PartialEq)]
pub struct IconProps {
pub route: Route,
pub alt: String,
pub icon_name: String,
}
#[function_component(Icon)]
pub fn icon(props: &IconProps) -> Html {
let src = format!("assets/icons/{}.svg", props.icon_name);
html! {
<div class={classes!("p-2", "mx-2", "my-1", "bg-green-100", "inline-block")}>
<Link<Route> classes={classes!("w-10", "h-10")} to={props.route}>
^^^^^^^^^^^^^^
// Diagnostics:
// 1. cannot move out of `props.route` which is behind a shared reference
// move occurs because `props.route` has type `Route`, which does not implement the `Copy` trait
<img
src={src}
alt={props.alt.to_owned()}
class={classes!("w-10", "h-10")}
/>
</Link<Route>>
</div>
}
}
So, how to take the route as a prop?

Well, the solution was somewhat obvious I guess. Just add Copy trait to enum
#[derive(Routable, PartialEq, Debug, Clone, Copy)]
pub enum Route {
#[at("/")]
Index,
#[at("about")]
About,
#[at("/contact")]
Contact,
#[at("/portfolio")]
Portfolio,
#[at("*")]
NotFound,
}

Related

How to solve partial move error when accessing struct fields?

I'm using a field of the struct ClientError (implemented in the Solana client):
#[derive(Error, Debug)]
#[error("{kind}")]
pub struct ClientError {
pub request: Option<rpc_request::RpcRequest>,
#[source]
pub kind: ClientErrorKind,
}
Below is how it's used in my code:
Err(err: ClientError) => {
if let MyError(myError) = err.kind {
...
}
return Err(custom_error(err, ...));
}
Which gives me the error:
use of partially moved value: `err`
partial move occurs because value has type `CustomType`, which does not implement the `Copy` trait
How can I solve this partial move error? I have seen this SO answer: Struct with partial move errors. But suggests using &'static str which isn't valid for me & I don't want to use Rc<Mutex<..>> either.

How to use enums as a query parameter with Axum

I have an enum similar to:
#[derive(Debug, Deserialize, Serialize)]
pub enum Gender {
Male,
Female,
NonBinary
}
and I a have an axum handler function which expects it to be passed in as Query(gender): Query<Gender>
But when I request the endpoint like this:
http://localhost:3000/greet?gender=Male
The page returns the following error message:
Failed to deserialize query string: invalid type: map, expected enum Gender
If I switch the params to use a struct instead everything works fine, which is why I know this is specifically an issue with enum serialization.
Any ideas how to fix?
You should use a struct as your query parameter type:
#[derive(Debug, Deserialize, Serialize)]
pub enum Gender {
Male,
Female,
NonBinary
}
#[derive(Debug, Deserialize)]
pub struct GreetParams {
gender: Gender,
}
async fn greet_handler(Query(params): Query<GreetParams>) {}
The reason you get an error is because ?gender=Male is parsed as a mapping of key-value pairs. Therefore, you need to use a mapped data structure that has named keys: something like BTreeMap<String, _>, HashMap<String, _>, or a struct.
If I switch the params to use a struct instead everything works fine, which is why I know this is specifically an issue with enum serialization. Any ideas how to fix?
There is nothing to fix; this is working as intended.

How to parse a string to case which doesn't match the type in 3rd party crate?

So this is some code from a 3rd party library:
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Ord, PartialOrd)]
pub enum ViewingMetric {
RatingPercentage,
Rating
}
and what I would like is to parse a string like "rating_percentage" without being able to add #[serde(rename_all = "...")] into 3rd party code. Can I somehow specify the renaming during invocation of serde_json::from_str? Or must I add another 3rd party library which handles conversions between casings?
There is a guide on how to derive Serde for remote creates, in where you can customize whatever you need:
Would be something like:
#[derive(Serialize, Deserialize)]
#[serde(remote = "OtherCrate::ViewingMetric", rename_all = "snake_case")]
enum ViwingMetricSerde {
RatingPercentage,
Rating
}
Important, you would have to implement From/Into from your new type to the remote one From<ViwingMetricSerde> for ViwingMetric.
Then from your code, to actually get the original type:
#[derive(Serialize, Deserialize)]
pub struct MyStruct {
#[serde(with = "ViwingMetricSerde")]
metric: ViwingMetris
}

Deserialize two different types into the same one

I have a simple structure that contains a resources field. I would like my resources to always be Urls.
In the actual file that I am trying to deserialize, resources can either be URLs or Path.
Here is my structure:
pub struct Myfile {
pub resources: Vec<Resource>,
}
pub type Resource = Url;
I would like to use serde to:
Try to deserialize each Resource using the implementation from the url crate.
If it fails, try to deserialize each one of them into a Path and then use url::from_*_path() to get a Url.
I am trying to adapt the string or map,map and structure examples but I am struggling to understand where to even start.
Since my end result will by a Url, the examples seem to show that I should be implementing Deserialize for Url. But I still need to current implementation. My Resource is an alias so I can't implement Deserialize for it.
Is there any simple way to deserialize both Paths and Urls into Urls?
PS: I will probably go for the answer proposed but after reading through this post, I tried the following which seems to work too:
#[derive(Clone, Debug)]
pub struct Resource(Url);
impl<'de> Deserialize<'de> for Resource {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
let s = String::deserialize(deserializer)?;
if let path = Path::new(&s) {
if path.is_file() {
Ok(Resource(Url::from_file_path(path).unwrap()))
} else {
Ok(Resource(Url::from_directory_path(path).unwrap()))
}
} else {
Url::deserialize(s.into_deserializer()).map(|x| Resource(x))
}
}
}
But is less convenient to work with than regular Urls.
I think the key to doing this with reasonable effort is to realize that serde::Deserialize for Url is also just cooking with water, i.e. just expecting a string and calling Url::parse on it.
So it's time to deploy my favourite serde trick: deserialize to a struct that serde can handle easily:
#[derive(Deserialize)]
pub struct MyfileDeserialize {
pub resources: Vec<String>,
}
Tell serde that it should get the struct you finally want from that easily handlable struct:
#[derive(Deserialize, Debug)]
#[serde(try_from = "MyfileDeserialize")]
pub struct Myfile {
pub resources: Vec<Resource>,
}
Finally, you just need to define how to turn MyfileDeserialize into Myfile.
impl TryFrom<MyfileDeserialize> for Myfile {
type Error = &'static str; // TODO: replace with anyhow or a proper error type
fn try_from(t: MyfileDeserialize) -> Result<Self, &'static str> {
Ok(Self {
resources: t
.resources
.into_iter()
.map(|url| {
if let Ok(url) = Url::parse(&url) {
return Ok(url);
};
if let Ok(url) = Url::from_file_path(url) {
return Ok(url);
};
// try more from_*_path here.
Err("Can't as url or path")
})
.collect::<Result<Vec<_>, _>>()?,
})
}
}
Playground
Edit regarding your PS:
If you are willing to mess around with manually implementing deserializer traits and functions, I suppose you do have the option of completely getting rid of any wrapper structs or mediating TryFrom types: add a #[serde(deserialize_with = …)] to your resources, and in there, first do a <Vec<String>>::deserialize(de)?, and then turn that into a Vec<Url> as usual.
Playground

Substrate storage declaration

I'm working on a pallet and after switching to the newest node template it stopped building. Here is declaration
#[derive(Encode, Decode, Clone)]
pub struct VendorData<T :Config>
{
pub vendor : VendorId,
pub owner_account_id : T::AccountId,
}
impl<T:Config> Default for VendorData<T> {
fn default() -> Self {
Self {
vendor : Default::default(),
owner_account_id : Default::default(),
}
}
}
#[pallet::storage]
pub(super) type Vendors<T: Config> = StorageMap<_, Blake2_128Concat, VendorId, VendorData<T>, ValueQuery>;
The cargo build error:
error[E0277]: the trait bound `VendorData<T>: TypeInfo` is not satisfied
...
#[pallet::storage]
^^^^^^^ the trait `TypeInfo` is not implemented for `VendorData<T>`
I've tried declaring struct in a different ways, for example by adding TypeInfo:
#[derive(Encode, Decode, Default, Eq, PartialEq, TypeInfo)]
but every time another errors arise, like this:
error: cannot find macro `format` in this scope
--> /home/psu/.cargo/git/checkouts/substrate-7e08433d4c370a21/352c46a/primitives/consensus/aura/src/lib.rs:50:3
|
50 | app_crypto!(ed25519, AURA);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: consider importing this macro:
scale_info::prelude::format
= note: this error originates in the macro `$crate::app_crypto_public_common_if_std` (in Nightly builds, run with -Z macro-backtrace for more info)
There is another storage item which does not cause errors
pub type Cid = Vec<u8>;
#[pallet::storage]
pub(super) type VendorModels<T: Config> = StorageMap<_, Blake2_128Concat, VendorId, Vec<Cid>, ValueQuery>;
So it seems it's only about storing VendorData struct. Please assist, after hours of experiments and googling I'm lost
UPDATE: after adding TypeInfo to all storage structs declaration the pallet is compiled but a lot of errors like below arised:
error: cannot find macro `format` in this scope
error: cannot find macro `vec` in this scope
You will need to either make sure that all your generic types implement TypeInfo or skip the types explicitly.
Based on this example code in Substrate you could write:
#[derive(Encode, Decode, Clone, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct VendorData<T :Config>
{
pub vendor : VendorId,
pub owner_account_id : T::AccountId,
}
Or alternatively the following should work:
#[derive(Encode, Decode, Clone, TypeInfo)]
pub struct VendorData<AccountId>
{
pub vendor : VendorId,
pub owner_account_id : AccountId,
}

Resources