#competitive #cli #windows

bin+lib cargo-equip

A Cargo subcommand to bundle your code into one .rs file for competitive programming

27 releases (11 breaking)

new 0.12.1 May 2, 2021
0.11.1 Mar 30, 2021
0.9.2 Dec 23, 2020
0.9.0 Nov 27, 2020

#32 in Cargo plugins

Download history 8/week @ 2021-01-13 1/week @ 2021-01-20 2/week @ 2021-01-27 3/week @ 2021-02-03 106/week @ 2021-02-10 70/week @ 2021-02-17 16/week @ 2021-02-24 33/week @ 2021-03-03 3/week @ 2021-03-10 4/week @ 2021-03-17 74/week @ 2021-03-24 37/week @ 2021-03-31 29/week @ 2021-04-07 58/week @ 2021-04-14 36/week @ 2021-04-21 40/week @ 2021-04-28

161 downloads per month

MIT/Apache

165KB
3.5K SLoC

cargo-equip

CI codecov dependency status Crates.io Crates.io

日本語

A Cargo subcommand to bundle your code into one .rs file for competitive programming.

Recent updates

See CHANGELOG.md for recent updates.

Example

Sqrt Mod - Library-Cheker

[package]
name = "solve"
version = "0.0.0"
edition = "2018"

[dependencies]
ac-library-rs-parted              = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-convolution  = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-dsu          = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-fenwicktree  = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-lazysegtree  = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-math         = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-maxflow      = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-mincostflow  = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-modint       = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-scc          = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-segtree      = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-string       = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
ac-library-rs-parted-twosat       = { git = "https://github.com/qryxip/ac-library-rs-parted"            }
qryxip-competitive-fastout        = { git = "https://github.com/qryxip/competitive-programming-library" }
qryxip-competitive-input          = { git = "https://github.com/qryxip/competitive-programming-library" }
qryxip-competitive-tonelli-shanks = { git = "https://github.com/qryxip/competitive-programming-library" }
# ...
#[macro_use]
extern crate fastout as _;
#[macro_use]
extern crate input as _;

use acl_modint::ModInt;
use tonelli_shanks::ModIntBaseExt as _;

#[fastout]
fn main() {
    input! {
        yps: [(u32, u32)],
    }

    for (y, p) in yps {
        ModInt::set_modulus(p);
        if let Some(sqrt) = ModInt::new(y).sqrt() {
            println!("{}", sqrt);
        } else {
            println!("-1");
        }
    }
}

 cargo equip --resolve-cfgs --remove comments docs --rustfmt --check --bin solve | xsel -b

Submit Info #40609 - Library-Checker

Installation

Install a nightly toolchain and cargo-udeps first.

 rustup update nightly
 cargo install cargo-udeps

From Crates.io

 cargo install cargo-equip

From master branch

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

GitHub Releases

Releases

Usage

Follow these constrants when you writing libraries to bundle.

  1. Do not put items with the name names of #[macro_export]ed macros in each crate root.

    cargo-equip inserts pub use crate::{ these_names }; just below each mod lib_name. Use #[macro_use] to import macros in a bin/example.

    // in main source code
    
    #[macro_use]
    extern crate input as _;
    

    extern crate items in bins/examples are commented-out.

    // in main source code
    
    /*#[macro_use]
    extern crate input as _;*/ // `use crate::$name;` is inserted if the rename is not `_`
    
  2. To make compatible with Rust 2015, do not resolve names of crates to bundle directly from extern prelude.

    Mount them in some module except the root one with a extern crate item and refer them with relative paths.

    cargo-equip replaces extern crate items with use crate::extern_crate_name_in_main_crate; except for crates specified with --exclude <SPEC>..., --exclude-atcoder-crates, or --exclude-codingame-crates. Rename the libraries not to use directly.

     mod extern_crates {
    -    pub(super) extern crate __another_lib as another_lib;
    +    pub(super) use crate::another_lib;
     }
    
     use self::extern_crates::another_lib::foo::Foo; // Prepend `self::` to make compatible with Rust 2015
    

    If you don't use website where Rust 2018 is unavailable (e.g. AIZU ONLINE JUDGE, yukicoder), you don't have to do this. mod __pseudo_extern_prelude like this is created in each library as a substitute for extern prelude. This mod __pseudo_extern_prelude itself is valid in Rust 2015 but unfortunately Rust 2015 cannot resolve the use another_lib::A;.

    +mod __pseudo_extern_prelude {
    +    pub(super) use crate::{another_lib1, another_lib2};
    +}
    +use self::__pseudo_extern_prelude::*;
    +
     use another_lib1::A;
     use another_lib2::B;
    
  3. Use $crate instead of crate in macros.

    cargo-equip replaces $crate in macro_rules! with $crate::extern_crate_name_in_main_crate. crate identifiers in macro_rules! are not modified.

  4. Do not use absolute path as possible.

    cargo-equip replaces crate with crate::extern_crate_name_in_main_crate and pub(crate) with pub(in crate::extern_crate_name_in_main_crate).

    However I cannot ensure this works well. Use self:: and super:: instead of crate::.

    -use crate::foo::Foo;
    +use super::foo::Foo;
    
  5. Split into small separate crates as possible.

    cargo-equip does not search "dependencies among items".

    On a website other except AtCoder, Split your library into small crates to fit in 64KiB.

    .
    ├── input
       ├── Cargo.toml
       └── src
           └── lib.rs
    ├── output
       ├── Cargo.toml
       └── src
           └── lib.rs
    
    

