use std::path::Path;
use crate::parsers::sexp::parse_channels_list;
use crate::types::Channel;
pub const DEFAULT_ETC_CHANNELS: &str = "/etc/guix/channels.scm";
pub const DEFAULT_PROVENANCE_CHANNELS: &str = "/run/current-system/channels.scm";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BaselineSource {
Etc,
Provenance,
Defaults,
}
#[derive(Debug, Clone)]
pub struct SystemBaseline {
pub channels: Vec<Channel>,
pub source: BaselineSource,
}
impl SystemBaseline {
pub async fn read(etc_override: Option<&Path>, provenance_override: Option<&Path>) -> Self {
let etc = etc_override
.map(Path::to_path_buf)
.unwrap_or_else(|| DEFAULT_ETC_CHANNELS.into());
let prov = provenance_override
.map(Path::to_path_buf)
.unwrap_or_else(|| DEFAULT_PROVENANCE_CHANNELS.into());
tokio::task::spawn_blocking(move || {
if let Some(chans) = read_channels_file(&etc) {
return SystemBaseline {
channels: chans,
source: BaselineSource::Etc,
};
}
if let Some(chans) = read_channels_file(&prov) {
return SystemBaseline {
channels: chans.into_iter().map(Channel::without_commit).collect(),
source: BaselineSource::Provenance,
};
}
SystemBaseline {
channels: Vec::new(),
source: BaselineSource::Defaults,
}
})
.await
.unwrap_or(SystemBaseline {
channels: Vec::new(),
source: BaselineSource::Defaults,
})
}
pub fn etc_present(&self) -> bool {
self.source == BaselineSource::Etc
}
pub fn contains(&self, name: &str) -> bool {
self.channels.iter().any(|c| c.name == name)
}
}
fn read_channels_file(path: &Path) -> Option<Vec<Channel>> {
let raw = std::fs::read_to_string(path).ok()?;
parse_channels_list(&raw)
.ok()
.map(crate::parsers::sexp::ChannelsList::into_channels)
}