tightness
tightness copied to clipboard
Problem with flexibility
Hi! I've read your post and think that this is a pretty cool idea. But I also think that there are some things that could be better. For example, when I tried to use this lib in my project, I found myself doing something like this:
use tightness::bound;
pub struct User {
pub name: UserName,
}
bound!(UserName: String where |s| !s.is_empty());
impl User {
fn new(name: &str) -> Result<User, &'static str> {
let name = name.trim().to_string(); // no need to make programmer always trim the string himself
let name = UserName::new(name).map_err(|_e| "name is empty")?;
Ok(User { name })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
assert!(matches!(User::new(" "), Err("name is empty")));
assert!(matches!(User::new(" something "), Ok(_)));
}
}
There's two problems:
- I want to mutate my value first. The data can come from different sources, i.e. from a HTTP request or from some CLI input. Implementing this mutation everywhere seems to make code clumsier.
- I want to be able to return my own error. Again, if data come from different sources, with current approach one must do
.map_err
everywhere.
All of this things tear the domain logic of the type into different places, which is not good. The code above solves some of this problems, but still if I want to mutate the name
somewhere else, I need to convert the error there. And, of course, the same thing applies to manual struct creation.
How do you thing, is it possible to expand the macro so it allows user to mutate the value and return another error? Would be happy to help with that, although I have no experience writing macros.
And one side note: is it possible to somehow integrate this thing with serde
if internal type implements Serialize
and Deserialize
? I would be awesome if the validation could be handled during serialization.