When you finish preparing your library crates, add them to [dependencies] of the bin/example. If you generate packages automatically with a tool, add them to its template.

If you want to use rust-lang-ja/ac-library-rs, use qryxip/ac-library-rs-parted instead. ac-library-rs-parted is a collection of 17 crates that process the real ac-library-rs with a script.

[dependencies]
ac-library-rs-parted             = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-convolution = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-dsu         = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-fenwicktree = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-lazysegtree = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-math        = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-maxflow     = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-mincostflow = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-modint      = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-scc         = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-segtree     = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-string      = { git = "https://github.com/qryxip/ac-library-rs-parted" }
ac-library-rs-parted-twosat      = { git = "https://github.com/qryxip/ac-library-rs-parted" }

The constraints for bins/examples are:

  1. Do not import macros with use. Use them with #[macro_use] or with qualified paths.
  2. If you create mods, inside them do not resolve names of crates to bundle directly from extern prelude.
#[macro_use]
extern crate input as _;

use std::io::Write as _;

fn main() {
    input! {
        n: usize,
    }

    buffered_print::buf_print(|out| {
        macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
        for i in 1..=n {
            match i % 15 {
                0 => println!("Fizz Buzz"),
                3 | 6 | 9 | 12 => println!("Fizz"),
                5 | 10 => println!("Buzz"),
                _ => println!("{}", i),
            }
        }
    });
}

Then execute cargo-equip.

 cargo equip --bin "$name"
 cargo equip --example "$name"

cargo-equip outputs code like this. It gives tentative extern_crate_names like __package_name_0_1_0 to dependencies of the dependencies.

+//! # Bundled libraries
+//!
+//! - `qryxip-competitive-buffered-print 0.0.0 (path+█████████████████████████████████████████████████████████████████████████████████████)` published in https://github.com/qryxip/competitive-programming-library licensed under `CC0-1.0` as `crate::buffered_print`
+//! - `qryxip-competitive-input 0.0.0 (path+████████████████████████████████████████████████████████████████████████████)`                   published in https://github.com/qryxip/competitive-programming-library licensed under `CC0-1.0` as `crate::input`

-#[macro_use]
-extern crate input as _;
+/*#[macro_use]
+extern crate input as _;*/

 use std::io::Write as _;

 fn main() {
     input! {
         n: usize,
     }

     buffered_print::buf_print(|out| {
         macro_rules! println(($($tt:tt)*) => (writeln!(out, $($tt)*).unwrap()));
         for i in 1..=n {
             match i % 15 {
                 0 => println!("Fizz Buzz"),
                 3 | 6 | 9 | 12 => println!("Fizz"),
                 5 | 10 => println!("Buzz"),
                 _ => println!("{}", i),
             }
         }
     });
 }
+
+// The following code was expanded by `cargo-equip`.
+
+#[allow(dead_code)]
+mod buffered_print {
+    // ...
+}
+
+#[allow(dead_code)]
+mod input {
+    // ...
+}

cargo-equip does the following modification.

  • bin/example
    • If a #![cfg_attr(cargo_equip, cargo_equip::skip)] was found, skips the remaining modification, does cargo check if --check is specified, and outputs the source code as-is.
    • If any, expands mod $name;s recursively indenting them except those containing multi-line literals.
    • Expands procedural macros.
    • Replaces some of the extern crate items.
    • Prepends a doc comment.
    • Appends the expanded libraries.
  • libs
    • Expands mod $name; recursively.
    • Replaces some of the crate paths.
    • Replaces some of the extern crate items.
    • Modifies macro_rules!.
    • Inserts mod __pseudo_extern_prelude { .. } and use (self::|$(super::)*)__pseudo_extern_prelude::*;.
    • Removes #[cfg(..)] attributes or their targets if --resolve-cfg is specified.
    • Removes doc comments if --remove docs is specified.
    • Removes comments if --remove comments is specified.
  • Whole
    • Minifies the whole output f--minify all is specified.
    • Formats the output if --rustfmt is specified.

Expanding procedural macros

cargo-equip can expand procedural macros.

#[macro_use]
extern crate memoise as _;
#[macro_use]
extern crate proconio_derive as _;

#[fastout]
fn main() {
    for i in 0..=100 {
        println!("{}", fib(i));
    }
}

#[memoise(n <= 100)]
fn fib(n: i64) -> i64 {
    if n == 0 || n == 1 {
        return n;
    }
    fib(n - 1) + fib(n - 2)
}

Output
//! # Procedural macros
//!
//! - `memoise 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)`         licensed under `BSD-3-Clause`
//! - `proconio-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)` licensed under `MIT OR Apache-2.0`

