use crate::embed::ext_php_rs_php_ini_builder_deinit;
use crate::ffi::{
php_ini_builder, php_ini_builder_define, php_ini_builder_prepend, php_ini_builder_quoted,
php_ini_builder_unquoted,
};
use crate::util::CStringScope;
use std::ffi::{CStr, NulError};
use std::fmt::Display;
use std::ops::Deref;
pub type IniBuilder = php_ini_builder;
impl Default for IniBuilder {
fn default() -> Self {
Self::new()
}
}
impl IniBuilder {
#[must_use]
pub fn new() -> IniBuilder {
IniBuilder {
value: std::ptr::null_mut(),
length: 0,
}
}
pub fn prepend<V: AsRef<str>>(&mut self, value: V) -> Result<(), NulError> {
let value = value.as_ref();
let raw = CStringScope::new(value)?;
unsafe {
php_ini_builder_prepend(self, *raw, value.len());
}
Ok(())
}
pub fn unquoted<N, V>(&mut self, name: N, value: V) -> Result<(), NulError>
where
N: AsRef<str>,
V: AsRef<str>,
{
let name = name.as_ref();
let value = value.as_ref();
let raw_name = CStringScope::new(name)?;
let raw_value = CStringScope::new(value)?;
unsafe {
php_ini_builder_unquoted(self, *raw_name, name.len(), *raw_value, value.len());
}
Ok(())
}
pub fn quoted<N, V>(&mut self, name: N, value: V) -> Result<(), NulError>
where
N: AsRef<str>,
V: AsRef<str>,
{
let name = name.as_ref();
let value = value.as_ref();
let raw_name = CStringScope::new(name)?;
let raw_value = CStringScope::new(value)?;
unsafe {
php_ini_builder_quoted(self, *raw_name, name.len(), *raw_value, value.len());
}
Ok(())
}
pub fn define<V: AsRef<str>>(&mut self, value: V) -> Result<(), NulError> {
let value = value.as_ref();
let raw = CStringScope::new(value)?;
unsafe {
php_ini_builder_define(self, *raw);
}
Ok(())
}
#[must_use]
pub fn finish(&self) -> &CStr {
unsafe {
if self.value.is_null() {
return c"";
}
self.value.add(self.length).write(0);
CStr::from_ptr(self.value)
}
}
}
impl Display for IniBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let content: &str = self.as_ref();
write!(f, "{content}")
}
}
impl Deref for IniBuilder {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl From<IniBuilder> for String {
fn from(builder: IniBuilder) -> Self {
let temp: &str = builder.as_ref();
temp.to_string()
}
}
impl AsRef<CStr> for IniBuilder {
fn as_ref(&self) -> &CStr {
self.finish()
}
}
impl AsRef<str> for IniBuilder {
fn as_ref(&self) -> &str {
self.finish().to_str().unwrap_or("")
}
}
impl Drop for IniBuilder {
fn drop(&mut self) {
if !self.value.is_null() {
unsafe {
ext_php_rs_php_ini_builder_deinit(self);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ini_builder_prepend() {
let mut builder = IniBuilder::new();
builder.prepend("foo=bar").expect("should prepend value");
let ini = builder.finish();
assert!(!ini.is_empty());
assert_eq!(ini, c"foo=bar");
}
#[test]
fn test_ini_builder_unquoted() {
let mut builder = IniBuilder::new();
builder
.unquoted("baz", "qux")
.expect("should add unquoted value");
let ini = builder.finish();
assert!(!ini.is_empty());
assert_eq!(ini, c"baz=qux\n");
}
#[test]
fn test_ini_builder_quoted() {
let mut builder = IniBuilder::new();
builder
.quoted("quux", "corge")
.expect("should add quoted value");
let ini = builder.finish();
assert!(!ini.is_empty());
assert_eq!(ini, c"quux=\"corge\"\n");
}
#[test]
fn test_ini_builder_define() {
let mut builder = IniBuilder::new();
builder
.define("grault=garply")
.expect("should define value");
let ini = builder.finish();
assert!(!ini.is_empty());
assert_eq!(ini, c"grault=garply\n");
}
#[test]
fn test_ini_builder_null_byte_error() {
let mut builder = IniBuilder::new();
assert!(builder.prepend("key=val\0ue").is_err());
}
#[test]
fn test_ini_builder_empty_values() {
let mut builder = IniBuilder::new();
assert!(builder.unquoted("", "").is_ok());
}
}