Skip to content

Commit e328b72

Browse files
authored
Merge pull request #1163 from pkgxdev/json-v2
--json=v2
2 parents d1145c8 + eb0ce63 commit e328b72

File tree

8 files changed

+105
-28
lines changed

8 files changed

+105
-28
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ jobs:
107107
- run: pkgx +git
108108
- run: pkgx +git --json
109109
- run: pkgx +git --json=v1
110+
- run: pkgx +git --json=v2
110111
- run: pkgx git --version
111112
- run: pkgx --silent +git
112113
- run: pkgx --quiet +git

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ regex = "1.11.1"
1414
indicatif = "0.17.9"
1515
nix = { version = "0.29.0", features = ["process"] }
1616
serde_json = "1.0.135"
17+
serde = { version = "1.0", features = ["derive"] }
1718
libpkgx = { version = "0.6.0", path = "../lib" }
1819
console = { version = "0.15", default-features = false, features = [
1920
"ansi-parsing",

crates/cli/src/args.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub enum Mode {
1010
pub struct Flags {
1111
pub quiet: bool,
1212
pub silent: bool,
13-
pub json: bool,
13+
pub json: Option<isize>,
1414
pub version_n_continue: bool,
1515
pub shebang: bool,
1616
pub sync: bool,
@@ -30,12 +30,13 @@ pub fn parse() -> Args {
3030
let mut args = Vec::new();
3131
let mut silent: bool = false;
3232
let mut quiet: bool = false;
33-
let mut json: bool = false;
33+
let mut json = None;
3434
let mut find_program = false;
3535
let mut collecting_args = false;
3636
let mut version_n_continue = false;
3737
let mut shebang = false;
3838
let mut sync = false;
39+
let json_latest_v: isize = 2;
3940

4041
for arg in std::env::args().skip(1) {
4142
if collecting_args {
@@ -47,17 +48,19 @@ pub fn parse() -> Args {
4748
collecting_args = true;
4849
} else if arg.starts_with("--") {
4950
match arg.as_str() {
51+
"--shebang" => shebang = true,
5052
"--json" => {
5153
if !silent {
5254
eprintln!(
53-
"{} use --json=v1",
54-
style("warning: --json is not stable").yellow()
55+
"{} use --json={}",
56+
style("warning: --json is not stable").yellow(),
57+
json_latest_v
5558
);
5659
}
57-
json = true
60+
json = Some(2);
5861
}
59-
"--shebang" => shebang = true,
60-
"--json=v1" => json = true,
62+
"--json=v1" => json = Some(1),
63+
"--json=v2" => json = Some(2),
6164
"--silent" => silent = true,
6265
"--help" => mode = Mode::Help,
6366
"--version" => mode = Mode::Version,
@@ -98,7 +101,7 @@ pub fn parse() -> Args {
98101
}
99102
'h' => mode = Mode::Help,
100103
's' => silent = true,
101-
'j' => json = true,
104+
'j' => json = Some(json_latest_v),
102105
'v' => version_n_continue = true,
103106
'!' => shebang = true,
104107
'Q' => mode = Mode::Query,

crates/cli/src/dump.rs

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,73 @@
1-
use std::collections::HashMap;
1+
use std::{collections::HashMap, path::PathBuf, vec};
22

33
use libpkgx::{
4+
env::expand_moustaches, pantry_db,
45
platform_case_aware_env_key::construct_platform_case_aware_env_key, types::Installation,
56
};
7+
use serde::Serialize;
68
use serde_json::json;
79

810
pub fn dump(
911
conn: rusqlite::Connection,
1012
installations: Vec<Installation>,
11-
env: HashMap<String, Vec<String>>,
1213
flags: &crate::args::Flags,
1314
) -> Result<(), Box<dyn std::error::Error>> {
14-
if !flags.json {
15+
if let Some(v) = flags.json {
16+
if v < 2 {
17+
let env = libpkgx::env::map(&installations);
18+
let mut runtime_env = HashMap::new();
19+
for pkg in installations.clone() {
20+
let pkg_runtime_env =
21+
libpkgx::pantry_db::runtime_env_for_project(&pkg.pkg.project, &conn)?;
22+
if !pkg_runtime_env.is_empty() {
23+
runtime_env.insert(pkg.pkg.project, pkg_runtime_env);
24+
}
25+
}
26+
let json = json!({
27+
"pkgs": installations,
28+
"env": env,
29+
"runtime_env": runtime_env
30+
});
31+
println!("{}", json);
32+
} else {
33+
let mut pkgs: HashMap<String, JsonV2Pkg> = HashMap::new();
34+
for installation in installations.clone() {
35+
let env = libpkgx::env::map(&vec![installation.clone()]);
36+
let project = installation.pkg.project.clone();
37+
38+
let mut runtime_env = libpkgx::pantry_db::runtime_env_for_project(&project, &conn)?;
39+
40+
for (installation_key, installation_value) in runtime_env.clone() {
41+
let installation_value =
42+
expand_moustaches(&installation_value, &installation, &installations);
43+
runtime_env.insert(installation_key, installation_value);
44+
}
45+
46+
let programs = pantry_db::programs_for_project(&project, &conn)?;
47+
let companions = pantry_db::companions_for_projects(&[project.clone()], &conn)?
48+
.iter()
49+
.map(|c| c.to_string())
50+
.collect::<Vec<String>>();
51+
52+
let pkg = JsonV2Pkg {
53+
path: installation.path,
54+
project,
55+
version: installation.pkg.version,
56+
env,
57+
runtime_env,
58+
programs,
59+
companions,
60+
};
61+
pkgs.insert(pkg.project.clone(), pkg);
62+
}
63+
64+
let json = json!({
65+
"pkgs": pkgs, "env": libpkgx::env::map(&installations)
66+
});
67+
println!("{}", json);
68+
}
69+
} else {
70+
let env = libpkgx::env::map(&installations);
1571
let env = env
1672
.iter()
1773
.map(|(k, v)| {
@@ -29,21 +85,21 @@ pub fn dump(
2985
value.replace(&format!(":${}", key), &format!("${{{}:+:${}}}", key, key))
3086
);
3187
}
32-
} else {
33-
let mut runtime_env = HashMap::new();
34-
for pkg in installations.clone() {
35-
let pkg_runtime_env =
36-
libpkgx::pantry_db::runtime_env_for_project(&pkg.pkg.project, &conn)?;
37-
if !pkg_runtime_env.is_empty() {
38-
runtime_env.insert(pkg.pkg.project, pkg_runtime_env);
39-
}
40-
}
41-
let json = json!({
42-
"pkgs": installations,
43-
"env": env,
44-
"runtime_env": runtime_env
45-
});
46-
println!("{}", json);
4788
}
4889
Ok(())
4990
}
91+
92+
#[derive(Serialize)]
93+
struct JsonV2Pkg {
94+
project: String,
95+
version: libpkgx::Version,
96+
#[serde(skip_serializing_if = "HashMap::is_empty")]
97+
env: HashMap<String, Vec<String>>,
98+
#[serde(skip_serializing_if = "HashMap::is_empty")]
99+
runtime_env: HashMap<String, String>,
100+
path: PathBuf,
101+
#[serde(skip_serializing_if = "Vec::is_empty")]
102+
programs: Vec<String>,
103+
#[serde(skip_serializing_if = "Vec::is_empty")]
104+
companions: Vec<String>,
105+
}

crates/cli/src/help.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ flags:
3131
-q, --quiet # suppress brief informational messages
3232
-qq, --silent # no chat. no errors. just execute.
3333
-v # print version and continue
34+
--sync # sync first (note: rarely if ever needed)
35+
-j,--json=v2 # output JSON (if sensible)
3436
3537
more:
3638
$ OPEN https://docs.pkgx.sh

crates/cli/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
5353
&mut spinner,
5454
)
5555
.await?;
56-
let env = libpkgx::env::map(&installations);
5756

5857
if !args.is_empty() {
58+
let env = libpkgx::env::map(&installations);
5959
let (cmd, args, env) =
6060
x::exec(find_program, args, installations, env, flags, conn, graph).await?;
6161
spinner.finish_and_clear();
6262
execve(cmd, args, env)?;
6363
Ok(())
6464
} else if !plus.is_empty() {
6565
spinner.finish_and_clear();
66-
dump::dump(conn, installations, env, &flags)?;
66+
dump::dump(conn, installations, &flags)?;
6767
Ok(())
6868
} else if flags.version_n_continue || flags.sync {
6969
Ok(())

crates/lib/src/pantry_db.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,16 @@ pub fn companions_for_projects(
179179
// Collect results into a Vec<PackageReq>, propagating errors
180180
Ok(companions.collect::<Result<Vec<_>, _>>()?)
181181
}
182+
183+
pub fn programs_for_project(
184+
project: &String,
185+
conn: &Connection,
186+
) -> Result<Vec<String>, rusqlite::Error> {
187+
let mut stmt = conn.prepare("SELECT program FROM provides WHERE project = ?1")?;
188+
let mut rv = Vec::new();
189+
let mut rows = stmt.query(params![project])?;
190+
while let Some(row) = rows.next()? {
191+
rv.push(row.get(0)?);
192+
}
193+
Ok(rv)
194+
}

0 commit comments

Comments
 (0)