207 lines
6.4 KiB
Rust
207 lines
6.4 KiB
Rust
use crate::types::{CadeActions, Env, EnvSet, InnerHook};
|
|
use anyhow::{Context, Result, anyhow, bail};
|
|
use std::{
|
|
collections::HashMap,
|
|
ffi::OsString,
|
|
fmt::{Debug, Display},
|
|
io::{BufRead, Read},
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
#[repr(u8)]
|
|
#[derive(bincode::Encode, bincode::Decode, PartialEq, Debug)]
|
|
pub enum Permission {
|
|
Allowed,
|
|
Disallowed,
|
|
}
|
|
|
|
impl Display for Permission {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
Debug::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
pub fn load_flake(path: &Path, output: Option<String>) -> Result<RawEnv> {
|
|
let mut nix_cmd = String::from("nix print-dev-env --json");
|
|
if let Some(flake_output) = output {
|
|
nix_cmd.push_str(&[" ", &flake_output, " "].concat());
|
|
}
|
|
let mut proc = std::process::Command::new("sh");
|
|
// proc.env_clear();
|
|
proc.arg("-c");
|
|
proc.arg(nix_cmd);
|
|
proc.current_dir(path);
|
|
let output = proc
|
|
.output()
|
|
.with_context(|| format!("loading flake at {}", path.display()))?
|
|
.stdout;
|
|
parse_json(output)
|
|
}
|
|
|
|
pub fn load_shell(path: &Path, filename: String) -> Result<RawEnv> {
|
|
let mut nix_cmd = String::from("nix print-dev-env --json ");
|
|
// let mut nix_cmd = String::from("nix-shell ");
|
|
if !filename.is_empty() {
|
|
nix_cmd.push_str(&["-F ", &filename].concat());
|
|
} else {
|
|
nix_cmd.push_str(&["-F ./shell.nix"].concat());
|
|
}
|
|
// nix_cmd.push_str("--pure --command env --null");
|
|
let mut proc = std::process::Command::new("sh");
|
|
proc.env_clear();
|
|
proc.arg("-c");
|
|
proc.arg(nix_cmd);
|
|
proc.current_dir(path);
|
|
let output = proc
|
|
.output()
|
|
.with_context(|| format!("loading shell at {}", path.display()))?
|
|
.stdout;
|
|
EnvSet::from_json(output)?
|
|
}
|
|
|
|
pub fn load_env(path: &Path, filename: String) -> Result<EnvSet> {
|
|
let mut p = path.to_path_buf();
|
|
if filename.is_empty() {
|
|
p.push(".env");
|
|
} else {
|
|
p.push(filename);
|
|
}
|
|
let mut file = std::fs::File::open(p)
|
|
.with_context(|| format!("opening env file at {}", path.display()))?;
|
|
let mut buf = String::new();
|
|
file.read_to_string(&mut buf).context("reading env file")?;
|
|
parse_envs(buf)
|
|
}
|
|
|
|
pub fn call(path: &Path, argv: Vec<String>) -> Result<EnvSet> {
|
|
let mut it = argv.iter();
|
|
// safety: already checked at parsing
|
|
let mut process = std::process::Command::new(it.next().unwrap());
|
|
process.current_dir(path);
|
|
process.args(it);
|
|
let output = process
|
|
.output()
|
|
.with_context(|| format!("running process {}", argv.concat()))?
|
|
.stdout;
|
|
|
|
let text = String::from_utf8(output)
|
|
.with_context(|| format!("converting call {} output to text", argv.concat()))?
|
|
.replace(' ', "\0");
|
|
parse_envs(text)
|
|
}
|
|
|
|
// TODO cache results by hash/storepath and read from db so we don't
|
|
// need to eat eval every time
|
|
|
|
fn check_permission(dir: &Path) -> Permission {
|
|
// TODO check database for permission
|
|
todo!();
|
|
}
|
|
|
|
pub fn do_activation() -> Result<()> {
|
|
let dir = ensure_dir()?;
|
|
// TODO finish sqlite impl
|
|
let db = rusqlite::Connection::open(dir)?;
|
|
|
|
// let db = sled::open(dir).context("open database")?;
|
|
let mut cwd = std::env::current_dir().context("determine CWD")?;
|
|
let cwd_str = cwd
|
|
.clone()
|
|
.into_os_string()
|
|
.into_string()
|
|
.map_err(|_| anyhow!("cwd has invalid unicode"))?;
|
|
|
|
let permission = db.query_one(
|
|
"SELECT Permission FROM WorkingPaths WHERE Path=(:path)",
|
|
&[(":path", &cwd_str)],
|
|
|row| Ok(row.get(0).unwrap_or(false)),
|
|
)?;
|
|
if !permission {
|
|
bail!("cade is not permitted to operate here; use 'cade allow'.");
|
|
}
|
|
|
|
let base_env = std::env::vars().collect::<HashMap<String, String>>();
|
|
|
|
use crate::core::{read_cade, realise};
|
|
let mut cascade = HashMap::new();
|
|
cascade.insert(
|
|
cwd.clone(),
|
|
read_cade(&cwd.join(".cade")).context("reading cade file")?,
|
|
);
|
|
|
|
// recurse into parent dirs
|
|
while let Some(parent) = cwd.parent()
|
|
&& std::fs::exists(parent.join(".cade"))
|
|
.context("check for .cade file in parent directory")?
|
|
{
|
|
cwd = parent.to_path_buf();
|
|
cascade.insert(
|
|
cwd.clone(),
|
|
read_cade(&cwd.join(".cade")).context("reading cade file")?,
|
|
);
|
|
}
|
|
|
|
let mut env = HashMap::new();
|
|
let adjustments = realise(cascade)?;
|
|
use CadeActions::*;
|
|
for action in adjustments {
|
|
match action {
|
|
Environ(env_actions) => {
|
|
for action in env_actions {
|
|
env.entry(action.name)
|
|
.and_modify(|iv: &mut Vec<String>| {
|
|
iv.extend(action.value.clone());
|
|
})
|
|
.or_insert(action.value);
|
|
}
|
|
}
|
|
Purify => {
|
|
// FIXME this is a dumb way to purify an env
|
|
for (bk, bv) in base_env.iter() {
|
|
if env.get(bk).is_some_and(|inner| inner.contains(bv)) {
|
|
env.remove(bk);
|
|
}
|
|
}
|
|
}
|
|
Hook(hook) => match hook.kind {
|
|
_ => todo!(),
|
|
},
|
|
};
|
|
}
|
|
for (k, v) in env {
|
|
let value: String = v.into_iter().map(|s| [&s, ":"].concat()).collect();
|
|
println!("{k}={}", &value[..value.len() - 1]);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn set_permission(permission: Permission) -> Result<()> {
|
|
let dir = ensure_dir()?;
|
|
let db = sled::open(dir).context("open database")?;
|
|
let cwd = std::env::current_dir()
|
|
.context("determine CWD")?
|
|
.into_os_string();
|
|
let encoded_permission = bincode::encode_to_vec(&permission, bincode::config::standard())
|
|
.context("encode permission value")?;
|
|
|
|
let _ = db
|
|
.insert(cwd.into_encoded_bytes(), encoded_permission)
|
|
.context("update permissions database")?;
|
|
eprintln!(
|
|
"cade is now {} here.",
|
|
permission.to_string().to_lowercase()
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn ensure_dir() -> Result<PathBuf> {
|
|
let xdg = microxdg::Xdg::new().context("establish XDG paths")?;
|
|
let mut path = xdg.state().context("find xdg state dir")?;
|
|
path.push("cade");
|
|
if !std::fs::exists(&path).is_ok_and(|v| v) {
|
|
std::fs::create_dir(&path).context("create cade's state path")?;
|
|
}
|
|
// FIXME should this be appended after ? we only guarantee the dir here ..
|
|
path.push("permissions.db");
|
|
Ok(path)
|
|
}
|