use url::Url;
use super::{PacEvaluator, PacPolicy, PacScript};
use crate::config::ProxyConfig;
use crate::error::Error;
use crate::resolve::ProxyStep;
#[cfg(pac_native)]
use super::NativePacResolver as Native;
pub struct PacResolver {
policy: PacPolicy,
evaluator: Option<Box<dyn PacEvaluator + Send + Sync>>,
#[cfg(pac_native)]
native: Option<Native>,
}
impl PacResolver {
#[must_use]
pub fn new(policy: PacPolicy) -> Self {
Self {
policy,
evaluator: None,
#[cfg(pac_native)]
native: None,
}
}
#[cfg(pac_native)]
#[must_use]
pub fn with_native(mut self, native: Native) -> Self {
self.native = Some(native);
self
}
pub fn with_system_native(self) -> Result<Self, Error> {
#[cfg(pac_native)]
let resolver = self.with_native(open_native()?);
#[cfg(not(pac_native))]
let resolver = self;
Ok(resolver)
}
#[cfg(pac_native)]
#[must_use]
pub fn native(&self) -> Option<&Native> {
self.native.as_ref()
}
#[must_use]
pub fn with_evaluator(mut self, evaluator: impl PacEvaluator + Send + Sync + 'static) -> Self {
self.evaluator = Some(Box::new(evaluator));
self
}
#[must_use]
pub fn policy(&self) -> &PacPolicy {
&self.policy
}
pub fn resolve_config(
&self,
config: &ProxyConfig,
url: &Url,
script: Option<&PacScript>,
) -> Result<Vec<ProxyStep>, Error> {
#[cfg(pac_native)]
if let Some(native) = &self.native
&& script.is_none()
&& matches!(
config.effective,
crate::ProxyMode::Pac { .. } | crate::ProxyMode::WpadAutoDetect
)
{
return native.resolve_config(config, url);
}
crate::resolve::resolve_with_pac_using(config, url, script, |script| {
match &self.evaluator {
Some(evaluator) => evaluator.evaluate(
script,
&super::sanitize_url(url),
url.host_str().unwrap_or_default(),
),
None => super::evaluate(script, url, &self.policy),
}
})
}
}
#[cfg(all(windows, feature = "pac-windows-native"))]
fn open_native() -> Result<Native, Error> {
Native::new()
}
#[cfg(all(pac_native, not(windows)))]
fn open_native() -> Result<Native, Error> {
Ok(Native::new())
}
impl std::fmt::Debug for PacResolver {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("PacResolver");
debug
.field("policy", &self.policy)
.field("evaluator", &self.evaluator.as_ref().map(|_| "attached"));
#[cfg(pac_native)]
debug.field("native", &self.native);
debug.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ProxyConfigSource, ProxyMode};
#[test]
fn attaching_the_system_native_keeps_direct_direct() {
let resolver = PacResolver::new(PacPolicy::new())
.with_system_native()
.unwrap();
#[cfg(pac_native)]
assert!(resolver.native().is_some());
let config = ProxyConfig::from_source(ProxyConfigSource::Env, ProxyMode::Direct);
let url = Url::parse("http://example.net/").unwrap();
assert_eq!(
resolver.resolve_config(&config, &url, None).unwrap(),
vec![ProxyStep::Direct]
);
}
}