obake
obake copied to clipboard
A way to use VersionOf and AnyVersion given reference to a struct
Currently it doesn't seem to be possible to determine current version of a struct given a reference to a struct (clone() is not an option for the struct I have).
I'm not sure I understand. Could you explain a little more? Maybe provide some code you'd like to work that doesn't?
My original problem is to detect the fact that configuration file need to be updated. The idea is to use the version of the file encoded in the vsn field of a top level struct and compare it with versions of all structs used in the configuration.
In order to do that I need to visit each node of the tree and collect versions of the struct involved.
It has been a challenge because obake doesn't support types with generics which means container types are not working out of the box.
Here is some code showing the configuration structure
#[derive(Deserialize, Serialize, Debug, Default, PartialEq, Validate)]
#[obake::versioned]
#[obake(derive(serde::Serialize, serde::Deserialize))]
#[obake(version("0.0.1"))]
pub struct ConfigFile {
pub vsn: u16,
#[serde(with = "humantime_serde")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub delay: Option<Duration>,
// I cannot use obake(version(xxx)) on container type
pub profiles: Vec<Profile>,
}
#[obake::versioned]
#[obake(derive(serde::Serialize, serde::Deserialize, PartialEq, Debug))]
#[obake(version("0.0.3"))]
#[derive(Validate, serde::Deserialize, serde::Serialize, PartialEq, Debug)]
pub struct Profile {
pub id: String,
pub name: String,
}
I wanted to write a helper function for each struct which would return the greatest version
impl ConfigFile {
pub fn vsn(self&) -> Version {
// p in map function bellow has type &Profile_v0_0_1
let profiles_vsn = self.profiles
.first()
.map(|p|
Version::parse(<VersionedProfile as VersionTagged<Profile>>::version_str(p))
.unwrap_or(Version::from("0.0.0")));
}
}
The code above fails
331 | ...on::parse(<VersionedProfile as VersionTagged<Profile>>::version_str(p)).unwrap_or(Version::from("0.0.0")));
| --------------------------------------------------------- ^ expected `&VersionedProfile`, found `&Profile_v0_0_1`
| |
| arguments to this function are incorrect
|
= note: expected reference `&VersionedProfile`
found reference `&Profile_v0_0_1`
pub fn vsn(&self) -> Version {
// p in map function bellow has type &Profile_v0_0_1
let profiles_vsn = self.profiles.first().map(|p|{
let v: obake::AnyVersion<Profile> = p.into();
Version::parse(v.version_str)
});
}
The above fails with
334 | let v: obake::AnyVersion<Profile> = p.into();
| ^^^^ the trait `From<&Profile_v0_0_1>` is not implemented for `VersionedProfile`
|
= note: required for `&Profile_v0_0_1` to implement `Into<VersionedProfile>`
pub fn vsn(&self) -> Version {
// p in map function bellow has type &Profile_v0_0_1
let profiles_vsn = self.profiles.first().map(|p| {
let v: obake::AnyVersion<Profile> = (*p).into();
Version::parse(v.version_str())
});
profiles_vsn.unwrap().unwrap()
}
The above fails
334 | let v: obake::AnyVersion<Profile> = (*p).into();
| ^^^^ ------ `*p` moved due to this method call
| |
| move occurs because `*p` has type `Profile_v0_0_1`, which does not implement the `Copy` trait
|
note: `into` takes ownership of the receiver `self`, which moves `*p`
As you can see there is no easy way to get the version. The following works
impl ConfigFile {
pub fn version(&self) -> Version {
Version::parse(Self::VERSION).expect("expect ConfigFile to be versioned")
}
pub fn vsn(&self) -> Version {
let inner = self.profiles.first().map(|p| p.vsn());
let inner = inner.unwrap();
if inner < self.version() {
self.version()
} else {
inner
}
}
}
impl Profile {
pub fn version(&self) -> Version {
Version::parse(Self::VERSION).expect("expect Profile to be versioned")
}
pub vsn(&self) -> Version {
self.version()
}
}