work on exit commands
This commit is contained in:
parent
870ab86ba4
commit
f8083521e1
5 changed files with 106 additions and 87 deletions
|
|
@ -2,6 +2,7 @@ use clap::{Parser, Subcommand};
|
|||
#[derive(Subcommand)]
|
||||
pub enum CliAction {
|
||||
Enter,
|
||||
Exit,
|
||||
Allow,
|
||||
Disallow,
|
||||
Edit,
|
||||
|
|
|
|||
186
src/core.rs
186
src/core.rs
|
|
@ -64,109 +64,121 @@ impl Cade {
|
|||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
// TODO break this function up
|
||||
pub fn do_activation(&mut self) -> Result<()> {
|
||||
let mut working_dir = self.cwd.clone();
|
||||
if !self.get_permission(&working_dir)? {
|
||||
bail!("cade is not permitted to operate here; use 'cade allow'.");
|
||||
}
|
||||
|
||||
let base_env = std::env::vars()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
v.split(':')
|
||||
.map(|i| i.to_string())
|
||||
.collect::<HashSet<String>>(),
|
||||
)
|
||||
})
|
||||
.collect::<HashMap<String, HashSet<String>>>();
|
||||
|
||||
use crate::core::{read_cade, realise};
|
||||
let mut cade_files = HashMap::new();
|
||||
cade_files.insert(
|
||||
working_dir.clone(),
|
||||
read_cade(&working_dir.join(".cade")).context("reading cade file")?,
|
||||
);
|
||||
|
||||
// recurse into parent dirs
|
||||
while let Some(parent) = working_dir.parent()
|
||||
&& std::fs::exists(parent.join(".cade"))
|
||||
.context("check for .cade file in parent directory")?
|
||||
{
|
||||
working_dir = parent.to_path_buf();
|
||||
fn collect_cade_files(mut working_dir: PathBuf) -> Result<HashMap<PathBuf, Vec<Keyword>>> {
|
||||
use crate::core::read_cade;
|
||||
let mut cade_files = HashMap::new();
|
||||
cade_files.insert(
|
||||
working_dir.clone(),
|
||||
read_cade(&working_dir.join(".cade")).context("reading cade file")?,
|
||||
);
|
||||
|
||||
// recurse into parent dirs
|
||||
while let Some(parent) = working_dir.parent()
|
||||
&& std::fs::exists(parent.join(".cade"))
|
||||
.context("check for .cade file in parent directory")?
|
||||
{
|
||||
working_dir = parent.to_path_buf();
|
||||
cade_files.insert(
|
||||
working_dir.clone(),
|
||||
read_cade(&working_dir.join(".cade")).context("reading cade file")?,
|
||||
);
|
||||
}
|
||||
Ok(cade_files)
|
||||
}
|
||||
// TODO use faststr
|
||||
|
||||
fn rollup_envs(cade_layers: Vec<CadeLayer>) -> (HashMap<String, HashSet<String>>, bool) {
|
||||
let mut purified = false;
|
||||
let mut env = HashMap::new();
|
||||
let mut hooks = HashMap::new();
|
||||
for layer in cade_layers {
|
||||
for (k, v) in layer.envs.0 {
|
||||
env.entry(k)
|
||||
.and_modify(|iv: &mut HashSet<String>| {
|
||||
iv.extend(v.clone());
|
||||
})
|
||||
.or_insert(v);
|
||||
}
|
||||
if !purified && layer.purify {
|
||||
purified = true;
|
||||
}
|
||||
// FIXME not really happy with this impl of hooks
|
||||
// should each line just be a `call` ?
|
||||
for hook in layer.hooks {
|
||||
hooks
|
||||
.entry(hook.kind)
|
||||
.and_modify(|h: &mut Vec<String>| h.extend(hook.content.clone()))
|
||||
.or_insert(hook.content);
|
||||
}
|
||||
}
|
||||
|
||||
(env, purified)
|
||||
}
|
||||
|
||||
let mut env = HashMap::new();
|
||||
let cade_layers = realise(cade_files)?;
|
||||
let mut purified = false;
|
||||
|
||||
for layer in cade_layers {
|
||||
for (k, v) in layer.envs.0 {
|
||||
env.entry(k)
|
||||
.and_modify(|iv: &mut HashSet<String>| {
|
||||
iv.extend(v.clone());
|
||||
fn output_changes(env: HashMap<String, HashSet<String>>, purified: bool) {
|
||||
if purified {
|
||||
let base_env = std::env::vars()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
v.split(':')
|
||||
.map(|i| i.to_string())
|
||||
.collect::<HashSet<String>>(),
|
||||
)
|
||||
})
|
||||
.or_insert(v);
|
||||
.collect::<HashMap<String, HashSet<String>>>();
|
||||
for (k, _) in base_env {
|
||||
print!("set -u {k};")
|
||||
}
|
||||
}
|
||||
if layer.purify {
|
||||
// && !purified {
|
||||
// for (bk, bv) in base_env.iter() {
|
||||
// // FIXME is this even worth doing ?
|
||||
// // no, just unset them in the export..
|
||||
// env.entry(bk.clone()).and_modify(|v| {
|
||||
// *v = v
|
||||
// .iter()
|
||||
// .filter(|iv| !bv.contains(*iv))
|
||||
// .cloned()
|
||||
// .collect::<HashSet<String>>();
|
||||
// });
|
||||
// }
|
||||
purified = true;
|
||||
for (k, v) in env {
|
||||
let len = v.len();
|
||||
let value: String = v
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| {
|
||||
if i < len.saturating_sub(1) {
|
||||
[&s, ":"].concat()
|
||||
} else {
|
||||
s
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
print!("set -x -g '{k}' '{value}';");
|
||||
}
|
||||
// TODO hooks don't exist yet
|
||||
println!();
|
||||
}
|
||||
// if !purified {
|
||||
// for (bk, bv) in base_env {
|
||||
// env.entry(bk)
|
||||
// .and_modify(|v| {
|
||||
// v.extend(bv.clone());
|
||||
// })
|
||||
// .or_insert(bv);
|
||||
// }
|
||||
// }
|
||||
|
||||
let working_dir = self.cwd.clone();
|
||||
if !self.get_permission(&working_dir)? {
|
||||
bail!("cade is not permitted to operate here; use 'cade allow'.");
|
||||
}
|
||||
|
||||
let cade_files = collect_cade_files(working_dir)?;
|
||||
let cade_layers = load_envs(cade_files)?;
|
||||
let (env, purified) = rollup_envs(cade_layers);
|
||||
output_changes(env, purified);
|
||||
|
||||
// TODO save these env sets for the unexport hook
|
||||
// base_envs will have to be fully restored
|
||||
// while envs must be carefully subtraced
|
||||
if purified {
|
||||
for (k, _) in base_env {
|
||||
print!("set -u {k};")
|
||||
}
|
||||
}
|
||||
for (k, v) in env {
|
||||
let len = v.len();
|
||||
let value: String = v
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| {
|
||||
if i < len.saturating_sub(1) {
|
||||
[&s, ":"].concat()
|
||||
} else {
|
||||
s
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
print!("set -x -g '{k}' '{value}';");
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this is more complex than it seems -
|
||||
// we need to understand what layers are in place, where our root is, and determine
|
||||
// which ones should remain (in case we are leaving only an inner layer)
|
||||
// then diff those against the current environment and output the appropriate set commands
|
||||
//
|
||||
// this will play into the caching story also, naturally
|
||||
// perhaps we could store related stateful information into our own env vars
|
||||
//
|
||||
// alternatively, we just traverse to root and reinit up to our current dir..
|
||||
pub fn do_restore(&mut self) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ensure_dir() -> Result<PathBuf> {
|
||||
let mut path = if let Ok(xdg) = microxdg::Xdg::new()
|
||||
&& let Ok(state_dir) = xdg.state()
|
||||
|
|
@ -235,7 +247,7 @@ impl CadeLayer {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn realise(cascade: HashMap<PathBuf, Vec<Keyword>>) -> Result<Vec<CadeLayer>> {
|
||||
pub fn load_envs(cascade: HashMap<PathBuf, Vec<Keyword>>) -> Result<Vec<CadeLayer>> {
|
||||
let mut actions = Vec::new();
|
||||
use crate::loaders::*;
|
||||
for (layer_count, (path, keywords)) in cascade.into_iter().enumerate() {
|
||||
|
|
@ -260,6 +272,8 @@ pub fn realise(cascade: HashMap<PathBuf, Vec<Keyword>>) -> Result<Vec<CadeLayer>
|
|||
}?;
|
||||
layer.push_action(act);
|
||||
}
|
||||
layer.origin = path;
|
||||
layer.layer = layer_count;
|
||||
actions.push(layer);
|
||||
}
|
||||
Ok(actions)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod cli;
|
|||
mod core;
|
||||
mod envs;
|
||||
mod loaders;
|
||||
mod shells;
|
||||
mod types;
|
||||
|
||||
use std::ffi::OsString;
|
||||
|
|
@ -17,6 +18,8 @@ fn try_main() -> Result<()> {
|
|||
let mut cade = Cade::init()?;
|
||||
match args.action {
|
||||
Enter => cade.do_activation().context("activate cade environment")?,
|
||||
// TODO
|
||||
Exit => cade.do_restore().context("deactivate cade environment")?,
|
||||
Allow => cade.set_permission(true)?,
|
||||
Disallow => cade.set_permission(false)?,
|
||||
Edit => {
|
||||
|
|
|
|||
1
src/shells.rs
Normal file
1
src/shells.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
// TODO write an Output trait and impls for each shell
|
||||
|
|
@ -36,7 +36,7 @@ pub enum Loadable {
|
|||
Cass(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum HookType {
|
||||
LoadPre,
|
||||
LoadPost,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue