use crate::debian::apt::AptManager;
use crate::dependencies::debian::DebianDependency;
use crate::dependency::Dependency;
use crate::installer::{Error, Explanation, InstallationScope, Installer};
use crate::session::Session;
use reqwest::StatusCode;
use tokio::runtime::Runtime;
use url::Url;
async fn resolve_apt_requirement_dep_server(
url: &url::Url,
_dep: &dyn Dependency,
) -> Result<Option<DebianDependency>, Error> {
let client = reqwest::Client::new();
let response = client
.post(url.join("resolve-apt").unwrap())
.json(&serde_json::json!( {
"requirement": {
}
}))
.send()
.await
.unwrap();
match response.status() {
StatusCode::NOT_FOUND => {
if response
.headers()
.get("Reason")
.map(|x| x.to_str().unwrap())
== Some("family-unknown")
{
return Err(Error::UnknownDependencyFamily);
}
Ok(None)
}
StatusCode::OK => {
let body = response.json::<DebianDependency>().await.unwrap();
Ok(Some(body))
}
_ => {
panic!("Unexpected response status: {}", response.status());
}
}
}
pub struct DepServerAptInstaller<'a> {
apt: AptManager<'a>,
dep_server_url: Url,
}
impl<'a> DepServerAptInstaller<'a> {
pub fn new(apt: AptManager<'a>, dep_server_url: &Url) -> Self {
Self {
apt,
dep_server_url: dep_server_url.clone(),
}
}
pub fn from_session(session: &'a dyn Session, dep_server_url: &'_ Url) -> Self {
let apt = AptManager::from_session(session);
Self::new(apt, dep_server_url)
}
pub fn resolve(&self, req: &dyn Dependency) -> Result<Option<DebianDependency>, Error> {
let rt = Runtime::new().unwrap();
match rt.block_on(resolve_apt_requirement_dep_server(
&self.dep_server_url,
req,
)) {
Ok(deps) => Ok(deps),
Err(o) => {
log::warn!("Falling back to resolving error locally");
Err(Error::Other(o.to_string()))
}
}
}
}
impl<'a> Installer for DepServerAptInstaller<'a> {
fn install(
&self,
dep: &dyn Dependency,
scope: crate::installer::InstallationScope,
) -> Result<(), Error> {
match scope {
InstallationScope::User => {
return Err(Error::UnsupportedScope(scope));
}
InstallationScope::Global => {}
InstallationScope::Vendor => {
return Err(Error::UnsupportedScope(scope));
}
}
let dep = self.resolve(dep)?;
if let Some(dep) = dep {
match self
.apt
.satisfy(vec![crate::debian::apt::SatisfyEntry::Required(
dep.relation_string(),
)]) {
Ok(_) => {}
Err(e) => {
return Err(Error::Other(e.to_string()));
}
}
Ok(())
} else {
Err(Error::UnknownDependencyFamily)
}
}
fn explain(
&self,
dep: &dyn Dependency,
scope: crate::installer::InstallationScope,
) -> Result<crate::installer::Explanation, Error> {
match scope {
InstallationScope::User => {
return Err(Error::UnsupportedScope(scope));
}
InstallationScope::Global => {}
InstallationScope::Vendor => {
return Err(Error::UnsupportedScope(scope));
}
}
let dep = self.resolve(dep)?;
let dep = dep.ok_or_else(|| Error::UnknownDependencyFamily)?;
let apt_deb_str = dep.relation_string();
let cmd = self.apt.satisfy_command(vec![apt_deb_str.as_str()]);
Ok(Explanation {
message: format!(
"Install {}",
dep.package_names()
.iter()
.map(|x| x.as_str())
.collect::<Vec<_>>()
.join(", ")
),
command: Some(cmd.iter().map(|s| s.to_string()).collect()),
})
}
}