cargo-equip 0.1.0

A Cargo subcommand to bundle your code into one `.rs` file for competitive programming.
Documentation
# cargo-equip

[![CI](https://github.com/qryxip/cargo-equip/workflows/CI/badge.svg)](https://github.com/qryxip/cargo-equip/actions?workflow=CI)
[![codecov](https://codecov.io/gh/qryxip/cargo-equip/branch/master/graph/badge.svg)](https://codecov.io/gh/qryxip/cargo-equip/branch/master)
[![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/)
[![Crates.io](https://img.shields.io/crates/v/cargo-equip.svg)](https://crates.io/crates/cargo-equip)
[![Crates.io](https://img.shields.io/crates/l/cargo-equip.svg)](https://crates.io/crates/cargo-equip)

[English](https://github.com/qryxip/cargo-equip)

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

## 

[Point Add Range Sum - Library-Cheker](https://judge.yosupo.jp/problem/point_add_range_sum)

`lib`側

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

`bin`側

```toml
[dependencies]
__complib = { package = "complib", path = "/path/to/complib" }
cargo_equip_marker = { git = "https://github.com/qryxip/cargo-equip", rev = "37d4972d57be0d41d3d8edfb5db691487359cb3b" }
```

```rust
#[cargo_equip::equip]
use ::__complib::{fenwick::AdditiveFenwickTree, input, output};

use std::io::Write as _;

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

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

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

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

↓

```console
$ cargo equip --oneline mods --rustfmt --check | xsel -ib
    Bundling code
    Checking cargo-equip-check-output-ixtp05p7mhbiumzg v0.1.0 (/tmp/cargo-equip-check-output-ixtp05p7mhbiumzg)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
```

<https://judge.yosupo.jp/submission/20767>

## インストール

### Crates.io

```console
$ cargo install cargo-equip
```

### `master`

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

### GitHub Releases

[バイナリでの提供](https://github.com/qryxip/cargo-equip/releases)もしています。

## 使い方

まずライブラリをこのように横に広く作ってください。
深さ2以上のモジュールはinline module (`mod { .. }`)として書いてください。

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

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

次にライブラリの`Cargo.toml`の`package.metadata`にモジュールの依存関係を手で書いてください。
欠けている場合はwarningと共にすべてのモジュールを展開します。

[使う側で指定できるようにすることも考えています](https://github.com/qryxip/cargo-equip/issues/2)。

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

`bin`側の準備としては、次の2つを`dependencies`に加えてください。

- 展開したいライブラリ
- `cargo_equip_marker`

```toml
[dependencies]
__my_lib = { package = "my_lib", path = "/path/to/my_lib" }
cargo_equip_marker = { git = "https://github.com/qryxip/cargo-equip", rev = "37d4972d57be0d41d3d8edfb5db691487359cb3b" }
```

ライブラリは誤って直接使わないようにリネームしておくことを強く推奨します。

`cargo_equip_marker`は何もしないattribute macroである`#[equip]`を提供します。
[`cargo-snippet`](https://github.com/hatoo/cargo-snippet)のやりかたを真似たものですが、本ツールでは別のパッケージに分けています。
`cargo-equip`と間違えないようにしてください。
`cargo_equip_marker`から`#[::cargo_equip::equip]`として呼びます。

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

```rust
#[cargo_equip::equip]
use ::__my_lib::{b::B, c::C};
```

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

```
#[cargo_equip::equip]
use ::__my_lib::{b::B, c::C};
    ^^
```

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

```
#[cargo_equip::equip]
use ::__my_lib::{b::B, c::C};
      ^^^^^^^^
```

パスの2つ目のsegmentから使用しているモジュールを判定します。
これらのモジュールと、先程書いた`mod-dependencies`で繋がっているモジュールが展開されます。

```
#[cargo_equip::equip]
use ::__my_lib::{b::B, c::C};
                 ^     ^
```

パスの3つ目以降のsegmentは`use self::$name::{ .. }`として展開されます。

```
#[cargo_equip::equip]
use ::__my_lib::{b::B, c::C};
                    ^     ^
```

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

```console
$ cargo equip --bin "$name"
```

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

```rust
/*#[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;

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

mod b {
    // ..
}

mod c {
    // ..
}
```

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

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

```rust
// input.rs

#[macro_export]
macro_rules! input {
    ($($tt:tt)*) => {
        compile_error!("TODO")
    };
}
```

```rust
#[cargo_equip::equip]
use ::__my_lib::input;
```

## オプション

### `--oneline`

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

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

### `--rustfmt`

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

### `--check`

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

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

## ライセンス

[MIT](https://opensource.org/licenses/MIT) or [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)のデュアルライセンスです。