Serialize a remote struct with private String - rust

I need to serialize a struct from a remote crate and all of the fields in the struct are private. There are getter's implemented in the remote struct to get those values. I am following this guidance and got it to work just fine for primitive types. However, I'm struggling with how to implement this for non-primitive types (ie: String) that the remote struct contains.
Below is a small piece of what I've implemented to frame the issue. The DataWrapper struct simply wraps Data, where Data is the remote struct.
#[derive(Serialize)]
pub struct DataWrapper {
#[serde(with = "DataDef")]
pub data: Data,
}
#[derive(Serialize)]
#[serde(remote = "remote_crate::data::Data")]
pub struct DataDef {
#[serde(getter = "Data::image_id")] // This works
image_id: u16,
#[serde(getter = "Data::description")] // This does not work
description: String,
}
The error I get when compiling this is
#[derive(Serialize)]
^^^^^^^^^ expected struct `std::string::String`, found `&str`
This makes sense, since the getter Data::description returns &str rather than a String. But, I'm not seeing a way in my code to coerce this so the compiler is happy.
If I change DataDef::description to be &str instead of String, then I have to implement lifetimes. But, when I do that, the compiler then says the remote "struct takes 0 lifetime arguments".
Appreciate any tips on how I can serialize this and other non-primitive types.

One approach you could do, so that you have full control of the serialization. Is to have the data wrapper be a copy of the struct fields you need, instead of the entire remote struct. Then you can implement From<remote_crate::data::Data> for DataWrapper and use serde without trying to coerce types.
#[derive(Serialize)]
pub struct Data {
image_id: u16,
description: String,
}
impl From<remote_crate::data::Data> for Data {
fn from(val: remote_crate::data::Data) -> Self {
Self {
image_id: val.image_id,
description: val.description.to_string(),
}
}
}
// Then you could use it like this:
// let my_data: Data = data.into();

