cargo-equip

CI codecov unsafe forbidden Crates.io Crates.io

English

競技プログラミング用にRustコードを一つの.rsファイルにバンドルするCargoサブコマンドです。

Point Add Range Sum - Library-Cheker

lib

toml [package.metadata.cargo-equip-lib.mod-dependencies] "algebraic" = [] "fenwick" = ["algebraic"] "input" = [] "output" = []

bin

toml [dependencies] __lib = { package = "lib", path = "/path/to/lib" }

```rust

[cfgattr(cargoequip, cargo_equip::equip)]

use ::__lib::{fenwick::AdditiveFenwickTree, input, output};

use std::io::Write as _;

fn main() { input! { n: usize, q: usize, r#as: [i64; n], }

let mut fenwick = AdditiveFenwickTree::new(n);

for (i, a) in r#as.into_iter().enumerate() {
    fenwick.plus(i, &a);
}

output::buf_print(|out| {
    macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
    for _ in 0..q {
        input!(kind: u32);
        match kind {
            0 => {
                input!(p: usize, x: i64);
                fenwick.plus(p, &x);
            }
            1 => {
                input!(l: usize, r: usize);
                println!("{}", fenwick.query(l..r));
            }
            _ => unreachable!(),
        }
    }
});

} ```

console $ cargo equip --oneline mods --rustfmt --check -o ./bundled.rs Bundling code Checking cargo-equip-check-output-b6yi355fkyhc37tj v0.1.0 (/tmp/cargo-equip-check-output-b6yi355fkyhc37tj) Finished dev [unoptimized + debuginfo] target(s) in 0.18s

https://judge.yosupo.jp/submission/21202

インストール

Crates.io

console $ cargo install cargo-equip

master

console $ cargo install --git https://github.com/qryxip/cargo-equip

GitHub Releases

バイナリでの提供もしています。

使い方

cargo-equipで展開できるライブラリには以下5つの制限があります。

  1. 非inline module (mod $name;)は深さ1まで
  2. 深さ2以上のモジュールはすべてinline module (mod $name { .. })
  3. crate root直下にはmod以外のpubなアイテムが置かれていない (置いてもいいですが使わないでください)
  4. #[macro_export] macro_rules! name { .. }mod nameの中に置かれている (それ以外の場所に置いていいですがその場合#[macro_use]で使ってください)
  5. #[macro_export]には組み込み以外のアトリビュート(e.g. #[rustfmt::skip])を使用しない (原理的に展開すると壊れる)

1.と2.はそのうち対応しようと思います。

このように薄く広く作ってください。

src ├── a.rs ├── b.rs ├── c.rs └── lib.rs

rust // src/lib.rs pub mod a; pub mod b; pub mod c;

この制限に合うようなライブラリを書いたら、そのCargo.tomlpackage.metadataにモジュールの依存関係を手で書いてください。 直接useしたモジュールと、その連結成分だけを展開します。 欠けている場合はwarningと共にすべてのモジュールを展開します。

使う側で指定できるようにすることも考えています

toml [package.metadata.cargo-equip-lib.mod-dependencies] "a" = [] "b" = ["a"] "c" = ["a"]

そしてbin側の準備として、バンドルしたいライブラリをdependenciesに加えてください。 コンテスト毎にツールでパッケージを自動生成しているならそれのテンプレートに加えてください。

ただしこの際、ライブラリは誤って直接使わないようにリネームしておくことを強く推奨します。 直接使った場合cargo-equipはそれについて何も操作しません。

toml [dependencies] __my_lib = { package = "my_lib", path = "/path/to/my_lib" }

準備ができたらこのようにattribute付きでライブラリをuseします。

```rust

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::{b::B, c::C};

// or // //#[cfgattr(cargoequip, cargoequip::equip)] //use ::{ // _mylib1::{b::B, c::C}, // _my_lib2::{d::D, e::E}, //}; ```

useのパスにはleading colon (::)を付けてください。

```

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::{b::B, c::C}; ^^ ```

パスの1つ目のsegmentから展開するべきライブラリを決定します。 leading colonを必須としているのはこのためです。

```

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::{b::B, c::C}; ^^^^^^^^ ```

先述したライブラリの制約より、パスの第二セグメントはモジュールとみなします。 これらのモジュールと、先程書いたmod-dependenciesで繋がっているモジュールが展開されます。

```

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::{b::B, c::C}; ^ ^ ```

第三セグメント以降はuse self::$name::{..}と展開されます。

```

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::{b::B, c::C}; ^ ^ ```

コードが書けたらcargo equipで展開します。 --bin {binの名前}--src {binのファイルパス}binを指定してください。 パッケージ内のbinが一つの場合は省略できます。 ただしdefault-runには未対応です。

console $ cargo equip --bin "$name"

コードはこのように展開されます。

``rust //! # Bundled libraries //! //! ## [mylib]({ a link to Crates.io or the repository }) //! //! ### Modules //! //! -::mylib::a$crate::a //! -::mylib::b$crate::b //! -::mylib::c$crate::c`

/#[cfg_attr(cargo_equip, cargo_equip::equip)] use ::__my_lib::{b::B, c::C};/

fn main() { todo!(); }

// The following code was expanded by cargo-equip.

use self::b::B; use self::c::C;

// bcで使われているとmod-dependenciesに記述されているため、展開される mod a { // .. }

mod b { // .. }

mod c { // .. } ```

モジュールの階層が変わらないため、各ファイルの中身を手を加えずにそのまま展開します。 そのため壊れにくくなっているはずです。 多分。

またライブラリ内の#[macro_export]しているマクロですが、マクロ名と同名のモジュールに入れておくと自然な形で使えると思います。

```rust // input.rs

[macro_export]

macrorules! input { ($($tt:tt)*) => { compileerror!("TODO") }; } ```

```rust

[cfgattr(cargoequip, cargo_equip::equip)]

use ::_mylib::input; ```

オプション

--oneline

--oneline modsで展開後の各モジュールをそれぞれ一行に折り畳みます。 --oneline allでコード全体を一行に折り畳みます。

トークン列を" "区切りで出力しているだけなので、minificationではありません。

--rustfmt

出力をRustfmtでフォーマットします。

--check

バンドルしたコードを出力する前にtarget directoryを共有した一時パッケージを作り、それの上でcargo checkします。

console $ cargo equip --check -o /dev/null Bundling code Checking cargo-equip-check-output-r3cw9cy0swqb5yac v0.1.0 (/tmp/cargo-equip-check-output-r3cw9cy0swqb5yac) Finished dev [unoptimized + debuginfo] target(s) in 0.18s

ライセンス

MIT or Apache-2.0のデュアルライセンスです。