use std::path::{Path, PathBuf};
use trusty_common::content::{
self, ContentError, ContentSource, DevOverride, ResolveOptions, find_dev_checkout,
};
use trusty_common::integrity::Sha256Digest;
use trusty_agents_common::agent_content::REMEDY;
use super::bundle_cache::manual_install;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstalledPin {
pub tag: String,
pub sha256: Sha256Digest,
}
#[derive(Debug)]
pub struct ContentStatus {
pub cache_dir: PathBuf,
pub dev: Option<PathBuf>,
pub installed: Result<InstalledPin, ContentError>,
}
pub fn content_status(cache_dir: &Path, start: Option<&Path>) -> ContentStatus {
let installed =
content::resolve(&ResolveOptions::new(cache_dir, DevOverride::Off)).and_then(|resolved| {
match resolved.source() {
ContentSource::Installed { tag, sha256, .. } => Ok(InstalledPin {
tag: tag.clone(),
sha256: sha256.clone(),
}),
_ => Err(ContentError::NotInstalled {
lock_path: cache_dir.join(content::LOCK_FILE_NAME),
}),
}
});
ContentStatus {
cache_dir: cache_dir.to_path_buf(),
dev: start.and_then(find_dev_checkout),
installed,
}
}
impl ContentStatus {
pub fn source_label(&self) -> &'static str {
match (&self.dev, &self.installed) {
(Some(_), _) => "dev",
(None, Ok(_)) => "bundle",
(None, Err(_)) => "none",
}
}
pub fn serves(&self) -> bool {
self.dev.is_some() || self.installed.is_ok()
}
pub fn not_installed(&self) -> bool {
matches!(self.installed, Err(ContentError::NotInstalled { .. }))
}
pub fn exits_ok(&self) -> bool {
self.serves()
}
pub fn lines(&self) -> Vec<String> {
let mut lines = vec![format!("source: {}", self.source_label())];
if let Some(root) = &self.dev {
lines.push(format!("dev checkout: {}", root.display()));
}
match &self.installed {
Ok(pin) => lines.push(format!(
"installed: {} sha256 {} (verified)",
pin.tag, pin.sha256
)),
Err(ContentError::NotInstalled { .. }) => {
lines.push(format!("installed: none — {REMEDY}"));
}
Err(e) => lines.push(format!("installed: UNHEALTHY — {e}; {}", remedy(e))),
}
lines.push(format!("cache: {}", self.cache_dir.display()));
lines.push(format!("binary: trusty-mpm {}", env!("CARGO_PKG_VERSION")));
lines
}
}
fn remedy(error: &ContentError) -> String {
match error {
ContentError::LockInvalid { .. } | ContentError::LockUnreadable { .. } => format!(
"the lock cannot be read, so replace it: run `tm content update --content-ref \
content-vX.Y.Z`, or {}",
manual_install(None)
),
ContentError::UnsupportedSchema { .. } => "upgrade tm to a release that reads this \
schema, or pin an older release with `tm content update --content-ref \
content-vX.Y.Z`"
.to_owned(),
_ => format!(
"run `tm content update` to fetch and pin the newest release, or {}",
manual_install(None)
),
}
}