I couldn't get it to work with an &str, but if you're OK with an allocation, you can write a custom getter:
mod local {
use super::remote::RemoteData;
use serde::{Deserialize, Serialize};
fn get_owned_description(rd: &RemoteData) -> String {
rd.description().into()
}
#[derive(Serialize)]
#[serde(remote = "RemoteData")]
pub struct DataDef {
#[serde(getter = "get_owned_description")]
description: String,
}
}
mod remote {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct RemoteData {
description: String,
}
impl RemoteData {
pub fn description(&self) -> &str {
&self.description
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e9c7c0b069d7e16b6faac2fa2b840c72

Related

Retrieve constants from inner in Newtype pattern

Is there a way to retrieve public constants from the inner struct in the Newtype pattern?
Say I am using a struct from another crate like this
#[derive(PartialEq)]
pub struct Version(u32);
impl Version {
pub const v0: Self = Self(0);
pub const v1: Self = Self(1);
}
Now, in my code I need to wrap it with a newtype pattern. So far so good.
#[derive(PartialEq)]
pub struct MyVersion(Version);
I want to get the inner constant using the wrapper type along the lines of MyVersion::v0. Is this doable?
Rust Playground link
Thanks to #Sven for suggesting a solution that works with stable Rust.
A way out of this is to implement a trait with an associated type. The end usage is awkward (due to E0223), but at least re-defining constants and associated methods is unnecessary.
// original crate
#[derive(Debug, PartialEq)]
pub struct Version(u32);
impl Version {
pub const V0: Self = Self(0);
pub const V1: Self = Self(1);
}
// another crate
use std::ops::Deref;
#[derive(Debug, PartialEq)]
pub struct MyVersion(Version);
impl Deref for MyVersion {
type Target = Version;
fn deref(&self) -> &Self::Target {
&self.0
}
}
trait Versioning {
type V;
}
impl Versioning for MyVersion {
type V = Version;
}
fn main() {
let myv0 = MyVersion(Version::V0);
assert_eq!(*myv0, <MyVersion as Versioning>::V::V0);
}
The custom trait will probably not be needed when inherent associated type lands in the stable channel.

Serialize complicated data structure

I'm having trouble serializing the following struct. I have narrowed it down to, that the problem lies within the variable objects, containing trait structs within a HashMap. I'll try to explain my circumstances:
I have the following main struct, which i'm interested in obtaining data from:
#[derive(Serialize)]
pub struct System {
pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
pub paths: Vec<Path>,
pub loops: Vec<Loop>,
pub physics: Physics,
pub run_limit_loops: u32,
pub run_limit_paths: u32,
pub error_tol_loops: f64,
pub error_tol_paths: f64,
pub unknown_flow_outlets: u32,
pub unknown_pumps_id: u32,
pub system: u32,
}
The kind of structs which are contained within objects are as the following:
pub struct Outlet {
// Variables found in all structs contained within 'objects'
pub Q: f64,
pub hL: f64,
pub ob: u32,
pub id: u32,
pub active: bool,
pub system: u32,
pub con_Q: HashMap<u32, f64>,
// Variables found only in this struct.
pub p_dyn: f64,
pub flow_specified: String,
pub submerged: bool,
}
pub struct Pipe {
// Variables found in all structs contained within 'objects'
pub Q: f64,
pub hL: f64,
pub ob: u32,
pub id: u32,
pub active: bool,
pub system: u32,
pub con_Q: HashMap<u32, f64>,
// Variables found only in this struct.
pub Re: f64,
pub f: f64,
}
These struct has some variables which are only used within themselves, and some variables which are common for all structs contained within objects (p_dyn is only used within Outlet, while Q is used for all structs contained within objects) To get these variables, get-function are defined, both within the local struct, and by the trait CommonObject. All i am interested in, is serializing objects in order to get all the variables in a string format, both the common ones, and the ones only appearing locally within a struct, so that i can send the variables to other programs to further visualization.
In this following code the error occurs:
// Where the system struct originates from.
let systems: Vec<System> = system_analyse::analyse_system(data);
// I try to only serialize the objects contained within one of the system structs.
let jsonstringresult = serde_json::to_string(&systems[0].objects);
match jsonstringresult {
Ok(v) => {
println!("{:?}", &v);
v
},
Err(e) => {
// The error message originates from here.
println!("An error occured at serializing: {}", e);
String::new()
}
}
I get the following error:
An error occured at serializing: key must be a string
I have found this thread discussing the issue of serializing dynamic traits, and i've followed the instructions and added the following to the trait:
pub trait CommonObjects: erased_serde::Serialize {
...
}
serialize_trait_object!(CommonObjects);
Which makes the code compile in the first place. I've also found this site getting the same error, but the issue there seems to be with enums. Maybe the problem in this site is related to my problem, but i cant figure out how if so.
I'm open to all sort of feedback and even fundamentally change the structure of the code if so necessary.
A quick way to serialize a HashMap with non-string keys to Json is to use the serde_as macro from the serde_with crate.
use serde_with::serde_as;
#[serde_as]
#[derive(Serialize)]
pub struct System {
#[serde_as(as = "Vec<(_, _)>")]
pub objects: HashMap<(u32, u32), Box<dyn CommonObjects>>,
//...
}
The #[serde_as(as = "Vec<(_, _)>")] encodes the map as a sequence of tuples, representing pairs of keys and values. In Json, this will become an array of 2-element arrays.

How to store Rust object without Default in GObject

I am basically following the gtk-rs book to create a simple app. I have the following rust struct, which I want to display in a ListView. This struct is basically read-only and should not be edited, so I do not want to expose it's members as properties and I also do not want to create it from GTK itself (it's created inside the rust-only business logic). I would like to expose the entire struct as one property.
pub struct Video {
pub number: u32,
pub encoding: Encoding,
pub chapters: Vec<Chapter>,
}
pub struct Chapter {
pub number: u32,
pub filename: String,
pub thumbnail_filename: Option<String>,
pub preview_video_filename: Option<String>,
}
How can I wrap this inside a GObject? I'm trying to create the following wrapper:
mod imp {
#[derive(Default)]
pub struct VideoObject {
pub data: Rc<RefCell<Video>>,
}
#[glib::object_subclass]
impl ObjectSubclass for VideoObject {
const NAME: &'static str = "VideoObject";
type Type = super::VideoObject;
}
impl ObjectImpl for VideoObject {}
}
pub const VIDEO_PROPERTY: &str = "video";
glib::wrapper! {
pub struct VideoObject(ObjectSubclass<imp::VideoObject>);
}
impl VideoObject {
pub fn new(video: Video) -> Self {
Object::new(&[]).expect("Could not create VideoObject")
}
}
This fails because Video does not implement Default. I thought about wrapping it inside an Option like data: Rc<RefCell<Option<Video>>>, which compiles. Unfortunately, then I'm stuck on how to set it as a property.
I guess one way would be to use a ParamSpecPointer, box and leak Video and then pass it before, but this strucks me as unsafe and ugly...
Is there a better way to do it?
You can directly access the "imp" object and set fields manually. No need to transform everything into GObject properties:
impl VideoObject {
pub fn new(video: Video) -> Self {
let object = Object::new(&[]).unwrap();
let imp = imp::VideoObject::from_instance(&object);
imp.data.replace(Some(video));
object
}
}

Rust - value of type cannot be built from `std::iter::Iterator<>`

I have struct from which I want to extract the data.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunResult {
pub runs: Vec<RunDetails>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunDetails {
pub details: String,
pub name: String,
pub id: String,
pub type: String,
}
I am getting the data which got inserted in above struct in the variable result which I am iterating below
let my_result:RunResult = result
.runs
.into_iter()
.filter(|line| line.details.contains(&"foo"))
.collect();
I am getting the error as
value of type `RunResult` cannot be built from `std::iter::Iterator<Item=RunDetails>`
I want to get the record where type=OK, details contains foo with highest id.
How can I achieve this?
Check out the documentation for collect here.
Your struct RunResult should implement FromIterator<RunDetails>.
Try adding this (untested by me):
impl FromIterator<RunDetails> for RunResult {
fn from_iter<I: IntoIterator<Item=RunDetails>>(iter: I) -> Self {
Self {
runs: Vec::from_iter(iter);
}
}
}

Generic traits not visible in external module

I have next code:
pub trait Osm<T> {
fn get_points(&self) -> Vec<Point<T>>;
}
#[deriving(Show, PartialEq, Clone)]
pub struct Point<T> {
pub lat: T,
pub lon: T,
}
#[deriving(Show, PartialEq, Clone)]
pub struct Node<T> {
pub point: Point<T>,
}
impl<T: Clone> Osm<T> for Node<T> {
fn get_points(&self) -> Vec<Point<T>> {
return vec![self.point.clone()];
}
}
When I put this code to main and call:
let b = Node { point: Point { lat: 10i64, lon: 12i64 }};
println!("{}", b.get_points());
all work fine.
But when I put it to another module get next error:
/prj/src/main.rs:64:22: 64:34 error: type `osm::test::Node<i64>` does not implement any method in scope named `get_points`
/prj/src/main.rs:64 println!("{}", b.get_points());
^~~~~~~~~~~~
error: aborting due to previous error
I little bit confused why it not work, probably it's make private method because for main all work fine, but when I try use pub keywords it show unnecessary visibility qualifier build error. So can I use generic traits for external module?
To use a trait method in a different module, you need to use the trait first.
So use osm::test::Osm; should work. This is the only case where a use statement imports functionality instead of just making an identifier available.
Also, methods don't need explicit pubs for pub traits since exporting the trait exports the methods.

Resources