use crate::error::NifiError;
pub trait RequireField<T> {
fn require(&self, path: &str) -> Result<&T, NifiError>;
}
impl<T> RequireField<T> for Option<T> {
fn require(&self, path: &str) -> Result<&T, NifiError> {
self.as_ref().ok_or_else(|| NifiError::MissingField {
path: path.to_owned(),
})
}
}
#[macro_export]
macro_rules! require {
($root:ident $(. $field:ident)+) => {{
let mut __path = ::std::string::String::new();
let __v = &$root;
$(
if !__path.is_empty() {
__path.push('.');
}
__path.push_str(::core::stringify!($field));
let __v = $crate::RequireField::require(&__v.$field, &__path)?;
)+
__v
}};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn require_some_returns_borrow() {
let opt: Option<String> = Some("v1".to_string());
let got: &String = opt.require("version").unwrap();
assert_eq!(got, "v1");
}
#[test]
fn require_none_returns_missing_field_with_verbatim_path() {
let opt: Option<String> = None;
let err = opt.require("about.version").unwrap_err();
match err {
NifiError::MissingField { path } => assert_eq!(path, "about.version"),
other => panic!("expected MissingField, got {other:?}"),
}
}
#[test]
fn require_does_not_consume_the_option() {
let opt: Option<String> = Some("kept".to_string());
let _first = opt.require("x").unwrap();
let second = opt.require("x").unwrap();
assert_eq!(second, "kept");
}
#[derive(Debug)]
struct Outer {
inner: Option<Inner>,
}
#[derive(Debug)]
struct Inner {
leaf: Option<String>,
}
#[test]
fn macro_single_hop_ok() {
let o = Outer {
inner: Some(Inner {
leaf: Some("v".to_string()),
}),
};
let result: Result<String, NifiError> = (|| {
let got: &String = crate::require!(o.inner.leaf);
Ok(got.clone())
})();
assert_eq!(result.unwrap(), "v");
}
#[test]
fn macro_missing_outer_reports_outer_path() {
let o = Outer { inner: None };
let result: Result<(), NifiError> = (|| {
let _leaf: &String = crate::require!(o.inner.leaf);
Ok(())
})();
let err = result.unwrap_err();
match err {
NifiError::MissingField { path } => assert_eq!(path, "inner"),
other => panic!("expected MissingField, got {other:?}"),
}
}
#[test]
fn macro_missing_leaf_reports_full_dotted_path() {
let o = Outer {
inner: Some(Inner { leaf: None }),
};
let result: Result<(), NifiError> = (|| {
let _leaf: &String = crate::require!(o.inner.leaf);
Ok(())
})();
let err = result.unwrap_err();
match err {
NifiError::MissingField { path } => assert_eq!(path, "inner.leaf"),
other => panic!("expected MissingField, got {other:?}"),
}
}
}