use std::fmt::Debug;
use std::marker::PhantomData;
use crate::controls::{self, Control, ControlType};
use crate::ldap::Ldap;
use crate::result::{LdapError, LdapResult, Result};
use crate::search::parse_refs;
use crate::search::{ResultEntry, Scope, SearchStream};
use async_trait::async_trait;
#[async_trait]
pub trait Adapter<'a, S, A>: AdapterClone<'a, S, A> + Debug + Send + Sync + 'a {
async fn start(
&mut self,
stream: &mut SearchStream<'a, S, A>,
base: &str,
scope: Scope,
filter: &str,
attrs: A,
) -> Result<()>;
async fn next(&mut self, stream: &mut SearchStream<'a, S, A>) -> Result<Option<ResultEntry>>;
async fn finish(&mut self, stream: &mut SearchStream<'a, S, A>) -> LdapResult;
}
pub trait AdapterClone<'a, S, A> {
fn box_clone(&self) -> Box<dyn Adapter<'a, S, A> + 'a>;
}
impl<'a, S, A, T> AdapterClone<'a, S, A> for T
where
T: Adapter<'a, S, A> + Clone + 'a,
{
fn box_clone(&self) -> Box<dyn Adapter<'a, S, A> + 'a> {
Box::new(self.clone())
}
}
pub trait SoloMarker {}
pub trait IntoAdapterVec<'a, S, A> {
fn into(self) -> Vec<Box<dyn Adapter<'a, S, A> + 'a>>;
}
impl<'a, S, A> IntoAdapterVec<'a, S, A> for Vec<Box<dyn Adapter<'a, S, A> + 'a>> {
fn into(self) -> Vec<Box<dyn Adapter<'a, S, A> + 'a>> {
self
}
}
impl<'a, Ad, S, A> IntoAdapterVec<'a, S, A> for Ad
where
Ad: Adapter<'a, S, A> + SoloMarker,
S: AsRef<str> + Send + Sync + 'a,
A: AsRef<[S]> + Send + Sync + 'a,
{
fn into(self) -> Vec<Box<dyn Adapter<'a, S, A> + 'a>> {
vec![Box::new(self)]
}
}
#[derive(Clone, Debug)]
pub struct EntriesOnly {
refs: Vec<String>,
}
#[allow(clippy::new_without_default)]
impl EntriesOnly {
pub fn new() -> Self {
Self { refs: vec![] }
}
}
impl SoloMarker for EntriesOnly {}
#[async_trait]
impl<'a, S, A> Adapter<'a, S, A> for EntriesOnly
where
S: AsRef<str> + Send + Sync + 'a,
A: AsRef<[S]> + Send + Sync + 'a,
{
async fn start(
&mut self,
stream: &mut SearchStream<'a, S, A>,
base: &str,
scope: Scope,
filter: &str,
attrs: A,
) -> Result<()> {
self.refs.clear();
stream.start(base, scope, filter, attrs).await
}
async fn next(&mut self, stream: &mut SearchStream<'a, S, A>) -> Result<Option<ResultEntry>> {
loop {
return match stream.next().await {
Ok(None) => Ok(None),
Ok(Some(re)) => {
if re.is_intermediate() {
continue;
} else if re.is_ref() {
self.refs.extend(parse_refs(re.0));
continue;
} else {
Ok(Some(re))
}
}
Err(e) => Err(e),
};
}
}
async fn finish(&mut self, stream: &mut SearchStream<'a, S, A>) -> LdapResult {
let mut res = stream.finish().await;
res.refs.extend(std::mem::take(&mut self.refs));
res
}
}
#[derive(Clone, Debug)]
pub struct PagedResults<S: AsRef<str>, A> {
page_size: i32,
ldap: Option<Ldap>,
base: String,
scope: Scope,
filter: String,
attrs: Option<A>,
_s: PhantomData<S>,
}
impl<S, A> SoloMarker for PagedResults<S, A>
where
S: AsRef<str> + Send + Sync,
A: AsRef<[S]> + Send + Sync,
{
}
impl<S, A> PagedResults<S, A>
where
S: AsRef<str> + Send + Sync,
A: AsRef<[S]> + Send + Sync,
{
pub fn new(page_size: i32) -> Self {
Self {
page_size,
ldap: None,
base: String::from(""),
scope: Scope::Base,
filter: String::from(""),
attrs: None,
_s: PhantomData,
}
}
}
#[async_trait]
impl<'a, S, A> Adapter<'a, S, A> for PagedResults<S, A>
where
S: AsRef<str> + Clone + Debug + Send + Sync + 'a,
A: AsRef<[S]> + Clone + Debug + Send + Sync + 'a,
{
async fn start(
&mut self,
stream: &mut SearchStream<'a, S, A>,
base: &str,
scope: Scope,
filter: &str,
attrs: A,
) -> Result<()> {
let stream_ldap = stream.ldap_handle();
let mut ldap = stream_ldap.clone();
ldap.timeout = stream_ldap.timeout;
ldap.search_opts = stream_ldap.search_opts.clone();
let empty_ctrls = vec![];
let mut found_pr = false;
let mut controls: Vec<_> = stream_ldap
.controls
.as_ref()
.unwrap_or(&empty_ctrls)
.iter()
.filter(|c| {
if c.ctype == "1.2.840.113556.1.4.319" {
found_pr = true;
false
} else {
true
}
})
.cloned()
.collect();
if found_pr {
return Err(LdapError::AdapterInit(String::from(
"found Paged Results control in op set",
)));
}
ldap.controls = Some(controls.clone());
controls.push(
controls::PagedResults {
size: self.page_size,
cookie: vec![],
}
.into(),
);
stream.ldap.controls = Some(controls);
self.ldap = Some(ldap);
self.base = String::from(base);
self.scope = scope;
self.filter = String::from(filter);
self.attrs = Some(attrs.clone());
stream.start(base, scope, filter, attrs).await
}
async fn next(&mut self, stream: &mut SearchStream<'a, S, A>) -> Result<Option<ResultEntry>> {
'ent: loop {
match stream.next().await {
Ok(None) => {
let mut pr_index = None;
let ctrls = if let Some(res_ref) = stream.res.as_mut() {
&mut res_ref.ctrls
} else {
return Ok(None);
};
for (cno, ctrl) in ctrls.iter().enumerate() {
if let Control(Some(ControlType::PagedResults), ref raw) = *ctrl {
pr_index = Some(cno);
let pr: controls::PagedResults = raw.parse();
if pr.cookie.is_empty() {
break;
}
let ldap_ref = self.ldap.as_ref().expect("ldap_ref");
let mut ldap = ldap_ref.clone();
ldap.timeout = ldap_ref.timeout;
ldap.search_opts = ldap_ref.search_opts.clone();
let mut controls = ldap_ref.controls.clone().expect("saved ctrls");
controls.push(
controls::PagedResults {
size: self.page_size,
cookie: pr.cookie.clone(),
}
.into(),
);
ldap.controls = Some(controls);
let new_stream = match ldap
.streaming_search(
&self.base,
self.scope,
&self.filter,
self.attrs.as_ref().unwrap(),
)
.await
{
Ok(strm) => strm,
Err(e) => return Err(e),
};
stream.ldap = new_stream.ldap;
stream.rx = new_stream.rx;
continue 'ent;
}
}
if let Some(pr_index) = pr_index {
ctrls.remove(pr_index);
}
return Ok(None);
}
any => return any,
}
}
}
async fn finish(&mut self, stream: &mut SearchStream<'a, S, A>) -> LdapResult {
stream.finish().await
}
}