use std::{env::current_dir, path::PathBuf};
use cargo_metadata::{CargoOpt, MetadataCommand};
use mingling::{
LazyInit, Program,
macros::program_setup,
picker::{IntoPicker, PickerArg, value::Flag},
prelude::*,
res::ResCurrentDir,
};
pub const CARGO_TOML: &str = "Cargo.toml";
use cargo_metadata::Metadata;
use serde::Serialize;
#[derive(Default, Clone, Serialize)]
pub struct ResMetadata {
data: Option<Metadata>,
}
#[derive(Default, Clone, Serialize)]
pub struct ResUsingJson {
pub using: bool,
}
pub static ARG_FEATURES: PickerArg<Vec<String>> = arg![features: Vec<String>];
pub static ARG_MANIFEST_PATH: PickerArg<Option<String>> = arg![manifest_path: Option<String>];
pub static ARG_MESSAGE_FORMAT: PickerArg<String> = arg![message_format: String];
pub static ARG_ALL_FEATURES: PickerArg<Flag> = arg![all_features: Flag];
pub static ARG_NO_DEFAULT_FEATURES: PickerArg<Flag> = arg![no_default_features: Flag];
pub static ARG_NO_DEPS: PickerArg<Flag> = arg![no_deps: Flag];
impl ResMetadata {
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.get_args().to_vec();
let (
feature_args,
manifest_path,
message_format,
enable_all_features,
no_default_features,
no_deps,
) = args
.pick(&ARG_FEATURES)
.pick(&ARG_MANIFEST_PATH)
.pick_or(&ARG_MESSAGE_FORMAT, || "disable".to_string())
.pick(&ARG_ALL_FEATURES)
.pick(&ARG_NO_DEFAULT_FEATURES)
.pick(&ARG_NO_DEPS)
.unwrap();
program.with_resource(ResUsingJson {
using: message_format.contains("json"),
});
let current_dir = current_dir().unwrap();
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 || {
let metadata_path = match manifest_path_clone {
Some(ref path_str) => PathBuf::from(path_str),
None => find_manifest().unwrap(),
};
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()),
}
}));
program.with_resource(ResCurrentDir::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;
}
}
}