/*#[macro_use]
extern crate memoise as _;*/
/*#[macro_use]
extern crate proconio_derive as _;*/

/*#[fastout]
fn main() {
    for i in 0..=100 {
        println!("{}", fib(i));
    }
}*/
fn main() {
    let __proconio_stdout = ::std::io::stdout();
    let mut __proconio_stdout = ::std::io::BufWriter::new(__proconio_stdout.lock());
    #[allow(unused_macros)]
    macro_rules ! print { ($ ($ tt : tt) *) => { { use std :: io :: Write as _ ; :: std :: write ! (__proconio_stdout , $ ($ tt) *) . unwrap () ; } } ; }
    #[allow(unused_macros)]
    macro_rules ! println { ($ ($ tt : tt) *) => { { use std :: io :: Write as _ ; :: std :: writeln ! (__proconio_stdout , $ ($ tt) *) . unwrap () ; } } ; }
    let __proconio_res = {
        for i in 0..=100 {
            println!("{}", fib(i));
        }
    };
    <::std::io::BufWriter<::std::io::StdoutLock> as ::std::io::Write>::flush(
        &mut __proconio_stdout,
    )
    .unwrap();
    return __proconio_res;
}

/*#[memoise(n <= 100)]
fn fib(n: i64) -> i64 {
    if n == 0 || n == 1 {
        return n;
    }
    fib(n - 1) + fib(n - 2)
}*/
thread_local ! (static FIB : std :: cell :: RefCell < Vec < Option < i64 > > > = std :: cell :: RefCell :: new (vec ! [None ; 101usize]));
fn fib_reset() {
    FIB.with(|cache| {
        let mut r = cache.borrow_mut();
        for r in r.iter_mut() {
            *r = None
        }
    });
}
fn fib(n: i64) -> i64 {
    if let Some(ret) = FIB.with(|cache| {
        let mut bm = cache.borrow_mut();
        bm[(n) as usize].clone()
    }) {
        return ret;
    }
    let ret: i64 = (|| {
        if n == 0 || n == 1 {
            return n;
        }
        fib(n - 1) + fib(n - 2)
    })();
    FIB.with(|cache| {
        let mut bm = cache.borrow_mut();
        bm[(n) as usize] = Some(ret.clone());
    });
    ret
}

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

#[allow(clippy::deprecated_cfg_attr)]#[cfg_attr(rustfmt,rustfmt::skip)]#[allow(unused)]pub mod memoise{}
#[allow(clippy::deprecated_cfg_attr)]#[cfg_attr(rustfmt,rustfmt::skip)]#[allow(unused)]pub mod proconio_derive{}
  • rust-analyzer(.exe) is automatically downloaded.
  • proc-macro crates need to be compile with Rust 1.47.0+. If version of the active toolchain is less than 1.47.0, cargo-equip finds an alternative toolchain and uses it for compiling proc-macros.
  • procedural macros re-exported with pub use $name::*; are also able to be expanded.

Options

--resolve-cfgs

  1. Removes #[cfg(always_true_predicate)] (e.g. cfg(feature = "enabled-feature")).
  2. Removes items with #[cfg(always_false_preducate)] (e.g. cfg(test), cfg(feature = "disable-feature")).

Predicates are evaluated according to this rule.

#[allow(dead_code)]
pub mod a {
    pub struct A;

    #[cfg(test)]
    mod tests {
        #[test]
        fn it_works() {
            assert_eq!(2 + 2, 4);
        }
    }
}

#[allow(dead_code)]
pub mod a {
    pub struct A;
}

--remove <REMOVE>...

Removes

  • doc comments (//! .., /// .., /** .. */, #[doc = ".."]) with --remove docs.
  • comments (// .., /* .. */) with --remove comments.
#[allow(dead_code)]
pub mod a {
    //! A.

    /// A.
    pub struct A; // aaaaa
}

#[allow(dead_code)]
pub mod a {
    pub struct A;
}

--minify <MINIFY>

Minifies

  • each expaned library with --minify lib.
  • the whole code with --minify all.

Not that the minification function is incomplete. Unnecessary spaces may be inserted.

--rustfmt

Formats the output with Rustfmt.

--check

Creates a temporary package that shares the current target directory and execute cargo check before outputting.

This flag works even if bundling was skipped by #![cfg_attr(cargo_equip, cargo_equip::skip)].

 cargo equip --check -o /dev/null
     Running `/home/ryo/.cargo/bin/rustup run nightly cargo udeps --output json -p solve --bin solve`
    Checking solve v0.0.0 (/home/ryo/src/local/a/solve)
    Finished dev [unoptimized + debuginfo] target(s) in 0.13s
info: Loading save analysis from "/home/ryo/src/local/a/solve/target/debug/deps/save-analysis/solve-4eea33c8603d6001.json"
    Bundling the code
    Checking cargo-equip-check-output-6j2i3j3tgtugeaqm v0.1.0 (/tmp/cargo-equip-check-output-6j2i3j3tgtugeaqm)
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s

License

Dual-licensed under MIT or Apache-2.0.

Dependencies

~12MB
~232K SLoC