Multiple changes: - New contexts (schroot, unshare) - Cross-building quirks, with ephemeral contexts and repositories management - Contexts with parents, global context manager, better lifetime handling - Local building of binary packages - Pull: pulling dsc files by default - Many small bugfixes and changes Co-authored-by: Valentin Haudiquet <valentin.haudiquet@canonical.com> Co-committed-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>
This commit was merged in pull request #1.
This commit is contained in:
+91
-41
@@ -4,22 +4,39 @@ use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use super::api::Context;
|
||||
use super::api::{Context, ContextConfig};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
pub current_context: Option<String>,
|
||||
pub contexts: HashMap<String, Context>,
|
||||
pub context: String,
|
||||
pub contexts: HashMap<String, ContextConfig>,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
let mut contexts = HashMap::new();
|
||||
contexts.insert("local".to_string(), ContextConfig::Local);
|
||||
Self {
|
||||
context: "local".to_string(),
|
||||
contexts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContextManager {
|
||||
context: RwLock<Arc<Context>>,
|
||||
config_path: PathBuf,
|
||||
config: Config,
|
||||
config: RwLock<Config>,
|
||||
}
|
||||
|
||||
pub static MANAGER: std::sync::LazyLock<ContextManager> =
|
||||
std::sync::LazyLock::new(|| ContextManager::new().expect("Cannot setup context manager"));
|
||||
|
||||
impl ContextManager {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
fn new() -> io::Result<Self> {
|
||||
let proj_dirs = ProjectDirs::from("com", "pkh", "pkh").ok_or_else(|| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
@@ -31,67 +48,101 @@ impl ContextManager {
|
||||
let config_path = config_dir.join("contexts.json");
|
||||
|
||||
let config = if config_path.exists() {
|
||||
// Load existing configuration file
|
||||
let content = fs::read_to_string(&config_path)?;
|
||||
serde_json::from_str(&content)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
|
||||
} else {
|
||||
let mut cfg = Config::default();
|
||||
cfg.contexts.insert("local".to_string(), Context::Local);
|
||||
cfg.current_context = Some("local".to_string());
|
||||
cfg
|
||||
// Create a new configuration file
|
||||
Config::default()
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
context: RwLock::new(Arc::new(Self::make_context(
|
||||
config.context.as_str(),
|
||||
&config,
|
||||
))),
|
||||
config_path,
|
||||
config,
|
||||
config: RwLock::new(config),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_config(&self) -> std::sync::RwLockReadGuard<'_, Config> {
|
||||
self.config.read().unwrap()
|
||||
}
|
||||
|
||||
pub fn with_path(path: PathBuf) -> Self {
|
||||
let config = Config::default();
|
||||
Self {
|
||||
context: RwLock::new(Arc::new(Self::make_context("local", &config))),
|
||||
config_path: path,
|
||||
config: Config::default(),
|
||||
config: RwLock::new(config),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self) -> io::Result<()> {
|
||||
let content = serde_json::to_string_pretty(&self.config)
|
||||
let config = self.config.read().unwrap();
|
||||
let content = serde_json::to_string_pretty(&*config)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
fs::write(&self.config_path, content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn make_context(name: &str, config: &Config) -> Context {
|
||||
let context_config = config
|
||||
.contexts
|
||||
.get(name)
|
||||
.cloned()
|
||||
.expect("Context not found in config");
|
||||
Context::new(context_config)
|
||||
}
|
||||
|
||||
pub fn list_contexts(&self) -> Vec<String> {
|
||||
self.config.contexts.keys().cloned().collect()
|
||||
self.config
|
||||
.read()
|
||||
.unwrap()
|
||||
.contexts
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_context(&self, name: &str) -> Option<&Context> {
|
||||
self.config.contexts.get(name)
|
||||
}
|
||||
|
||||
pub fn add_context(&mut self, name: &str, context: Context) -> io::Result<()> {
|
||||
self.config.contexts.insert(name.to_string(), context);
|
||||
pub fn add_context(&self, name: &str, config: ContextConfig) -> io::Result<()> {
|
||||
self.config
|
||||
.write()
|
||||
.unwrap()
|
||||
.contexts
|
||||
.insert(name.to_string(), config);
|
||||
self.save()
|
||||
}
|
||||
|
||||
pub fn remove_context(&mut self, name: &str) -> io::Result<()> {
|
||||
if self.config.contexts.remove(name).is_some() {
|
||||
if self.config.current_context.as_deref() == Some(name) {
|
||||
self.config.current_context = Some("local".to_string());
|
||||
if !self.config.contexts.contains_key("local") {
|
||||
self.config
|
||||
.contexts
|
||||
.insert("local".to_string(), Context::Local);
|
||||
}
|
||||
pub fn remove_context(&self, name: &str) -> io::Result<()> {
|
||||
let mut config = self.config.write().unwrap();
|
||||
if name == "local" {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Cannot remove local context",
|
||||
));
|
||||
}
|
||||
if config.contexts.remove(name).is_some() {
|
||||
// If we are removing the current context, fallback to local
|
||||
if name == config.context {
|
||||
config.context = "local".to_string();
|
||||
self.set_current_ephemeral(Self::make_context("local", &config));
|
||||
}
|
||||
|
||||
drop(config); // Drop write lock before saving
|
||||
self.save()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_current(&mut self, name: &str) -> io::Result<()> {
|
||||
if self.config.contexts.contains_key(name) {
|
||||
self.config.current_context = Some(name.to_string());
|
||||
pub fn set_current(&self, name: &str) -> io::Result<()> {
|
||||
let mut config = self.config.write().unwrap();
|
||||
if config.contexts.contains_key(name) {
|
||||
config.context = name.to_string();
|
||||
self.set_current_ephemeral(Self::make_context(name, &config));
|
||||
drop(config); // Drop write lock before saving
|
||||
self.save()?;
|
||||
Ok(())
|
||||
} else {
|
||||
@@ -102,16 +153,15 @@ impl ContextManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current(&self) -> Context {
|
||||
self.config
|
||||
.current_context
|
||||
.as_deref()
|
||||
.and_then(|name| self.config.contexts.get(name))
|
||||
.cloned()
|
||||
.unwrap_or(Context::Local)
|
||||
pub fn set_current_ephemeral(&self, context: Context) {
|
||||
*self.context.write().unwrap() = context.into();
|
||||
}
|
||||
|
||||
pub fn current_name(&self) -> Option<String> {
|
||||
self.config.current_context.clone()
|
||||
pub fn current(&self) -> Arc<Context> {
|
||||
self.context.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn current_name(&self) -> String {
|
||||
self.config.read().unwrap().context.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user