use core::ptr::NonNull;
use crate::ffi::{ngx_core_conf_t, ngx_module_t};
pub trait CoreModule {
fn module() -> &'static ngx_module_t;
}
pub trait CoreModuleConfExt {
#[inline]
unsafe fn core_main_conf_unchecked<T>(&self, _module: &ngx_module_t) -> Option<NonNull<T>> {
None
}
}
impl CoreModuleConfExt for crate::ffi::ngx_cycle_t {
#[inline]
unsafe fn core_main_conf_unchecked<T>(&self, module: &ngx_module_t) -> Option<NonNull<T>> {
let conf_ctx = NonNull::new(self.conf_ctx)?;
let conf = unsafe { *conf_ctx.as_ptr().add(module.index) };
NonNull::new(conf.cast())
}
}
impl CoreModuleConfExt for crate::ffi::ngx_conf_t {
#[inline]
unsafe fn core_main_conf_unchecked<T>(&self, module: &ngx_module_t) -> Option<NonNull<T>> {
unsafe { self.cycle.as_ref()?.core_main_conf_unchecked(module) }
}
}
pub unsafe trait CoreModuleMainConf: CoreModule {
type MainConf;
fn main_conf(o: &impl CoreModuleConfExt) -> Option<&'static Self::MainConf> {
unsafe { Some(o.core_main_conf_unchecked(Self::module())?.as_ref()) }
}
fn main_conf_mut(o: &impl CoreModuleConfExt) -> Option<&'static mut Self::MainConf> {
unsafe { Some(o.core_main_conf_unchecked(Self::module())?.as_mut()) }
}
}
pub struct NgxCoreModule;
impl CoreModule for NgxCoreModule {
fn module() -> &'static ngx_module_t {
unsafe { &*core::ptr::addr_of!(nginx_sys::ngx_core_module) }
}
}
unsafe impl CoreModuleMainConf for NgxCoreModule {
type MainConf = ngx_core_conf_t;
}
#[cfg(test)]
mod tests {
extern crate alloc;
extern crate std;
use alloc::boxed::Box;
use core::ffi::c_void;
use core::mem::MaybeUninit;
use core::ptr::addr_of_mut;
use super::{CoreModule, CoreModuleConfExt, CoreModuleMainConf};
use crate::ffi::{ngx_conf_t, ngx_cycle_t, ngx_module_t};
type CoreConfSlot = *mut *mut *mut c_void;
fn module_with_index(index: usize) -> ngx_module_t {
let mut module = ngx_module_t::default();
module.index = index;
module
}
#[test]
fn null_conf_ctx_returns_none() {
let cycle: ngx_cycle_t = unsafe { MaybeUninit::zeroed().assume_init() };
let module = module_with_index(0);
assert!(unsafe { cycle.core_main_conf_unchecked::<u32>(&module) }.is_none());
}
#[test]
fn missing_module_slot_returns_none() {
let mut slots: [CoreConfSlot; 2] = [core::ptr::null_mut(); 2];
let mut cycle: ngx_cycle_t = unsafe { MaybeUninit::zeroed().assume_init() };
cycle.conf_ctx = slots.as_mut_ptr();
let module = module_with_index(1);
assert!(unsafe { cycle.core_main_conf_unchecked::<u32>(&module) }.is_none());
}
#[test]
fn populated_slot_returns_typed_reference() {
let mut value: u32 = 42;
let mut slots: [CoreConfSlot; 1] = [addr_of_mut!(value).cast()];
let mut cycle: ngx_cycle_t = unsafe { MaybeUninit::zeroed().assume_init() };
cycle.conf_ctx = slots.as_mut_ptr();
let mut conf: ngx_conf_t = unsafe { MaybeUninit::zeroed().assume_init() };
conf.cycle = addr_of_mut!(cycle);
let module = module_with_index(0);
let got = unsafe {
conf.core_main_conf_unchecked::<u32>(&module)
.map(|v| *v.as_ref())
};
assert_eq!(got, Some(42));
let got_mut = unsafe {
conf.core_main_conf_unchecked::<u32>(&module)
.map(|mut v| v.as_mut())
};
assert!(got_mut.is_some());
if let Some(v) = got_mut {
*v = 99;
}
assert_eq!(value, 99);
}
struct TestCoreModule;
impl CoreModule for TestCoreModule {
fn module() -> &'static ngx_module_t {
Box::leak(Box::new(module_with_index(0)))
}
}
unsafe impl CoreModuleMainConf for TestCoreModule {
type MainConf = u32;
}
#[test]
fn main_conf_trait_accessors_return_typed_references() {
let mut value: u32 = 42;
let mut slots: [CoreConfSlot; 1] = [addr_of_mut!(value).cast()];
let mut cycle: ngx_cycle_t = unsafe { MaybeUninit::zeroed().assume_init() };
cycle.conf_ctx = slots.as_mut_ptr();
let mut conf: ngx_conf_t = unsafe { MaybeUninit::zeroed().assume_init() };
conf.cycle = addr_of_mut!(cycle);
assert_eq!(TestCoreModule::main_conf(&conf).copied(), Some(42));
if let Some(v) = TestCoreModule::main_conf_mut(&conf) {
*v = 99;
}
assert_eq!(value, 99);
}
}