working refactor 1
This commit is contained in:
parent
93b2b5c512
commit
eaaecb831f
8 changed files with 255 additions and 209 deletions
39
Cargo.lock
generated
39
Cargo.lock
generated
|
|
@ -114,6 +114,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_json",
|
||||
"sled",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -280,6 +281,17 @@ version = "0.2.175"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"libc",
|
||||
"redox_syscall 0.5.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.35.0"
|
||||
|
|
@ -344,7 +356,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
|
@ -382,6 +394,15 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.37.0"
|
||||
|
|
@ -520,6 +541,22 @@ version = "0.0.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
||||
|
||||
[[package]]
|
||||
name = "wasite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"wasite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@ rusqlite = "0.37.0"
|
|||
serde = { version = "1.0.226", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
sled = "0.34.7"
|
||||
whoami = { version = "1.6.1", default-features = false }
|
||||
|
|
|
|||
130
src/actions.rs
130
src/actions.rs
|
|
@ -21,7 +21,7 @@ impl Display for Permission {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_flake(path: &Path, output: Option<String>) -> Result<RawEnv> {
|
||||
pub fn load_flake(path: &Path, output: Option<String>) -> Result<EnvSet> {
|
||||
let mut nix_cmd = String::from("nix print-dev-env --json");
|
||||
if let Some(flake_output) = output {
|
||||
nix_cmd.push_str(&[" ", &flake_output, " "].concat());
|
||||
|
|
@ -35,28 +35,24 @@ pub fn load_flake(path: &Path, output: Option<String>) -> Result<RawEnv> {
|
|||
.output()
|
||||
.with_context(|| format!("loading flake at {}", path.display()))?
|
||||
.stdout;
|
||||
parse_json(output)
|
||||
EnvSet::from_json(&output)
|
||||
}
|
||||
|
||||
pub fn load_shell(path: &Path, filename: String) -> Result<RawEnv> {
|
||||
pub fn load_shell(path: &Path, filename: String) -> Result<EnvSet> {
|
||||
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");
|
||||
let mut proc = std::process::Command::new(nix_cmd);
|
||||
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)?
|
||||
EnvSet::from_json(&output)
|
||||
}
|
||||
|
||||
pub fn load_env(path: &Path, filename: String) -> Result<EnvSet> {
|
||||
|
|
@ -70,7 +66,7 @@ pub fn load_env(path: &Path, filename: String) -> Result<EnvSet> {
|
|||
.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)
|
||||
EnvSet::from_envs(&buf)
|
||||
}
|
||||
|
||||
pub fn call(path: &Path, argv: Vec<String>) -> Result<EnvSet> {
|
||||
|
|
@ -84,10 +80,10 @@ pub fn call(path: &Path, argv: Vec<String>) -> Result<EnvSet> {
|
|||
.with_context(|| format!("running process {}", argv.concat()))?
|
||||
.stdout;
|
||||
|
||||
// FIXME locale ?
|
||||
let text = String::from_utf8(output)
|
||||
.with_context(|| format!("converting call {} output to text", argv.concat()))?
|
||||
.replace(' ', "\0");
|
||||
parse_envs(text)
|
||||
.with_context(|| format!("converting call {} output to text", argv.concat()))?;
|
||||
EnvSet::from_envs(&text)
|
||||
}
|
||||
|
||||
// TODO cache results by hash/storepath and read from db so we don't
|
||||
|
|
@ -98,110 +94,4 @@ fn check_permission(dir: &Path) -> 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)
|
||||
}
|
||||
// FIXME TODO make these methods on Cade struct
|
||||
|
|
|
|||
|
|
@ -44,12 +44,19 @@ impl FromStr for Keyword {
|
|||
Some("shell") => {
|
||||
Load(Loadable::Shell(s.get(11..).unwrap_or("").to_string()))
|
||||
}
|
||||
|
||||
Some("flake") => {
|
||||
Load(Loadable::Flake(s.get(11..).unwrap_or("").to_string()))
|
||||
}
|
||||
|
||||
Some("env") => {
|
||||
Load(Loadable::Env(s.get(9..).unwrap_or("").to_string()))
|
||||
}
|
||||
|
||||
Some("cass") => {
|
||||
Load(Loadable::Cass(s.get(10..).unwrap_or("").to_string()))
|
||||
}
|
||||
|
||||
Some(_) => return Err(ParseError::UnknownLoadable),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
137
src/core.rs
137
src/core.rs
|
|
@ -1,11 +1,146 @@
|
|||
use crate::types::{CadeActions, Keyword, Loadable};
|
||||
use anyhow::{Context, Result, anyhow};
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use rusqlite::named_params;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::BufRead,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub struct Cade {
|
||||
db: rusqlite::Connection,
|
||||
layers: HashMap<u8, CadeLayer>,
|
||||
cwd: PathBuf,
|
||||
}
|
||||
|
||||
impl Cade {
|
||||
pub fn init() -> anyhow::Result<Cade> {
|
||||
let mut db_path = Cade::ensure_dir()?;
|
||||
db_path.push("cade.db");
|
||||
let mut db = rusqlite::Connection::open(db_path)?;
|
||||
Cade::ensure_db(&mut db)?;
|
||||
Ok(Self {
|
||||
db,
|
||||
layers: HashMap::new(),
|
||||
cwd: std::env::current_dir().context("determine cwd")?,
|
||||
})
|
||||
}
|
||||
|
||||
fn ensure_db(conn: &mut rusqlite::Connection) -> Result<()> {
|
||||
conn.execute(
|
||||
"
|
||||
CREATE TABLE IF NOT EXISTS WorkingPaths (
|
||||
Path TEXT PRIMARY KEY,
|
||||
Permission INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
",
|
||||
[],
|
||||
)
|
||||
.context("create table in database")
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn set_permission(&mut self, permission: bool) -> Result<()> {
|
||||
self.db.execute(
|
||||
"INSERT OR REPLACE INTO WorkingPaths (Permission) VALUES ((:cwd), (:perm));",
|
||||
named_params! {
|
||||
":cwd": self.cwd.to_str().context("parse cwd as unicode")?,
|
||||
":perm": permission,
|
||||
},
|
||||
)?;
|
||||
eprintln!(
|
||||
"cade is now {} here.",
|
||||
if permission { "allowed" } else { "disallowed" }
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_activation(&mut self) -> Result<()> {
|
||||
let permission = self.db.query_one(
|
||||
"SELECT Permission FROM WorkingPaths WHERE Path=(:path)",
|
||||
&[(":path", &self.cwd.to_str().context("parse cwd as unicode")?)],
|
||||
|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(())
|
||||
}
|
||||
|
||||
fn ensure_dir() -> Result<PathBuf> {
|
||||
let mut path = if let Ok(xdg) = microxdg::Xdg::new()
|
||||
&& let Ok(state_dir) = xdg.state()
|
||||
{
|
||||
state_dir
|
||||
} else {
|
||||
let mut p = PathBuf::new();
|
||||
p.push("home");
|
||||
p.push(whoami::username());
|
||||
p.push(".state");
|
||||
p
|
||||
};
|
||||
path.push("cade");
|
||||
|
||||
if !std::fs::exists(&path).is_ok_and(|v| v) {
|
||||
std::fs::create_dir(&path).context("create cade state path")?;
|
||||
}
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_cade(path: &Path) -> Result<Vec<Keyword>> {
|
||||
let contents = std::fs::read(path).context("reading cade file")?;
|
||||
let mut accum = Vec::new();
|
||||
|
|
|
|||
78
src/envs.rs
78
src/envs.rs
|
|
@ -1,30 +1,8 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::types::EnvSet;
|
||||
use anyhow::{Result, anyhow};
|
||||
use serde_json::Value;
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
|
||||
pub enum EnvProviders {
|
||||
RawEnvs,
|
||||
NixJson,
|
||||
Cade,
|
||||
}
|
||||
pub enum EnvSetParseErr {
|
||||
Bad,
|
||||
}
|
||||
|
||||
impl FromStr for EnvSet {
|
||||
type Err = EnvSetParseErr;
|
||||
}
|
||||
|
||||
pub fn parse(text: &str) -> Result<EnvSet> {}
|
||||
|
||||
pub trait EnvProvider {
|
||||
fn parse(text: &str) -> Result<EnvSet>;
|
||||
}
|
||||
|
||||
impl EnvProvider for RawEnvs {
|
||||
fn parse(text: &str) -> Result<EnvSet> {
|
||||
impl EnvSet {
|
||||
pub fn from_envs(text: &str) -> Result<EnvSet> {
|
||||
let mut envs = Vec::new();
|
||||
for line in text.lines() {
|
||||
let split: Vec<&str> = line.split('=').collect();
|
||||
|
|
@ -35,11 +13,57 @@ impl EnvProvider for RawEnvs {
|
|||
envs.push((key, values));
|
||||
}
|
||||
_ => {
|
||||
return Err(anyhow!("parsing variable from {text}"));
|
||||
bail!("parsing variable from {text}")
|
||||
} // 1 => Clear(split[0].to_string()),
|
||||
// _ => Add(split[0].to_string(), split[1..].concat().to_string()),
|
||||
};
|
||||
}
|
||||
Ok(envs)
|
||||
Ok(EnvSet(envs))
|
||||
}
|
||||
pub fn from_json(raw: &[u8]) -> Result<EnvSet> {
|
||||
let json: serde_json::Value = serde_json::from_slice(raw).context("parsing json")?;
|
||||
if json.is_object()
|
||||
&& let Some(all_vars) = json.get("variables")
|
||||
{
|
||||
let vars = all_vars
|
||||
.as_object()
|
||||
.map(|inner| {
|
||||
inner
|
||||
.iter()
|
||||
.filter(|(var, _)| match var.as_str() {
|
||||
"NIX_BUILD_TOP"
|
||||
| "NIX_BUILD_CORES"
|
||||
| "NIX_STORE"
|
||||
| "TEMP"
|
||||
| "TEMPDIR"
|
||||
| "TMP"
|
||||
| "TMPDIR"
|
||||
| "builder"
|
||||
| "out"
|
||||
| "stdenv"
|
||||
| "system"
|
||||
| "dontAddDisableDepTrack"
|
||||
| "outputs" => false,
|
||||
_ => true,
|
||||
})
|
||||
.filter_map(|var| {
|
||||
Some((var.0.to_string(), var.1.get("value")?.as_str()?.to_owned()))
|
||||
})
|
||||
.map(|(name, value)| {
|
||||
(
|
||||
name,
|
||||
value
|
||||
.split(':')
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.context("collecting env vars")?;
|
||||
Ok(EnvSet(vars))
|
||||
} else {
|
||||
Err(anyhow!("failed to parse values from JSON output"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,21 @@ mod types;
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::OsString,
|
||||
fmt::{Debug, Display},
|
||||
io::{BufRead, Read},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use clap::Parser;
|
||||
use rusqlite::named_params;
|
||||
|
||||
use crate::types::CadeLayer;
|
||||
|
||||
fn try_main() -> Result<()> {
|
||||
let args = cli::clap::Cli::parse();
|
||||
use crate::actions::*;
|
||||
use cli::clap::CliAction::*;
|
||||
let cade = Cade::init()?;
|
||||
match args.action {
|
||||
// TODO recursively ascend until we hit a top level .cade, then activate downwards (ie. in reverse)
|
||||
Enter => do_activation().context("activate cade environment")?,
|
||||
Allow => set_permission(Permission::Allowed)?,
|
||||
Disallow => set_permission(Permission::Disallowed)?,
|
||||
|
|
|
|||
65
src/types.rs
65
src/types.rs
|
|
@ -9,6 +9,12 @@ pub enum CadeActions {
|
|||
Hook(InnerHook),
|
||||
}
|
||||
|
||||
pub struct CadeLayer {
|
||||
pub envs: EnvSet,
|
||||
pub origin: PathBuf,
|
||||
pub layer: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Env {
|
||||
pub name: String,
|
||||
|
|
@ -16,12 +22,6 @@ pub struct Env {
|
|||
// FIXME wtf, at this level we don't need to know origin..
|
||||
// that is for a higher structure
|
||||
pub origin: PathBuf,
|
||||
pub layer: u8,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
// upcast envset to env
|
||||
fn from_set(env: EnvSet, origin: &Path, layer: u8) -> Env {}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -38,6 +38,7 @@ pub enum Loadable {
|
|||
Flake(String),
|
||||
Shell(String),
|
||||
Env(String),
|
||||
Cass(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -50,58 +51,8 @@ pub enum HookType {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct InnerHook {
|
||||
// FIXME should be a pathbuf ?
|
||||
pub content: Vec<String>,
|
||||
pub kind: HookType,
|
||||
}
|
||||
|
||||
pub struct EnvSet(Vec<(String, Vec<String>)>);
|
||||
|
||||
impl EnvSet {
|
||||
pub fn from_json(raw: &[u8]) -> Result<EnvSet> {
|
||||
let json: serde_json::Value = serde_json::from_slice(raw).context("parsing json")?;
|
||||
if json.is_object()
|
||||
&& let Some(all_vars) = json.get("variables")
|
||||
{
|
||||
let vars = all_vars
|
||||
.as_object()
|
||||
.map(|inner| {
|
||||
inner
|
||||
.iter()
|
||||
.filter(|(var, _)| match var.as_str() {
|
||||
"NIX_BUILD_TOP"
|
||||
| "NIX_BUILD_CORES"
|
||||
| "NIX_STORE"
|
||||
| "TEMP"
|
||||
| "TEMPDIR"
|
||||
| "TMP"
|
||||
| "TMPDIR"
|
||||
| "builder"
|
||||
| "out"
|
||||
| "stdenv"
|
||||
| "system"
|
||||
| "dontAddDisableDepTrack"
|
||||
| "outputs" => false,
|
||||
_ => true,
|
||||
})
|
||||
.filter_map(|var| {
|
||||
Some((var.0.to_string(), var.1.get("value")?.as_str()?.to_owned()))
|
||||
})
|
||||
.map(|(name, value)| {
|
||||
(
|
||||
name,
|
||||
value
|
||||
.split(':')
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.context("collecting env vars")?;
|
||||
Ok(EnvSet(vars))
|
||||
} else {
|
||||
Err(anyhow!("failed to parse values from JSON output"))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct EnvSet(pub Vec<(String, Vec<String>)>);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue