I'm trying to learn ReasonML and following the example for bs-json we can decode raw json into a type like this:
type point = {
x: float,
y: float
};
module Decode = {
let point = json =>
Json.Decode.{
x: json |> field("x", float),
y: json |> field("y", float)
};
}
I'm a bit confused as to what this Json.Decode.{ ... } is. I understand we can open up a scope into a module using the .() dot parenthesis, but I haven't seen this dot curly braces before.
It means pretty much the same thing, that Json.Decode is opened in the scope of {}, which defines a record, as usual. Essentially just a short-hand for Json.Decode.({ .. }).
Edit: I just added a note to bs-jsons README, just below the example, to explain this syntax.
Related
To start: this question is already kind of resolved for me. But the discussion might be interesting.
I like code so let's look at this function:
fun foo(path: Path) {
val absPath = path.normalize().absolute() // sanitizing
doSomethingWith(path) // this is unsafe use because path is not sanitized
doSomethingWith(absPath) // this is safe because we are using the sanitized absPath value
}
Kotlin function parameters are always val, therefore we are required to create a new variable if we want to derive from it's value.
We can choose between using a new name or using an old name and annotating it with #Suppress("NAME_SHADOWING") to not get the Name shadowed: ... warning.
I'm looking for something like
fun foo(path: Path) {
val absPath = path.normalize().absolute()
#DoNotUseAnymore path
doSomethingWith(path) // should give a warning/error
doSomethingWith(absPath) // is fine
}
Do you know something like that? Or do you think I'm fiddling around at the wrong end of the equation and should just learn to not feel like doing bad stuff when using the #Suppress-annotation? Since I like to code, this is what I mean:
fun foo(path: Path) {
#Suppress("NAME_SHADOWING")
val path = path.normalize().absolute() // sanitizing
doSomethingWith(path) // there is only one sanitized variable so we are safe
}
In some way this method is the cleanest one... I probably stick to that... Should I publish this question now? Well... maybe :)
Summary
I'm writing a program in rust and I would prefer use a TOML file to store a vector of struct's. However I can't figure out how to store a vector of struct's in a TOML file. I am able to do this using JSON but was hoping for TOML so just confirming that I'm not overlooking something (not even sure if the TOML format would be able to support what I want). Therefore, I'm trying to find out if anyone knows of a way use rust to serialize a vector of struct's to TOML and more importantly to deserialize it back into a vector.
Error message (on attempt to deserialize)
thread 'main' panicked at 'called Result::unwrap() on an Err value: Error { inner: ErrorInner { kind: Wanted { expected: "a table key", found: "a right bracket" }, line: Some(0), col: 2, at: Some(2), message: "", key: [] } }', src/main.rs:22:55
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
Excerpt from Cargo.toml
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.86"
toml = "0.5.9"
Example code
Link to code on Playground
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Debug, Serialize, Deserialize)]
struct Point {
x: i32,
}
/// An ordered list of points (This is what I want to store in the TOML file)
type Points = Vec<Point>;
fn main(){
// Create sample data to test on
let original: Points = vec![Point { x: 1 }];
// Working code that converts it to json and back
let json = serde_json::to_string(&original).unwrap();
let reconstructed: Points = serde_json::from_str(&json).unwrap();
assert_eq!(original, reconstructed); // No panic
// "Desired" code that converts it to toml but unable to deserialize
let toml = toml::to_string(&original).unwrap();
let reconstructed: Points = toml::from_str(&toml).unwrap(); // Panics!
assert_eq!(original, reconstructed);
}
Output of toml::to_string(&original).unwrap()
[[]]
x = 1
Explanation of example code
In the example code I create some sample data then convert it to JSON and back with no issue. I then try to convert it to TOML, which doesn't give an error but the output doesn't make sense to me. Then I try to convert it back into a rust vector and that triggers the error. My biggest problem is I'm not even sure how I would expect the TOML file to look for a valid representation of a vector with multiple struct's.
Related Questions / Research
I wasn't able to find any information for creating a vector with multiple struct's the closest I could find is this question, and while the question looks like it should solve my problem the actual problem was serializing enums and the solution hence refers to that and doesn't solve my problem.
It seems that to represent an array of tables in Toml the syntax is
[[points]]
x = 1
[[points]]
x = 2
So backtracking from Toml syntax and original panic error Error { inner: ErrorInner { kind: Wanted { expected: "a table key", found: "a right bracket" }: Introducing a wrapper struct to represent table key fixes the issue.
use serde::{Deserialize, Serialize};
#[derive(PartialEq, Debug, Serialize, Deserialize)]
struct Point {
x: i32,
}
#[derive(PartialEq, Debug,Serialize, Deserialize)]
struct Points {
points: Vec<Point>
}
impl From<Vec<Point>> for Points {
fn from(points: Vec<Point>) -> Self {
Points {
points
}
}
}
fn main(){
let original: Points = vec![Point { x: 1 }, Point {x : 2}].into();
let json = serde_json::to_string(&original).unwrap();
let reconstructed: Points = serde_json::from_str(&json).unwrap();
assert_eq!(original, reconstructed);
let toml = toml::to_string(&original).unwrap();
let reconstructed: Points = toml::from_str(&toml).unwrap();
assert_eq!(original, reconstructed);
}
I'm trying to get two Handlebars variables to render inside a custom Handlebars helper I've created.
I'm using the Express.js view engine for handlebars.js, and in my app.js have set up a helper to compare equality:
const hbs = require('hbs');
app.set('view engine', 'hbs');
hbs.registerHelper('ifEqual', (a, b, options) => {
if (a === b) {
return options.fn(this);
}
return options.inverse(this);
});
My controller passes two variables to the view:
res.render('my-view', {
x: 3,
y: 3,
});
In my-view.hbs I'd like to render the variables if they're equal, so I tried:
{{#ifEqual x y}}
foo
{{x}}
{{y}}
{{/ifEqual}}
The result is only foo renders. Why don't {{x}} and {{y}} render here? Do I need to do this with a partial?
The reason your template will not render the values of x or y from within your ifEqual block is because there are no x or y properties within the context of that block. The reason that these properties are missing from the context is a very simple one: It is because in your call to registerHelper you used an Arrow Function Expression to define the Helper function.
Arrow Functions Expressions, in addition to a more compact syntax, are different from standard Function Expressions. The important difference in this case is that they do not have their own this context.
When you call registerHelper, Handlebars will bind the helper callback function to the data context of the template, in this case that would be the Object: { x: 3, y: 3 }. However, this will only work if you use a regular Function Expression as your callback and not an Arrow Function Expression - as the Arrow Function Expression cannot be dynamically bound to a different this context.
This means that you must use a regular function expression as your argument to registerHelper:
hbs.registerHelper('ifEqual', function (a, b, options) {
// Function body remains the same.
});
To get a better sense of what is wrong, you could console.log(this) within your helper using both function expression types and compare the difference.
I have created a fiddle to demonstrate the difference.
Ok so I've done this with a different approach:
hbs.registerHelper('renderVars', (a, b) => {
let output;
if (a === b) {
output = `foo ${a} ${b}`;
} else {
output = 'foo';
}
return output;
});
Then in my view:
{{#renderVars x y}}
{{/renderVars}}
I'm trying to use an actix-web server as a gateway to a small stack to guarantee a strict data format inside of the stack while allowing some freedoms for the user.
To do that, I want to deserialize a JSON string to the struct, then validate it, serialize it again and publish it on a message broker. The main part of the data is an array of arrays that contain integers, floats and datetimes. I'm using serde for deserialization and chrono to deal with datetimes.
I tried using a struct combined with an enum to allow the different types:
#[derive(Serialize, Deserialize)]
pub struct Data {
pub column_names: Option<Vec<String>>,
pub values: Vec<Vec<ValueType>>,
}
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum ValueType {
I32(i32),
F64(f64),
#[serde(with = "datetime_handler")]
Dt(DateTime<Utc>),
}
Since chrono::DateTime<T> does not implement Serialize, I added a custom module for that similar to how it is described in the serde docs.
mod datetime_handler {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer, Serializer};
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let s = dt.to_rfc3339();
serializer.serialize_str(&s)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
println!("Checkpoint 1");
let s = String::deserialize(deserializer)?;
println!("{}", s);
println!("Checkpoint 2");
let err1 = match DateTime::parse_from_rfc3339(&s) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => Err(e),
};
println!("Checkpoint 3");
const FORMAT1: &'static str = "%Y-%m-%d %H:%M:%S";
match Utc.datetime_from_str(&s, FORMAT1) {
Ok(dt) => return Ok(dt.with_timezone(&Utc)),
Err(e) => println!("{}", e), // return first error not second if both fail
};
println!("Checkpoint 4");
return err1.map_err(serde::de::Error::custom);
}
}
This tries 2 different time formats one after the other and works for DateTime strings.
The Problem
It seems like the combination of `#[derive(Serialize, Deserialize)]`, `#[serde(untagged)]` and `#[serde(with)]` does something unexpected. `serde:from_str(...)` tries to deserialize every entry in the array with my custom `deserialize` function.
I would expect it to either try to deserialize into `ValueType::I32` first, succeed and continue with the next entry, as [the docs](https://serde.rs/enum-representations.html) say:
Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
What happens is that the custom deserializeis applied to e.g. "0" fails and the deserialization stops.
What's going on? How do I solve it?
My ideas are that I either fail to deserialize in the wrong way or that I somehow "overwrite" the derived deserialize with my own.
#jonasbb helped me realize the code works when using [0,16.9,"2020-12-23 00:23:14"] but it does not when trying to deserialize ["0","16.9","2020-12-23 00:23:14"]. Serde does not serialize numbers from strings by default, the attempts for I32 and F64 just fail silently. This is discussed in this serde-issue and can be solved using the inofficial serde-aux crate.
Many crates will implement serde and other common utility crates, but will leave them as optional features. This can help save time when compiling. You can check a crate by viewing the Cargo.toml file to see if there is a feature for it or the dependency is included but marked as optional.
In your case, I can go to chrono on crates.io and select the Repository link to view the source code for the crate. In the Cargo.toml file, I can see that serde is used, but is not enabled by default.
[features]
default = ["clock", "std", "oldtime"]
alloc = []
std = []
clock = ["libc", "std", "winapi"]
oldtime = ["time"]
wasmbind = ["wasm-bindgen", "js-sys"]
unstable-locales = ["pure-rust-locales", "alloc"]
__internal_bench = []
__doctest = []
[depenencies]
...
serde = { version = "1.0.99", default-features = false, optional = true }
To enable it you can go into the Cargo.toml for your project and add it as a feature to chrono.
[depenencies]
chrono = { version: "0.4.19", features = ["serde"] }
Alternatively, chrono lists some (but not all?) of their optional features in their documentation. However, not all crates do this and docs can sometimes be out of date so I usually prefer the manual method.
As for the issue between the interaction of deserialize_with and untagged on enums, I don't see any issue with your code. It may be a bug in serde so I suggest you create an issue on the serde Repository so they can further look into why this error occurs.
I am new to Kotlin and I am struggling to understand following code:
println((1..5).joinToString(", ") { (it * 2).toString() }) // 2, 4, 6, 8, 10
From my understanding the above code should have been written like this:
println((1..5).map { it * 2 }.joinToString(", ")) // 2, 4, 6, 10
To which function are we passing the lambda { (it * 2).toString() } and why do we have .toString() there? I didn't find any clue in documentation of joinToString either. So, how does this work?
joinToString has an optional parameter called transform of type ((T) -> CharSequence)?. If a non-null argument is passed for that parameter, joinToString will run it on each element of the receiver collection before joining the elements into a string. Let's look at both code snippets you've provided in more detail:
println((1..5).joinToString(", ") { (it * 2).toString() })
Here, you're calling joinToString on a range of (1..5). You're passing a transform function, { (it * 2).toString() }, so before joining the elements of the range into a string, joinToString will multiply each of them by 2. You'll also need to call toString() on the result of the multiplication since the signature of transform is (T) -> CharSequence.
A piece of general advice: whenever you're confused by Kotlin's terse syntax, reduce the level of terseness until you fully understand the code. In this case, changing the snippet to the following might make it easier to understand:
println((1..5).joinToString(", ", transform = { (it * 2).toString() }))
The second snippet produces the exact same result:
println((1..5).map { it * 2 }.joinToString(", "))
You don't need the transform function here since the elements are already multiplied by 2 inside map.
A note on performance (this is irrelevant here cause the size of the input is too small, but important for large inputs), map will have to copy the entire collection, hence the second variant will be slower and less memory efficient. The first variant does not have that problem.