1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
use std::{env::current_dir, path::PathBuf};
use cargo_metadata::{CargoOpt, MetadataCommand};
use mingling::{
LazyInit, Program,
consts::REMAINS,
macros::program_setup,
picker::{IntoPicker, value::Flag},
prelude::*,
res::ResCurrentDir,
};
/// Name of Cargo manifest file
pub const CARGO_TOML: &str = "Cargo.toml";
use cargo_metadata::Metadata;
use serde::Serialize;
#[derive(Default, Clone, Serialize)]
pub struct ResMetadata {
data: Option<Metadata>,
}
impl ResMetadata {
/// Returns a reference to the parsed `cargo metadata`.
///
/// # Panics
///
/// This function does **not** panic in practice, because `ResMetadata` is
/// always initialized via [`LazyInit`] inside the program setup, and the
/// initialization either succeeds (setting `data` to `Some(...)`) or fails
/// by propagating the error from `cmd.exec().unwrap()`. Therefore, by the
/// time this getter is called, `self.data` is guaranteed to be `Some`.
pub fn data(&self) -> &Metadata {
self.data.as_ref().unwrap()
}
}
#[program_setup]
pub fn cargo_metadata_setup(program: &mut Program<crate::ThisProgram>) {
let args = program.take_args();
let (
feature_args,
manifest_path,
message_format,
enable_all_features,
no_default_features,
no_deps,
args,
) = args
.pick(&arg![features: Vec<String>])
.pick(&arg![manifest_path: Option<String>])
.pick_or(&arg![message_format: String], || "disable".to_string())
.pick(&arg![all_features: Flag])
.pick(&arg![no_default_features: Flag])
.pick(&arg![no_deps: Flag])
.pick(&REMAINS)
.unwrap();
program.replace_args(args.into());
// Bind format setting to StructuralRenderer
program.structural_renderer_name = message_format.into();
// Current Dir
let current_dir = current_dir().unwrap();
// Leak `feature_args` into a static slice so it can be captured by the metadata closure.
// Since the process does not loop, this memory leak is harmless.
let feature_args_leaked: &[String] = Box::leak(feature_args.into_boxed_slice());
let manifest_path_clone = manifest_path.clone();
let current_dir_clone = current_dir.clone();
program.with_resource(ResMetadata::lazy_init(move || {
// Paths
let metadata_path = match manifest_path_clone {
Some(ref path_str) => PathBuf::from(path_str),
None => find_manifest().unwrap(),
};
// Cargo Metadata - bind to a longer-lived variable
let mut cmd = MetadataCommand::new();
cmd.manifest_path(metadata_path)
.current_dir(¤t_dir_clone);
if *enable_all_features {
cmd.features(CargoOpt::AllFeatures);
}
if *no_default_features {
cmd.features(CargoOpt::NoDefaultFeatures);
}
if *no_deps {
cmd.no_deps();
}
cmd.features(CargoOpt::SomeFeatures(feature_args_leaked.to_vec()));
ResMetadata {
data: Some(cmd.exec().unwrap()),
}
}));
// Current Dir
program.with_resource(ResCurrentDir::from(current_dir));
}
/// Find `Cargo.toml` by searching upward from `current_dir`.
fn find_manifest() -> Option<PathBuf> {
let mut dir = current_dir().ok()?;
loop {
let candidate = dir.join(CARGO_TOML);
if candidate.is_file() {
return Some(candidate);
}
if !dir.pop() {
return None;
}
}
}
|