Skip to content

Commit 819bef3

Browse files
committed
feat: add config file option
Add new Config struct. Rename images to examples and add config.toml example.
1 parent 30c07d5 commit 819bef3

File tree

13 files changed

+1520
-714
lines changed

13 files changed

+1520
-714
lines changed

Cargo.lock

Lines changed: 482 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,20 @@ tinytemplate = "1.2"
3737
version = "0.1"
3838
optional = true
3939

40+
[dependencies.serde_with]
41+
version = "3.16"
42+
optional = true
43+
44+
[dependencies.toml]
45+
version = "0.9"
46+
default-features = false
47+
features = ["std", "serde", "parse"]
48+
optional = true
49+
4050
[features]
41-
default = ["regex"]
51+
default = ["regex", "config"]
4252
regex = ["dep:regex-lite"]
53+
config = ["dep:serde_with", "dep:toml"]
4354

4455
[profile.release]
4556
lto = "thin"

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ It is important to note that the sizes is and will always be only an *estimation
88

99
## Examples
1010

11-
`pugio --release --gradient blues -t non-zero --scale-factor 1.2 --separation-factor 0.8 -o images/pugio.svg`
11+
`pugio -c examples/config.toml`
1212

1313
![pugio](images/pugio.svg)
1414

15-
`pugio --bin rg --release --scheme dep-count --gradient purples -R "grep v0.4.1" -d 2 -t 1KiB --gamma 0.5 --dark-mode --std -o images/ripgrep.svg`
15+
`pugio --bin rg --release --scheme dep-count --gradient purples -R "grep v0.4.1" -d 2 -t 1KiB --gamma 0.5 --dark-mode --std -o examples/ripgrep.svg`
1616

1717
![ripgrep](images/ripgrep.svg)
1818

19-
`pugio --release --node-label-template "{short}\n{value_binary}" -E 'clap|rand' -g reds -t non-zero --dark-mode --std -o images/hyperfine.svg`
19+
`pugio --release --node-label-template "{short}\n{value_binary}" -E 'clap|rand' -g reds -t non-zero --dark-mode --std -o examples/hyperfine.svg`
2020

2121
![hyperfine](images/hyperfine.svg)
2222

@@ -44,8 +44,9 @@ To customise enabled Cargo features, add the options:
4444

4545
## Feature flags
4646

