From 95b357002df66837456516dc26b13735e9892b34 Mon Sep 17 00:00:00 2001 From: Andrew Helwer Date: Thu, 18 Apr 2024 14:07:34 -0700 Subject: [PATCH] Made CLI more terse (#10) Signed-off-by: Andrew Helwer <2n8rn1w1f@mozmail.com> --- .github/workflows/ci.yml | 25 +++++-------- .github/workflows/release.yml | 26 +++++--------- Cargo.toml | 18 +++++----- README.md | 17 +++++---- src/lib.rs | 12 +++---- src/main.rs | 68 ++++++++++++++++------------------- tests/corpus | 2 +- 7 files changed, 72 insertions(+), 96 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93134cd..656e8b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,24 +15,15 @@ jobs: fail-fast: false steps: - name: Clone repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: - submodules: recursive - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable + submodules: true + - name: Use stable rust toolchain + run: rustup default stable - name: Build - uses: actions-rs/cargo@v1 - with: - command: build + run: cargo build - name: Check Formatting - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --check + run: cargo fmt --check - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: -- --nocapture + run: cargo test -- --nocapture + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73ce4ef..0540e82 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,16 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone repo - uses: actions/checkout@v3 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable + uses: actions/checkout@v4 + - name: Use stable rust toolchain + run: rustup default stable - name: Publish - uses: actions-rs/cargo@v1 - with: - command: publish - args: --token ${{secrets.CRATES_AUTH_TOKEN}} + run: cargo publish --token ${{secrets.CRATES_AUTH_TOKEN}} github-release-binaries: runs-on: ${{ matrix.os }} strategy: @@ -32,16 +27,11 @@ jobs: fail-fast: false steps: - name: Clone repo - uses: actions/checkout@v3 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable + uses: actions/checkout@v4 + - name: Use stable rust toolchain + run: rustup default stable - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release + run: cargo build --release - name: Package Binary if: matrix.os == 'windows-latest' shell: pwsh diff --git a/Cargo.toml b/Cargo.toml index feda42c..5f89fb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,25 @@ [package] name = "tlauc" description = "Rewrites TLA⁺ specs to use Unicode symbols instead of ASCII, and vice-versa" -version = "0.2.0" +version = "1.0.0" authors = ["Andrew Helwer <2n8rn1w1f@mozmail.com>"] repository = "https://github.com/tlaplus-community/tlauc" license = "MIT" readme = "README.md" keywords = ["tla+", "tlaplus", "pluscal", "unicode"] categories = ["command-line-utilities", "text-editors"] -edition = "2018" +edition = "2021" exclude = ["tests", ".github", ".gitignore", ".gitmodules"] [dependencies] -anyhow = "1.0.75" -clap = { version = "4.0.32", features = ["derive"] } +anyhow = "1.0.81" +clap = { version = "4.5.4", features = ["derive"] } csv = "1.3.0" -serde = { version = "1.0.192", features = ["derive"] } -tree-sitter = "0.20.10" -tree-sitter-tlaplus = "1.0.6" +serde = { version = "1.0.197", features = ["derive"] } +tree-sitter = "0.22.5" +tree-sitter-tlaplus = "1.2.8" [dev-dependencies] -glob = "0.3.0" -rayon = "1.6.1" +glob = "0.3.1" +rayon = "1.10.0" diff --git a/README.md b/README.md index 6c94707..3084581 100644 --- a/README.md +++ b/README.md @@ -37,17 +37,20 @@ To get the command line tool, either download it directly from [a release](https 1. Run `cargo install tlauc` 1. Ensure the [cargo installation directory](https://doc.rust-lang.org/cargo/commands/cargo-install.html#description) is on your path -From the command line, convert a TLA⁺ file from ASCII to Unicode as follows: +From the command line, convert a TLA⁺ file from ASCII to Unicode in place as follows: ```sh -tlauc unicode --input Ascii.tla --output Unicode.tla +tlauc Ascii.tla ``` -Convert from Unicode to ASCII: +Convert from Unicode to ASCII in place: ```sh -tlauc ascii --input Unicode.tla --output Ascii.tla +tlauc Unicode.tla --ascii ``` -By default, the program will fail if a file exists at the output location; override this behavior with the `--overwrite` flag. -There are also several safety checks performed during the translation process, like that the input spec parses correctly and that the output spec has the same parse tree as the input spec. -You can override these safety checks with the `-f` or `--force` flag, which also sets the `--overwrite` flag. +To output to a separate file instead of overwriting the input, use the `--output` or `-o` parameter with a filepath. +There are several safety checks performed during the translation process, like that the input spec parses correctly and that the output spec has the same parse tree as the input spec. +You can override these safety checks with the `--force` or `-f` flag. + +If parse errors exist their locations will be output as a best-effort list of line numbers. +Unfortunately tree-sitter does not expose more advanced parse error reporting at this time. To consume the library, add [the tlauc package](https://crates.io/crates/tlauc) as a dependency of your project then use it as follows: ```rs diff --git a/src/lib.rs b/src/lib.rs index 0744019..7249e13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ pub enum TlaError { pub fn rewrite(input: &str, mode: &Mode, force: bool) -> Result { let mut parser = Parser::new(); parser - .set_language(tree_sitter_tlaplus::language()) + .set_language(&tree_sitter_tlaplus::language()) .expect("Error loading TLA⁺ grammar"); let mut cursor = QueryCursor::new(); @@ -313,7 +313,7 @@ struct InfixOp { impl JList { fn query() -> Query { Query::new( - tree_sitter_tlaplus::language(), + &tree_sitter_tlaplus::language(), "[(conj_list) (disj_list)] @jlist", ) .unwrap() @@ -321,7 +321,7 @@ impl JList { fn terminating_infix_op_query() -> Query { Query::new( - tree_sitter_tlaplus::language(), + &tree_sitter_tlaplus::language(), "(bound_infix_op lhs: [(conj_list) (disj_list)]) @capture", ) .unwrap() @@ -404,7 +404,7 @@ fn mark_symbols(tree: &Tree, cursor: &mut QueryCursor, tla_lines: &mut [TlaLine] .map(|s| s.source_query(mode)) .collect::>() .join(""); - let query = Query::new(tree_sitter_tlaplus::language(), queries).unwrap(); + let query = Query::new(&tree_sitter_tlaplus::language(), queries).unwrap(); for capture in cursor.matches(&query, tree.root_node(), "".as_bytes()) { let capture = capture.captures[0]; @@ -533,7 +533,7 @@ mod tests { fn check_ascii_replaced(text: &str) { let mut parser = Parser::new(); parser - .set_language(tree_sitter_tlaplus::language()) + .set_language(&tree_sitter_tlaplus::language()) .unwrap(); let tree = parser.parse(&text, None).unwrap(); assert!(!tree.root_node().has_error()); @@ -543,7 +543,7 @@ mod tests { .map(|s| s.ascii_query()) .collect::>() .join(""); - let query = Query::new(tree_sitter_tlaplus::language(), &queries).unwrap(); + let query = Query::new(&tree_sitter_tlaplus::language(), &queries).unwrap(); assert_eq!( 0, cursor diff --git a/src/main.rs b/src/main.rs index 6905faf..ca96715 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,22 @@ use anyhow::{anyhow, Context, Result}; -use clap::{Parser, Subcommand}; +use clap::Parser; use std::fs::File; use std::io::{Read, Write}; -use std::path::Path; +use std::path::{Path, PathBuf}; use tlauc::{rewrite, Mode, TlaError}; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Args { - #[command(subcommand)] - action: Action, -} - -#[derive(Subcommand)] -enum Action { - #[command(about = "Convert symbols in a TLA⁺ file from ASCII to Unicode")] - Unicode(Config), - - #[command(about = "Convert symbols in a TLA⁺ file from Unicode to ASCII")] - Ascii(Config), -} - -#[derive(Parser)] -struct Config { - #[arg(short, long, help = "Path to TLA⁺ file to read as input")] - input: String, + #[arg(help = "Path to TLA⁺ file to convert")] + input: PathBuf, #[arg( short, long, - help = "Path to file to use as output; will fail if file already exists unless using --overwrite" + help = "Optional path to output; will overwrite input file by default" )] - output: String, + output: Option, #[arg( short, @@ -44,37 +29,44 @@ struct Config { #[arg( long, default_value_t = false, - help = "Whether to overwrite any file existing at the output path; also set by --force" + help = "Convert the TLA⁺ file to ASCII instead of Unicode" )] - overwrite: bool, + ascii: bool, } fn main() -> Result<()> { let args = Args::parse(); - match args.action { - Action::Unicode(config) => convert(&config, Mode::AsciiToUnicode), - Action::Ascii(config) => convert(&config, Mode::UnicodeToAscii), - } + let output_path = if let Some(output_path) = args.output { + output_path + } else { + args.input.clone() + }; + convert( + args.input.as_path(), + output_path.as_path(), + if args.ascii { + Mode::UnicodeToAscii + } else { + Mode::AsciiToUnicode + }, + args.force, + ) } -fn convert(config: &Config, mode: Mode) -> Result<()> { +fn convert(input_path: &Path, output_path: &Path, mode: Mode, force: bool) -> Result<()> { let mut input = String::new(); { - let mut input_file = File::open(&config.input) - .context(format!("Failed to open input file [{}]", &config.input))?; + let mut input_file = File::open(input_path) + .context(format!("Failed to open input file [{:?}]", input_path))?; input_file .read_to_string(&mut input) - .context(format!("Failed to read input file [{}]", &config.input))?; - } - - if Path::new(&config.output).exists() && !(config.overwrite || config.force) { - return Err(anyhow!("File already exists at output file location [{}]; use --overwrite flag to overwrite it", &config.output)); + .context(format!("Failed to read input file [{:?}]", input_path))?; } - let mut output_file = File::create(&config.output)?; - match rewrite(&input, &mode, config.force) { + let mut output_file = File::create(output_path)?; + match rewrite(&input, &mode, force) { Ok(output) => { - output_file.write_all(output.as_bytes()).context(format!("Failed to write to output file [{}]", &config.output))?; + output_file.write_all(output.as_bytes()).context(format!("Failed to write to output file [{:?}]", output_path))?; Ok(()) }, Err(TlaError::InputFileParseError { error_lines, .. }) => { diff --git a/tests/corpus b/tests/corpus index 62d104f..b7d2d8c 160000 --- a/tests/corpus +++ b/tests/corpus @@ -1 +1 @@ -Subproject commit 62d104f2e1cfd63b3f928342b8f634298706d145 +Subproject commit b7d2d8c2433854c391b43fe5557a8992cb601bbf