Open CrowdHailer opened 4 years ago
Running through some examples for input handling.
In all these cases the error type is
pub type Invalid(a) {
Missing(key: String)
CastFailure(key: String, help: a)
}
In these examples all the custom as_*
functions return the same error type, which will end up as the content in help.
// required field
try name = params.find(form, "name")
try name = params.cast(form, as_name)
// optional field
let url = params.optional(form, "url")
try url = params.cast_optional(url, url.parse) |> result.map_error(fn(_) { "not a valid url"})
// or wrapper function that handles mapping the error
try url = params.cast_optional(url, as_url)
// handling a fallback
let url = params.use_fallback(url, Uri(...))
cast
cast_optional
need to be given key value for writing debug messages.try name = params.required(from: form, get: "name", cast: as_name)
try url = params.optional(from: form, get: "url", cast: as_url)
try url = params.overridable(from: form, get: "url", cast: as_url, or: "default.com")
A params module could define cast functions for strings ints etc
try name = params.required(form, "name", as_string(_, [MinLength(5), Pattern(), MaxLength(30)])
try
syntaxIf this change existed
let name = try params.required(...
// instead of
try name = params.required(...
Then you could construct form objects really easily
fn cast_form(form) -> Result(CreateUser, params.Invalid) {
Ok(CreateUser(
name: try params.required(form, "name", as_name),
url: try params.overridable(form, "url", as_url, "home.com")
))
}
Instead of wrapper functions that all return the same error type we could have a function for parse_form and then wrap the error once.
fn try_parse_form(form) {
try ...
try ...
FormValues(a: ....)
}
pub fn parse_form(form) {
try_parse_form(form)
|> result.map_error()
}
There will be a lot of these function pairs. because there is no way to wrap the error inside the first function.
Currently I prefer writting web wrapper functions, but this could be a lot of wrapper functions and requires more up front work about choosing an error type.
handle functions return Result(Response, Error)
The error could be another response but this makes it too easy to encode the error in different ways.
It could a big enum
type ErrorResponse {
UnprocessableEntity(input.Invalid())
}
fn parse_form(form) {
try_parse_form(form)
// big enum
|> result.map_error(UnprocessableEntity)
}
There is no completeness to assure that Enum is used correctly. Also there might be alot of time designing each sub type. Nice to use in parse_form func tho, see example above
Or it could be a special Error type
type ErrorResponse {
ErrorResponse(type: ErrorType, details: String)
}
can enum all the error types, has only a single place to set the details.
@lpil thanks for the feedback on the PR, I have merged version 1 to play with will see how it goes. Am mulling some of your suggestions for a next update.
I particularly think it might make sense to work on maps because although params come in order the api as designed so far doesn't allow you to pull out a value that is defined multiple times
Perhaps we could move forward with this and a more complex validation library can be left as an exercise for someone else. Getting 90% of the way there with everything provides the most value now!
Thanks
Add an example endpoint that parses input.
This will probably make use of https://github.com/rjdellecese/gleam_decode and ideally the approach would be reusable for multiple input sources. e.g. query strings, JSON, forms