qrock 0.2.2

Helpers for Rocket HTTP server applications.
Documentation
pub mod fields;

use std::collections::HashMap;

use rocket::{
  form::{error::ErrorKind, Contextual, Form},
  serde::json::Json,
  Responder
};

#[derive(Responder)]
pub enum ApiError {
  #[response(status = 422)]
  BadForm(Json<HashMap<String, Vec<String>>>)
}


/// Compatibility wrapper for [`unpack()`].
///
/// # Notes
/// This function will be removed in a future version.  Use `unpack()` instead.
#[allow(clippy::missing_errors_doc)]
#[deprecated(since = "0.2.0", note = "please use `unpack()` instead")]
pub fn unpack_field_errors<T>(
  form: Form<Contextual<'_, T>>
) -> Result<T, ApiError>
where
  T: std::fmt::Debug
{
  unpack(form)
}


/// Given a [`Contextual`] form, return the inner form type if no form errors
/// are detected.
///
/// # Errors
/// If there are field errors, return [`ApiError::BadForm`].  The `BadForm`
/// will contain a `HashMap<String, Vec<String>>`, where the keys are form
/// field names, and their values (`Vec<String>`) are string representation of
/// the errors these fields caused.
pub fn unpack<T>(form: Form<Contextual<'_, T>>) -> Result<T, ApiError>
where
  T: std::fmt::Debug
{
  let form = form.into_inner();

  if let Some(form) = form.value {
    Ok(form)
  } else {
    let mut field_errors = HashMap::new();

    for err in form.context.errors() {
      let Some(ref name) = err.name else {
        continue;
      };
      let errmsg = match &err.kind {
        ErrorKind::Validation(errmsg) => {
          format!("validation failed; {errmsg}")
        }
        ErrorKind::InvalidLength { .. } => {
          format!("invalid length; {}", err.kind)
        }
        ErrorKind::InvalidChoice { choices: _ } => "invalid choice".into(),
        ErrorKind::OutOfRange { .. } => {
          format!("out of range; {}", err.kind)
        }
        _ => {
          eprintln!("Unhandled error kind: {err:?}");
          continue;
        }
      };
      field_errors
        .entry(name.to_string())
        .or_insert_with(Vec::<String>::new)
        .push(errmsg);
    }

    Err(ApiError::BadForm(Json(field_errors)))
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :