Skip to content

Commit e280e1e

Browse files
authored
Merge pull request #17441 from github/redsun82/rust-cli-flags
Rust: make the cli flags override automatic
2 parents d72f8b2 + cb53911 commit e280e1e

File tree

13 files changed

+157
-48
lines changed

13 files changed

+157
-48
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"shared/tree-sitter-extractor",
77
"ruby/extractor",
88
"rust/extractor",
9+
"rust/extractor/macros",
910
]
1011

1112
[patch.crates-io]

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ r.from_cargo(
6060
"//:Cargo.toml",
6161
"//ruby/extractor:Cargo.toml",
6262
"//rust/extractor:Cargo.toml",
63+
"//rust/extractor/macros:Cargo.toml",
6364
"//shared/tree-sitter-extractor:Cargo.toml",
6465
],
6566
)

ruby/extractor/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ codeql_rust_binary(
1212
deps = all_crate_deps(
1313
normal = True,
1414
) + [
15-
"//shared/tree-sitter-extractor:codeql-extractor",
15+
"//shared/tree-sitter-extractor",
1616
],
1717
)

rust/extractor/BUILD.bazel

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ codeql_rust_binary(
77
aliases = aliases(),
88
proc_macro_deps = all_crate_deps(
99
proc_macro = True,
10-
),
10+
) + [
11+
"//rust/extractor/macros",
12+
],
1113
visibility = ["//rust:__subpackages__"],
1214
deps = all_crate_deps(
1315
normal = True,
1416
) + [
15-
"//shared/tree-sitter-extractor:codeql-extractor",
17+
"//shared/tree-sitter-extractor",
1618
],
1719
)

rust/extractor/Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ serde = "1.0.209"
2222
serde_with = "3.9.0"
2323
stderrlog = "0.6.0"
2424
triomphe = "0.1.13"
25-
# Ideally, we'd like to pull this in via a relative path.
26-
# However, our bazel/rust tooling chokes on this, c.f. https://github.com/bazelbuild/rules_rust/issues/1525
27-
# Therefore, we have a pretty bad hack in place instead, see README.md in the codeql-extractor-fake-crate directory.
25+
argfile = "0.2.1"
2826
codeql-extractor = { path = "../../shared/tree-sitter-extractor" }
27+
rust-extractor-macros = { path = "macros" }

rust/extractor/macros/BUILD.bazel

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("@rules_rust//rust:defs.bzl", "rust_proc_macro")
2+
load("@tree_sitter_extractors_deps//:defs.bzl", "aliases", "all_crate_deps")
3+
4+
rust_proc_macro(
5+
name = "rust_extractor_macros",
6+
srcs = glob(["src/**/*.rs"]),
7+
aliases = aliases(),
8+
proc_macro_deps = all_crate_deps(
9+
proc_macro = True,
10+
),
11+
deps = all_crate_deps(
12+
normal = True,
13+
),
14+
)
15+
16+
alias(
17+
name = "macros",
18+
actual = "rust_extractor_macros",
19+
visibility = ["//rust:__subpackages__"],
20+
)

rust/extractor/macros/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "rust-extractor-macros"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
quote = "1.0.37"
11+
syn = { version = "2.0.77", features = ["full"] }

rust/extractor/macros/src/lib.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use proc_macro::TokenStream;
2+
use quote::{format_ident, quote};
3+
4+
/// Allow all fields in the extractor config to be also overrideable by extractor CLI flags
5+
#[proc_macro_attribute]
6+
pub fn extractor_cli_config(_attr: TokenStream, item: TokenStream) -> TokenStream {
7+
let ast = syn::parse_macro_input!(item as syn::ItemStruct);
8+
let name = &ast.ident;
9+
let new_name = format_ident!("Cli{}", name);
10+
let fields: Vec<_> = ast
11+
.fields
12+
.iter()
13+
.map(|f| {
14+
let id = f.ident.as_ref().unwrap();
15+
let ty = &f.ty;
16+
if let syn::Type::Path(p) = ty {
17+
if p.path.is_ident(&format_ident!("bool")) {
18+
return quote! {
19+
#[arg(long)]
20+
#[serde(skip_serializing_if="<&bool>::not")]
21+
#id: bool,
22+
};
23+
}
24+
}
25+
if id == &format_ident!("verbose") {
26+
quote! {
27+
#[arg(long, short, action=clap::ArgAction::Count)]
28+
#[serde(skip_serializing_if="u8::is_zero")]
29+
#id: u8,
30+
}
31+
} else if id == &format_ident!("inputs") {
32+
quote! {
33+
#id: #ty,
34+
}
35+
} else {
36+
quote! {
37+
#[arg(long)]
38+
#id: Option<#ty>,
39+
}
40+
}
41+
})
42+
.collect();
43+
let gen = quote! {
44+
#[serde_with::apply(_ => #[serde(default)])]
45+
#[derive(Debug, Deserialize, Default)]
46+
#ast
47+
48+
#[serde_with::skip_serializing_none]
49+
#[derive(clap::Parser, Serialize)]
50+
#[command(about, long_about = None)]
51+
struct #new_name {
52+
#(#fields)*
53+
}
54+
};
55+
gen.into()
56+
}

