Keats / validator

Simple validation for Rust structs
MIT License
2k stars 144 forks source link

Is it possible to make the the `message` be a variable? #142

Open Pranoy1c opened 3 years ago

Pranoy1c commented 3 years ago

Is it possible to make the the message be a variable?

For example I have this:

#[derive(Deserialize, Serialize, Validate, Debug)]
struct Auth {
    #[validate(length(min = 3, max = 21, message = "Username must be 3-21 characters long alphanumeric"), regex(path = "ALPHANUMERIC_REGEX",
    message = "Username must be 3-21 characters long alphanumeric"))]
    username: String,
    #[validate(length(min = 6, max = 100, message = "Password must be 6-100 characters long"))]
    password: String,
    #[validate(custom = "validate_authtype")]
    authtype: String,
}

Notice how I am having to repeat the string "Username must be 3-21 characters long alphanumeric" twice - once for the length and once for alphanumeric regex. Is it possible to put a const variable instead to avoid the repetition? Or some custom function for the message - this can be helpful for localization purposes too.

xFrednet commented 3 years ago

This is currently sadly not possible. The message is stored in the SchemaValidation struct which only holds a string.

My solutions for custom messages or a multilingual app was to simply store the translation key. The key was then used in the frontend to access the actual message. This doesn't completely cover your use case, but it might give you a nice workaround.

#[derive(Deserialize, Serialize, Validate, Debug)]
struct AwesomeData {
    #[validate(length(min = 6, max = 100, message = "validation.fail.str_len"))]
    #[validate(regex(path = "ALPHANUMERIC_REGEX", message = "validation.fail.str_len"))]
    username: String
}

Some background information if someone would think about implementing this :upside_down_face:

Changing it to reference a value would be relatively simple, but it would require a syntax change to distinguish between a message string and a value path. The length validator distinguished between constant values and paths by the type. This is not possible in this case since both types (constant value and path value) are stored in a string.

Other validators separate the parameter usage by name. Here is an example:

#[validate(custom = "validate_authtype", message = "Cheesecake")] // Const string
#[validate(custom = "validate_authtype", message_path = "PATH_VALUE"] // Path value
authtype: String,

This is not ideal, and I'm not 100% happy with it but would be the simplest solution without breaking existing code.

Keats commented 3 years ago

Yeah, the initial intent was to use to have code as the translation key for something like fluent if needed (so what @xFrednet uses in message but that works as well) and message if you wanted a simple string on top. The params of the validation are passed (https://github.com/Keats/validator/blob/master/validator/src/types.rs#L13) for interpolation with i18n systems.

Edit: nevermind, I'm not entirely sure anymore why we have code...

pintariching commented 2 years ago

How about if you prepend the message with something like CONST: then it expects a variable, otherwise it expects a string. Something like this:

#[validate(length(max = 100, message = "CONST:TOO_LONG_MESSAGE"))]
var: String