47-
- `default`: `regex`
48-
- `regex`: support regex pattern matching in options
47+
- `default`: `regex`, `config`
48+
- `regex`: support regex pattern matching in options: [regex-lite syntax](https://docs.rs/regex-lite/latest/regex_lite/index.html#syntax)
49+
- `config`: support TOML config file
4950

5051
## Planned Features
5152

@@ -62,6 +63,9 @@ A command-line dependency binary size graph visualisation tool
6263
Usage: pugio [OPTIONS]
6364
6465
Options:
66+
-c, --config <CONFIG_FILE>
67+
Config TOML file path, "-" for stdin
68+
disables all other options
6569
-p, --package <PACKAGE>
6670
Package to inspect
6771
--bin <BINARY>

examples/config.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
release = true
2+
gradient = "blues"
3+
threshold = "non-zero"
4+
scale-factor = 1.2
5+
separation-factor = 0.8
6+
output = "examples/pugio.svg"

examples/pugio.svg

Lines changed: 732 additions & 0 deletions
Loading
File renamed without changes.

images/pugio.svg

Lines changed: 0 additions & 525 deletions
This file was deleted.

src/cargo.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use anyhow::{Context, bail};
77
use petgraph::{graph::NodeIndex, prelude::StableGraph};
88
use serde_json::Value;
99

10-
use crate::{Args, graph::NodeWeight};
10+
use crate::{config::Config, graph::NodeWeight};
1111

1212
#[derive(Debug, Default)]
1313
pub struct CargoOptions {
@@ -19,8 +19,8 @@ pub struct CargoOptions {
1919
pub release: bool,
2020
}
2121

22-
impl From<&Args> for CargoOptions {
23-
fn from(value: &Args) -> Self {
22+
impl From<&Config> for CargoOptions {
23+
fn from(value: &Config) -> Self {
2424
Self {
2525
package: value.package.clone(),
2626
binary: value.binary.clone(),

src/config.rs

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
use std::str::FromStr;
2+
3+
use clap::Args;
4+
#[cfg(feature = "config")]
5+
use serde::de;
6+
7+
use crate::{NodeColoringGradient, NodeColoringScheme};
8+
9+
// Obfuscate type for clap
10+
type OptScheme = Option<NodeColoringScheme>;
11+
12+
#[cfg_attr(
13+
feature = "config",
14+
derive(serde::Deserialize),
15+
serde(rename_all = "kebab-case")
16+
)]
17+
#[derive(Args)]
18+
pub struct Config {
19+
/// Package to inspect
20+
#[arg(short, long)]
21+
pub package: Option<String>,
22+
23+
/// Binary to inspect
24+
#[arg(long = "bin")]
25+
pub binary: Option<String>,
26+
27+
/// Space or comma separated list of features to activate
28+
#[arg(short = 'F', long)]
29+
pub features: Option<String>,
30+
31+
/// Activate all available features
32+
#[arg(long)]
33+
#[cfg_attr(feature = "config", serde(default))]
34+
pub all_features: bool,
35+
36+
/// Do not activate the `default` feature
37+
#[arg(long)]
38+
#[cfg_attr(feature = "config", serde(default))]
39+
pub no_default_features: bool,
40+
41+
/// Build artifacts in release mode, with optimizations
42+
#[arg(long)]
43+
#[cfg_attr(feature = "config", serde(default))]
44+
pub release: bool,
45+
46+
/// Exclude dependency names matching the regex patterns
47+
#[cfg(feature = "regex")]
48+
#[arg(short = 'E', long)]
49+
pub excludes: Option<Vec<String>>,
50+
51+
/// Exclude dependency names matching the prefixes
52+
#[cfg(not(feature = "regex"))]
53+
#[arg(short = 'E', long)]
54+
pub excludes: Option<Vec<String>>,
55+
56+
/// Change root to the unique depndency name matching the regex pattern
57+
#[cfg(feature = "regex")]
58+
#[arg(short = 'R', long)]
59+
pub root: Option<String>,
60+
61+
/// Change root to the unique depndency name matching the prefix
62+
#[cfg(not(feature = "regex"))]
63+
#[arg(short = 'R', long)]
64+
pub root: Option<String>,
65+
66+
/// Add std standalone node
67+
#[arg(long)]
68+
#[cfg_attr(feature = "config", serde(default))]
69+
pub std: bool,
70+
71+
/// Color scheme of nodes
72+
/// - "cum-sum": cumulative sum of the size of a node and its dependencies (default)
73+
/// - "dep-count": dependency count; number of transitive dependency relations from a node
74+
/// - "rev-dep-count": reverse dependency count; number of paths from the root to a node
75+
/// - "none"
76+
#[cfg_attr(
77+
feature = "config",
78+
serde(deserialize_with = "de_scheme", default = "default_opt_scheme")
79+
)]
80+
#[arg(short, long, default_value = "cum-sum", hide_default_value = true, value_parser = parse_scheme, verbatim_doc_comment)]
81+
pub scheme: OptScheme,
82+
83+
/// Color gradient of nodes
84+
/// - "reds" (default), "oranges", "purples", "greens", "blues"
85+
/// - custom CSS gradient format, e.g. "#fff, 75%, #00f"
86+
#[arg(short, long, verbatim_doc_comment)]
87+
pub gradient: Option<NodeColoringGradient>,
88+
89+
/// Color gamma of nodes, between 0.0 and 1.0
90+
/// default is scheme-specific
91+
#[arg(long, verbatim_doc_comment)]
92+
pub gamma: Option<f32>,
93+
94+
/// Remove nodes that have cumulative sum below threshold
95+
/// - human readable byte format, e.g. "21KiB", "69 KB"
96+
/// - "non-zero"
97+
#[arg(short, long, value_parser = parse_threshold, verbatim_doc_comment)]
98+
#[cfg_attr(feature = "config", serde(deserialize_with = "de_threshold", default))]
99+
pub threshold: Option<usize>,
100+
101+
/// Remove nodes that are more than max depth deep
102+
#[arg(short = 'd', long)]
103+
pub max_depth: Option<usize>,
104+
105+
/// Inverse color gradient
106+
#[arg(long)]
107+
#[cfg_attr(feature = "config", serde(default))]
108+
pub inverse_gradient: bool,
109+
110+
/// Dark mode for output svg file
111+
#[arg(long)]
112+
#[cfg_attr(feature = "config", serde(default))]
113+
pub dark_mode: bool,
114+
115+
/// Scale factor for output svg file
116+
#[arg(long)]
117+
pub scale_factor: Option<f32>,
118+
119+
/// Separation factor for output svg file
120+
#[arg(long)]
121+
pub separation_factor: Option<f32>,
122+
123+
/// Custom node label formatting template
124+
/// default: "{short}"
125+
#[arg(long, verbatim_doc_comment)]
126+
pub node_label_template: Option<String>,
127+
128+
/// Custom node tooltip formatting template
129+
/// default: "{full}\n{size_binary}"
130+
#[arg(long, verbatim_doc_comment)]
131+
pub node_tooltip_template: Option<String>,
132+
133+
/// Custom edge label formatting template
134+
#[arg(long, verbatim_doc_comment)]
135+
pub edge_label_template: Option<String>,
136+
137+
/// Custom edge tooltip formatting template
138+
/// default: "{source} -> {target}"
139+
#[arg(long, verbatim_doc_comment)]
140+
pub edge_tooltip_template: Option<String>,
141+
142+
/// Dot output file only
143+
#[arg(long)]
144+
#[cfg_attr(feature = "config", serde(default))]
145+
pub dot_only: bool,
146+
147+
/// Output filename, default is output.*
148+
#[arg(short, long)]
149+
pub output: Option<String>,
150+
151+
/// Do not open output svg file
152+
#[arg(long)]
153+
#[cfg_attr(feature = "config", serde(default))]
154+
pub no_open: bool,
155+
}
156+
157+
#[cfg(feature = "config")]
158+
fn default_opt_scheme() -> OptScheme {
159+
Some(NodeColoringScheme::CumSum)
160+
}
161+
162+
#[cfg(feature = "config")]
163+
fn de_scheme<'de, D: de::Deserializer<'de>>(d: D) -> Result<Option<NodeColoringScheme>, D::Error> {
164+
let str: String = de::Deserialize::deserialize(d)?;
165+
parse_scheme(&str).map_err(de::Error::custom)
166+
}
167+
168+
#[cfg(feature = "config")]
169+
fn de_threshold<'de, D: de::Deserializer<'de>>(d: D) -> Result<Option<usize>, D::Error> {
170+
#[derive(serde::Deserialize)]
171+
#[serde(untagged)]
172+
enum Threshold {
173+
Usize(usize),
174+
String(String),
175+
}
176+
177+
let threshold: Threshold = de::Deserialize::deserialize(d)?;
178+
match threshold {
179+
Threshold::Usize(u) => Ok(Some(u)),
180+
Threshold::String(s) if s == "non-zero" => Ok(Some(1)),
181+
Threshold::String(s) => parse_size::parse_size(s)
182+
.map(|b| Some(b as usize))
183+
.map_err(de::Error::custom),
184+
}
185+
}
186+
187+
fn parse_scheme(s: &str) -> Result<Option<NodeColoringScheme>, strum::ParseError> {
188+
match s {
189+
"none" => Ok(None),
190+
_ => Ok(Some(NodeColoringScheme::from_str(s)?)),
191+
}
192+
}
193+
194+
fn parse_threshold(t: &str) -> Result<usize, parse_size::Error> {
195+
if t == "non-zero" {
196+
Ok(1)
197+
} else {
198+
parse_size::parse_size(t).map(|b| b as usize)
199+
}
200+
}

0 commit comments

Comments
 (0)