#![allow(private_interfaces)]
use std::{
ffi::{CString, c_int},
path::PathBuf,
};
use crate::{
Error, Handle,
config::private::ConfigPrivateExt,
ffi::Flag,
pool::{Pool, Source},
};
pub trait Config: ConfigPrivateExt + Sized {
fn build_handle(self) -> Result<Handle, Error> {
let flags = self.flags();
self.into_source()?.create_handle(flags, None)
}
fn build_pool(self) -> Result<Pool, Error> {
Pool::new(self.flags(), self.into_source()?)
}
fn remove_flag(self, flag: Flag) -> Self;
fn set_flag(self, flag: Flag) -> Self;
}
pub(crate) mod private {
use super::*;
pub trait ConfigPrivateExt {
fn flags(&self) -> c_int;
fn into_source(self) -> Result<Source, Error>;
}
}
#[derive(Debug, Clone)]
pub struct DefaultConfig {
flags: c_int,
}
impl DefaultConfig {
fn _remove_flag(&mut self, flag: Flag) {
self.flags &= !(flag as c_int);
}
fn _set_flag(&mut self, flag: Flag) {
self.flags |= flag as c_int;
}
}
impl Config for DefaultConfig {
fn remove_flag(mut self, flag: Flag) -> Self {
self._remove_flag(flag);
self
}
fn set_flag(mut self, flag: Flag) -> Self {
self._set_flag(flag);
self
}
}
impl ConfigPrivateExt for DefaultConfig {
fn flags(&self) -> c_int {
self.flags
}
fn into_source(self) -> Result<Source, Error> {
Ok(Source::Default)
}
}
impl Default for DefaultConfig {
fn default() -> Self {
Self {
flags: Flag::Error as c_int,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BufferConfig {
config: DefaultConfig,
buffers: Vec<Vec<u8>>,
}
impl BufferConfig {
pub fn with_buffer(mut self, buffer: &[u8]) -> Self {
self.buffers.push(buffer.to_vec());
self
}
}
impl Config for BufferConfig {
fn remove_flag(mut self, flag: Flag) -> Self {
self.config._remove_flag(flag);
self
}
fn set_flag(mut self, flag: Flag) -> Self {
self.config._set_flag(flag);
self
}
}
impl ConfigPrivateExt for BufferConfig {
fn flags(&self) -> c_int {
self.config.flags
}
fn into_source(self) -> Result<Source, Error> {
Ok(Source::Buffers(self.buffers.into()))
}
}
#[derive(Debug, Clone, Default)]
pub struct FileConfig {
config: DefaultConfig,
paths: Vec<PathBuf>,
}
impl FileConfig {
pub fn with_file(mut self, path: impl Into<PathBuf>) -> Self {
self.paths.push(path.into());
self
}
}
impl Config for FileConfig {
fn remove_flag(mut self, flag: Flag) -> Self {
self.config._remove_flag(flag);
self
}
fn set_flag(mut self, flag: Flag) -> Self {
self.config._set_flag(flag);
self
}
}
impl ConfigPrivateExt for FileConfig {
fn flags(&self) -> c_int {
self.config.flags
}
fn into_source(self) -> Result<Source, Error> {
self.paths
.into_iter()
.try_fold(Vec::new(), |mut acc, path| {
if !acc.is_empty() {
acc.push(b':');
}
let bytes = path.into_os_string().into_encoded_bytes();
if bytes.contains(&b':') {
Err(Error::EmbeddedColons)
} else {
acc.extend(bytes);
Ok(acc)
}
})
.map(|bytes| {
CString::new(bytes)
.map(Source::Files)
.map_err(|_| Error::EmbeddedNuls)
})?
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn flags() {
let config = DefaultConfig::default();
assert_eq!(config.flags, Flag::Error as c_int);
let builder = config.set_flag(Flag::Apple).set_flag(Flag::Check);
assert_eq!(
builder.flags,
(Flag::Error as c_int) | (Flag::Apple as c_int) | (Flag::Check as c_int)
);
let builder = builder.remove_flag(Flag::Error);
assert_eq!(
builder.flags,
(Flag::Apple as c_int) | (Flag::Check as c_int)
);
}
}