Parsing yaml in Rust using serde_yaml - rust

I am new to Rust. I am trying to parse yaml in Rust using serde_yaml, but can't make the code compile:
My Cargo.toml:
[package]
name = "apmdeps"
version = "0.1.0"
authors = ["Roger Rabbit"]
edition = "2018"
[dependencies]
git2 = "0.10"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
I tried to adapt the code sample found on the serde_yaml website, to no avail:
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Dependency {
url: String,
tag: String,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Project {
dependencies: Vec<Dependency>,
}
fn main() {
let s = "---\ndependencies:\n--url:http://test1\ntag:tag1\n--url:http://test2\ntag:tag2";
let project: Project = serde_yaml::from_str(&s);
}
I get the following error:
error[E0308]: mismatched types
--> src/main.rs:17:28
|
17 | let project: Project = serde_yaml::from_str(&s);
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Project`, found enum `std::result::Result`
|
= note: expected type `Project`
found type `std::result::Result<_, serde_yaml::error::Error>`

Your problem is that serde_yaml::from_str(&s) does not return a Dependency struct directly as you expect, but a Result struct.
Result structs are rust's way of error handling. Results are either Ok(value) or an Err and you need to check which one it is. Typically in a match expression.
In your case the parsed dependency is wrapped in Ok(project) if parsing the string is successful.
I could get your code to compile with the following match expression:
let project_result : Result<Project, _> = serde_yaml::from_str(&s);
match project_result {
Ok(project) => println!("dependencies = {:?}", project),
Err(_) => println!("Error!")
}
The next problem however is that your string seemed not proper yaml, at least not as expected from serde, and I do get "Error!" from the program.
I changed your string to the following to get some useful output. I don't know if that is your intended yaml though.
let s = "---\ndependencies:\n - {url: http://test1, tag: tag1}\n - {url: http://test2, tag: tag2}\n";

Related

(De)serialize RFC-3339 timestamp with serde to time-rs OffsetDateTime

My goal is to (de)serialize objects with RFC-3339 timestamps from Json to Rust structs (and vice versa) using serde and time-rs.
I would expect this ...
use serde::Deserialize;
use time::{OffsetDateTime};
#[derive(Deserialize)]
pub struct DtoTest {
pub timestamp: OffsetDateTime,
}
fn main() {
let deserialization_result = serde_json::from_str::<DtoTest>("{\"timestamp\": \"2022-07-08T09:10:11Z\"}");
let dto = deserialization_result.expect("This should not panic");
println!("{}", dto.timestamp);
}
... to create the struct and display the timestamp as the output, but I get ...
thread 'main' panicked at 'This should not panic: Error("invalid type: string \"2022-07-08T09:10:11Z\", expected an `OffsetDateTime`", line: 1, column: 36)', src/main.rs:12:38
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
My dependencies look like this:
[dependencies]
serde = { version = "1.0.138", features = ["derive"] }
serde_json = "1.0.82"
time = { version = "0.3.11", features = ["serde"] }
According to the documentation of the time-rs crate, this seems to be possible but I must be missing something.
The default serialization format for time is some internal format. If you want other formats, you should enable the serde-well-known feature and use the serde module to choose the format you want:
#[derive(Deserialize)]
pub struct DtoTest {
#[serde(with = "time::serde::rfc3339")]
pub timestamp: OffsetDateTime,
}
The solution below is based on the serde_with crate. As per its documentation, it aims to be more flexible and composable.
use serde_with::serde_as;
use time::OffsetDateTime;
use time::format_description::well_known::Rfc3339;
#[serde_as]
#[derive(Deserialize)]
pub struct DtoTest {
#[serde_as(as = "Rfc3339")]
pub timestamp: OffsetDateTime,
}
And the Cargo.toml file should have:
[dependencies]
serde_with = { version = "2", features = ["time_0_3"] }
At the following page are listed all De/Serialize transformations available.

Error while rocket implementation for creating a basic API

I have been programming in Rust for a month or so and now I wanted to try out API's using Rocket in rust. I tried to implement the below code but I got the error the trait bound \Json<Test>: Responder<'_, '_> is not satisfied . Not quite sure what I did wrong I tried to find out a reason for it but couldn't get any. I am referring to this video and this guide, according to which I did everything correct.
Also side note: I use extern crate rocket but it still does not recognize Build, routes, etc. Any solution to this as well?
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use rocket::*;
use rocket_contrib::json::Json;
use serde_json::json;
struct Test{
name: String,
}
#[get("/")]
fn hello_world() -> Json<Test> {
let test: Test = Test { name: String::from("Test54") };
Json(test)
}
#[launch]
fn rocket() -> Rocket<Build> {
rocket::build()
.mount(
"/",
routes![hello_world]
)
}
cargo.toml
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "lib"
path = "src/lib.rs"
[[bin]]
name = "backend"
path = "src/bin.rs"
[dependencies]
rocket = "0.5.0-rc.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dependencies.rocket_contrib]
version = "0.4"
default-features = false
features = ["json"]
error log
error[E0277]: the trait bound `Json<Test>: Responder<'_, '_>` is not satisfied
--> src/bin.rs:19:21
|
19 | fn hello_world() -> Json<Test> {
| ^^^^^^^^^^ the trait `Responder<'_, '_>` is not implemented for `Json<Test>`
|
= help: the following other types implement trait `Responder<'r, 'o>`:
<&'o [u8] as Responder<'r, 'o>>
<&'o str as Responder<'r, 'o>>
<() as Responder<'r, 'static>>
<(ContentType, R) as Responder<'r, 'o>>
<(Status, R) as Responder<'r, 'o>>
188 | pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::fr
om`
For more information about this error, try `rustc --explain E0277`.
rocket_contrib::json::Json only implements rocket::response::Responder if the T it contains implements serde::ser::Serialize (due to this). Currently, your Teststruct does not implement Serialize, so Json<Test> does not implement Responder; this is what the compiler is complaining about.
You can easily implement Serialize for Test by deriving it:
use serde::Serialize;
#[derive(Serialize)]
struct Test {
name: String,
}

Compiler Unaware of Provided Methods of Trait

I'm using the prost crate in a very "hello world" way, with a basic ProtoBuf file:
foo.proto
syntax = "proto3";
package foo;
message Foo {
string mystring = 1;
}
I can verify the types that prost produces at compile time by inspecting ./target/PROJECT/build/PROJECT-{some hash}/out/foo.rs:
foo.rs
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Foo {
#[prost(string, tag="1")]
pub mystring: ::prost::alloc::string::String,
}
See that the prost::Message trait gets derived? Its docs claim I have tons of functions at my disposal for this trait, but only the two required ones, encoded_len() and clear(), can be called without error. Any of the provided ones (those with default implementations) result in a cannot find function in this scope error.
main.rs
use prost::Message;
pub mod foo {
include!(concat!(env!("OUT_DIR"), "/foo.rs"));
}
fn main() {
let f = foo::Foo { mystring: "bar".to_string() };
let v = encode_to_vec(&f);
}
cargo run
error[E0425]: cannot find function `encode_to_vec` in this scope
|
| let v = encode_to_vec(&f);
| ^^^^^^^^^^^^^ not found in this scope
And, worryingly, this warning implies it's not even looking at the trait it needs to:
warning: unused import: prost::Message
(edit) For reference:
Cargo.toml
[package]
name = "testing"
version = "0.1.0"
edition = "2018"
[dependencies]
prost = { version = "0.7.0", features = ["std"] }
[build-dependencies]
prost-build = "0.7.0"
encode_to_vec is not a free function. You need to call it as one of
f.encode_to_vec()
foo::Foo::encode_to_vec(&f)
Message::encode_to_vec(&f)
<foo::Foo as Message>::encode_to_vec(&f)
The first method is the most normal, but the other ones may produce useful error messages when you get confused about what's going on.
[Edit:] Well, encode_to_vec was added in prost 0.8.0, so with 0.7.0, it won't be usable.

Deserialize XML with multiple schema structures

How can I deserialize an XML-string (using serde derive) returned by an API that might have different children?
If I call an API it might return one of the following result sets:
use serde::Deserialize;
use serde_xml_rs::from_str;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Error {
code: u32,
message: String,
}
#[derive(Debug, Deserialize)]
struct Item {
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct DataSet {
error: Option<Error>,
data: Option<Vec<Item>>
}
fn main() {
let error = r#"
<DataSet>
<Error>
<Code>0</Code>
<Message>error</Message>
</Error>
</DataSet>
"#;
let dataset: DataSet = from_str(error).unwrap();
println!("{:#?}", dataset);
let items = r#"
<DataSet>
<Item>..</Item>
<Item>..</Item>
</DataSet>
"#;
let dataset: DataSet = from_str(items).unwrap();
println!("{:#?}", dataset);
}
[package]
name = "dataset"
version = "0.1.0"
edition = "2018"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde-xml-rs = "0.3"
But I feel like there should be a more idiomatic way of achieving this. Especially since it would make matching off the data a lot easier.
I've tried using #[serde(flatten)], and I've tried setting ApiResponse to a tuple-struct and a struct with a single tuple parameter. No luck.
Is this possible without having to build a custom deserializer?
Take a look at some of the serde examples which might be able to help you here. Specifically the examples/docs on untagged enums which allow you to define many ways to deserialize into a single enum type.

Unable to use parity_codec's Encode even though I have derived it for a struct

I am trying to use Encode::encode on structs with a derived implementation, but I am getting this error:
error[E0599]: no method named `encode` found for type `AnimalCollection<Animal>` in the current scope
--> src/main.rs:42:57
|
13 | struct AnimalCollection<A: AnimalTrait> {
| --------------------------------------- method `encode` not found for this
...
42 | println!("animal collection encoded = {:?}",animals.encode());
| ^^^^^^
|
= note: the method `encode` exists but the following trait bounds were not satisfied:
`AnimalCollection<Animal> : _IMPL_DECODE_FOR_Animal::_parity_codec::Encode`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `encode`, perhaps you need to implement it:
candidate #1: `_IMPL_DECODE_FOR_Animal::_parity_codec::Encode`
This is the code:
use indexmap::map::IndexMap;
use parity_codec;
use parity_codec::{Decode, Encode};
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
struct Animal {
name: String,
cell: u32,
}
trait AnimalTrait: Encode + Decode {}
impl AnimalTrait for Animal {}
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
struct AnimalCollection<A: AnimalTrait> {
animals: IndexMap<String, A>,
}
impl<A: AnimalTrait> AnimalCollection<A> {
fn new() -> AnimalCollection<A> {
AnimalCollection {
animals: IndexMap::new(),
}
}
fn add(&mut self, name: String, a: A) {
self.animals.insert(name, a);
}
}
fn main() {
let a = Animal {
name: String::from("Tiger"),
cell: 10,
};
println!("animal struct encoded = {:?}", a.encode());
let mut animals = AnimalCollection::<Animal>::new();
animals.add(
String::from("Dog"),
Animal {
name: String::from("Dog"),
cell: 1,
},
);
animals.add(
String::from("Cat"),
Animal {
name: String::from("Cat"),
cell: 2,
},
);
println!("animal collection encoded = {:?}", animals.encode());
}
Why isn't it working despite that I have #[derive]d all the traits automatically? How do I fix it?
Since I am deriving the Encode and Decode traits, I shouldn't really implement anything on my own, or should I?
I tested this code and it works on the Animal struct but doesn't work on the AnimalCollection struct. I also tried to implement the Encode trait on AnimalCollection but immediately got "conflicting implementation" error, so I am kind of stuck on how to solve this.
Cargo.toml is a bit tricky, you need to use derive feature:
[package]
name = "encmap"
version = "0.0.1"
edition = "2018"
[dependencies]
parity-codec = { version = "3.3", features = ["derive"] }
indexmap = "1.0.2"

Resources