rust/extractor/src/config.rs

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
use anyhow::Context;
2-
use clap::{ArgAction, Parser, ValueEnum};
2+
use clap::Parser;
33
use codeql_extractor::trap;
44
use figment::{
55
providers::{Env, Serialized},
66
Figment,
77
};
8+
use num_traits::Zero;
9+
use rust_extractor_macros::extractor_cli_config;
810
use serde::{Deserialize, Serialize};
11+
use std::ops::Not;
912
use std::path::PathBuf;
1013

11-
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone, Copy, ValueEnum)]
14+
#[derive(Debug, PartialEq, Eq, Default, Serialize, Deserialize, Clone, Copy, clap::ValueEnum)]
1215
#[serde(rename_all = "lowercase")]
1316
#[clap(rename_all = "lowercase")]
1417
pub enum Compression {
@@ -26,8 +29,7 @@ impl From<Compression> for trap::Compression {
2629
}
2730
}
2831

29-
#[serde_with::apply(_ => #[serde(default)])]
30-
#[derive(Debug, Deserialize, Default)]
32+
#[extractor_cli_config]
3133
pub struct Config {
3234
pub scratch_dir: PathBuf,
3335
pub trap_dir: PathBuf,
@@ -38,40 +40,13 @@ pub struct Config {
3840
pub inputs: Vec<PathBuf>,
3941
}
4042

41-
#[serde_with::apply(_ => #[serde(skip_serializing_if = "is_default")])]
42-
#[derive(clap::Parser, Serialize)]
43-
#[command(about, long_about = None)]
44-
struct CliArgs {
45-
#[arg(long)]
46-
scratch_dir: Option<PathBuf>,
47-
#[arg(long)]
48-
trap_dir: Option<PathBuf>,
49-
#[arg(long)]
50-
source_archive_dir: Option<PathBuf>,
51-
#[arg(long)]
52-
compression: Option<Compression>,
53-
#[arg(short, long, action = ArgAction::Count)]
54-
verbose: u8,
55-
#[arg(long)]
56-
inputs_file: Option<PathBuf>,
57-
58-
inputs: Vec<PathBuf>,
59-
}
60-
61-
fn is_default<T: Default + PartialEq>(t: &T) -> bool {
62-
*t == Default::default()
63-
}
64-
6543
impl Config {
6644
pub fn extract() -> anyhow::Result<Config> {
67-
let mut cli_args = CliArgs::parse();
68-
if let Some(inputs_file) = cli_args.inputs_file.take() {
69-
let inputs_list = std::fs::read_to_string(inputs_file).context("reading file list")?;
70-
cli_args
71-
.inputs
72-
.extend(inputs_list.split_terminator("\n").map(PathBuf::from));
73-
}
45+
let args = argfile::expand_args(argfile::parse_fromfile, argfile::PREFIX)
46+
.context("expanding parameter files")?;
47+
let cli_args = CliConfig::parse_from(args);
7448
Figment::new()
49+
.merge(Env::prefixed("CODEQL_"))
7550
.merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_"))
7651
.merge(Env::prefixed("CODEQL_EXTRACTOR_RUST_OPTION_"))
7752
.merge(Serialized::defaults(cli_args))

0 commit comments

Comments
 (0)