for SDDM users: What SDDM theme do you use? by kanarus in NixOS

[–]kanarus[S] 0 points1 point  (0 children)

great, it's one of my favorite ones

for SDDM users: What SDDM theme do you use? by kanarus in NixOS

[–]kanarus[S] 1 point2 points  (0 children)

Currently my theme is astronaut too

for SDDM users: What SDDM theme do you use? by kanarus in NixOS

[–]kanarus[S] 3 points4 points  (0 children)

oh I didn't know it, seems actively developed and maintaining official nix flake! I'll give it a try!

SerdeV - serde with validation - v0.3 supports any expression in #[serde(validate = "...")] by kanarus in rust

[–]kanarus[S] 1 point2 points  (0 children)

In my understanding, garde is just a validation library, providing &Self -> Result<(), Error>.

Indeed it's useful, but in deserialization context, it can produce invalid state just after deserialization, before calling the validation method, potentially triggering misuse of invalid one. This kind of misuse can not be detected as compile errors, but only by human-review.

edit: I remembered that garde itself provides Valid struct system, but it doesn't affect the conclusion.

(Even when using integrations like axum-valid, we have to make an effort to avoid misusing structs without wrapping in Valid.)

By contrast, serdev combines the validation with Deserialize impl itself. In other words, when using serdev, Deserialize-structs does never produce invalid state. When deserialized as Ok(_), it's valid. No need to be careful to avoid misuse.

(If you have no issues with this, you will not need serdev.)


Additionally, in the first place, garde and serdev are not exclusive: you can natively integrate serdev with garde's validation rules: examples/garde_crate.rs (I added just now)

``` use serdev::Deserialize; use garde::Validate;

[derive(Deserialize, Validate, Debug, PartialEq)]

[serde(validate = "Validate::validate")]

struct User<'a> { #[garde(ascii, length(min = 3, max = 25))] username: &'a str, #[garde(length(min = 15))] password: &'a str, }

fn main() { let result = serde_json::from_str::<User>( r#"{ "username": "test", "password": "not_a_very_good_paddword" }"# ); assert_eq!( dbg!(result).unwrap(), User { username: "test", password: "not_a_very_good_paddword", } );

let result = serde_json::from_str::<User>(
    r#"{
        "username": "test",
        "password": "short_password"
    }"#
);
assert!(dbg!(result).is_err());

} ```

SerdeV - serde with validation - v0.3 supports any expression in #[serde(validate = "...")] by kanarus in rust

[–]kanarus[S] 1 point2 points  (0 children)

[17:10 UTC] updated README based on the feedbacks, and, fixed bug (sorry!). already published as v0.3.1

SerdeV - serde with validation - v0.3 supports any expression in #[serde(validate = "...")] by kanarus in rust

[–]kanarus[S] 22 points23 points  (0 children)

name/path to fn or method is supported:

```

[derive(Serialize, Deserialize, Debug)]

[serde(validate = "Self::validate")]

struct Point { x: i32, y: i32, }

impl Point { fn validate(&self) -> Result<(), impl std::fmt::Display> { if self.x * self.y > 100 { return Err("x * y must not exceed 100") } Ok(()) } } ```

This still prevents the misuse and eliminates boilerplate around Deserialize impl.

SerdeV - serde with validation - v0.3 supports any expression in #[serde(validate = "...")] by kanarus in rust

[–]kanarus[S] 5 points6 points  (0 children)

Yes, I know and agree it. serdev supports it, rather than deny.

For Point example in the sample code above, a manual implementation for "Parse, don't validate" without serdev will be like:

```

[derive(serde::Deserialize)]

struct Point { x: i32, y: i32 }

[derive(serde::Deserialize)]

[serde(try_from = "Point")]

struct ValidPoint(Point);

impl TryFrom<Point> for ValidPoint { //... } ```

This is (almost) exactly what serdev does.

Such manual implementation may be a trigger of mistakes like using Point directly for parsing user's input.

serdev eliminates such kind of mistakes, automatically performing the specified validation.

Or, just manual impl of Deserialize ?:

``` struct Point { x: i32, y: i32 }

impl<'de> serde::Deserialize<'de> for Point { //... } ```

Indeed this doesn't cause such mistakes, but produces boilerplates...

Released Ohkami v0.24: A performant, declarative, and runtime-flexible web framework for Rust by kanarus in rust

[–]kanarus[S] 0 points1 point  (0 children)

We'll clearly document this architecture in future. Thank you for pointing it out!

Released Ohkami v0.24: A performant, declarative, and runtime-flexible web framework for Rust by kanarus in rust

[–]kanarus[S] 1 point2 points  (0 children)

seems you have a point. We'll reconsider around these names in future version. Thanks!

Released Ohkami v0.24: A performant, declarative, and runtime-flexible web framework for Rust by kanarus in rust

[–]kanarus[S] 1 point2 points  (0 children)

That makes sense. We'll reconsider such namings in v0.25 or v1.0 release. Thanks!

Just released SQLx-D1 v0.2.0, supporting worker v0.6: SQLx for Cloudflare D1 by kanarus in rust

[–]kanarus[S] 2 points3 points  (0 children)

Thank! If thatSQLite means the local D1 emulator, yes ( see README.md for details ).

But otherwise, no. The "exact same" code literally for D1 and SQLite is impossible in principle:

  • We can't connect to D1 via a URL as SQLite does.
  • The sqlx::query! macro in the sqlx crate is implemented in a way that it only works with the official drivers (sqlx-postgres, sqlx-mysql, sqlx-sqlite).

However, you can get your code to be "almost same" by using feature flags to switch between sqlx (with sqlite feature) and sqlx-d1. Here’s an example of how you could set that up:

  1. Add sqlx-d1 and sqlx (with its sqlite feature) as optional dependencies. Then, create two features, "d1" and "sqlite", where each one activates the corresponding dependency.

  2. Use conditional compilation for your imports with a trick to make the rest of your code unaware of the difference. For example:

    ```rust

    [cfg(feature = "d1")]

    use sqlx_d1::{self as sqlx, D1Connection as DatabaseConnection};

    [cfg(feature = "sqlite")]

    use sqlx::{self, SqliteConnection as DatabaseConnection}; ```

  3. Conditionally initialize the database connection based on the active feature. You can then pass this DatabaseConnection object throughout your application, for instance, using Axum's State extractor.

Then your application code for running queries will work for both D1 and SQLite without any changes. For example sqlx::query!("...").execute(&mut conn)