メトリクスの選択

デフォルトでは、analyze のすべての呼び出しが完全なメトリクススイート(ABC、cognitive、cyclomatic、Halstead、LoC、MI、NArgs、NExits、NOM、NPA、NPM、tokens、WMC)を計算します。ユーザーが単に「メトリクス」を求めた CLI にとっては正しいデフォルトですが、ファイルごとに 1 つの数値だけが欲しい呼び出し側には重量級です。

MetricsOptions::with_only(&[Metric]) を使うと、ウォーカーをメトリクスのサブセットに制限できます。選択されなかったメトリクスはノード単位でスキップされ(T::Halstead::computeT::Cognitive::compute なども実行されません)、CodeMetrics のシリアライズ出力からも省略されます。

具体例

LoC だけを計算し、その結果を読み取ります。

use big_code_analysis::{analyze, LANG, Metric, MetricsOptions, Source};

fn main() {
    let source = b"fn f(x: i32) -> i32 { if x > 0 { 1 } else { 0 } }";

    let opts = MetricsOptions::default().with_only(&[Metric::Loc]);
    let space = analyze(
        Source::new(LANG::Rust, source).with_name(Some("snippet.rs".to_owned())),
        opts,
    )
    .expect("parses");

    // LoC は選択されているため、実際の数値を持ちます。
    println!("ploc = {}", space.metrics.loc.ploc());

    // Halstead、cognitive、cyclomatic などはスキップされました。
    // それらの `Stats` フィールドは `Default` のままで、JSON 出力からは省略されます。
    let json = serde_json::to_string_pretty(&space.metrics).unwrap();
    println!("{json}");
}

この呼び出しの JSON 出力に含まれるのは loc オブジェクトだけで、その他のメトリクスは存在しません。

メトリクス間の依存関係

2 つのメトリクスは 派生 メトリクスです。ファイナライズの段階で、他のメトリクスの出力を利用します。

メトリクス依存関係
Metric::MiLoc, Cyclomatic, Halstead
Metric::WmcCyclomatic, Nom

with_only はこれらの依存関係の閉包を暗黙のうちに解決します。Mi だけを指定しても Loc + Cyclomatic + Halstead が計算されるため、MI の値はゼロデフォルト入力の関数ではなく、意味のある値になります。

#![allow(unused)]
fn main() {
use big_code_analysis::{Metric, MetricSet, MetricsOptions};
let opts = MetricsOptions::default().with_only(&[Metric::Mi]);
// opts.metrics には Mi + Loc + Cyclomatic + Halstead が含まれるようになります。
}

最終的な選択集合は、得られた FuncSpace から space.metrics.selected() を介して確認できます。

#![allow(unused)]
fn main() {
use big_code_analysis::{analyze, LANG, Metric, MetricsOptions, Source};
let space = analyze(
    Source::new(LANG::Rust, b"fn f() {}"),
    MetricsOptions::default().with_only(&[Metric::Mi]),
).unwrap();
let sel = space.metrics.selected();
assert!(sel.contains(Metric::Mi));
assert!(sel.contains(Metric::Loc)); // 自動的に追加された依存メトリクス
}

デフォルトの動作は変わりません

MetricsOptions::default() はすべてのメトリクスを選択します。with_only なしで analyze(または Ast::metrics)を呼び出すと、従来とバイト単位で同一の JSON が生成されます。

X 以外のすべて」はどうするか

補集合を指定する組み込み API はありません。with_only が受け取るのは除外リストではなく、選択対象の明示的なリストです。この意図的な非対称性が、依存関係の閉包を曖昧さのないものに保ちます。正のリストは常に Metric::dependencies を通じて拡張されますが、除外リストの場合、残したメトリクスの依存先を呼び出し側が除外したときにどうするかを決めなければならなくなります。

本当に「Halstead 以外のすべて」が欲しい場合は、リストを明示的に構築してください。Metric#[non_exhaustive] なので、下流のクレートはバリアントを構築できますが、網羅的に match することはできません。そのため慣習的なパターンは、必要なバリアントを列挙し、将来 Metric バリアントが追加されても自動的には含まれないことを受け入れる、というものです。

#![allow(unused)]
fn main() {
use big_code_analysis::{Metric, MetricsOptions};

let opts = MetricsOptions::default().with_only(&[
    Metric::Cognitive,
    Metric::Cyclomatic,
    Metric::Loc,
    Metric::Nom,
    Metric::Tokens,
    Metric::Nargs,
    Metric::Nexits,
    Metric::Abc,
    Metric::Npm,
    Metric::Npa,
    Metric::Wmc,
    // Metric::Mi は意図的に省略しています。依存関係の閉包を通じて
    // Halstead を再び引き込んでしまうためです。
]);
}

落とし穴に注意してください。Metric::Mi を残すと、Metric::dependencies を通じて Metric::Halstead が再び追加されます。本当に Halstead を外すには、Mi も外す必要があります。

with_only を使うべき場面

  • ファイルごとに 1〜2 個のメトリクスしか必要としないホットパス。特に Halstead はスペースごとの HalsteadMaps アロケーションを独自に持っており、LoC のみの実行における最大の節約対象です。
  • 1 つの数値だけを表示し(たとえば認知的複雑度のゲート)、CodeMetrics の残りをキャッシュされる JSON ペイロードから取り除きたい CI 統合
  • big-code-analysis を自前のレポートに組み込むライブラリ呼び出し側。この機能がなければ、自分の UI にすべてのメトリクスのフィールドが表示されてしまいます。

メトリクス単位の Cargo フィーチャ(コンパイル時の除去)は、このノブの対象外です。