diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..aed2147 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,87 @@ +name: Build +on: + pull_request: + push: + branches: + - master + schedule: + - cron: '00 01 * * *' + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - uses: Swatinem/rust-cache@v1 + + - name: Run cargo check + uses: actions-rs/cargo@v1 + with: + command: check + + test: + name: Test Suite + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + rust: [stable] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + + - uses: Swatinem/rust-cache@v1 + + - name: Run cargo test + uses: actions-rs/cargo@v1 + with: + command: test + + + lints: + name: Lints + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + with: + submodules: true + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt, clippy + + - uses: Swatinem/rust-cache@v1 + + - name: Run cargo fmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + - name: Run cargo clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: -- -D warnings \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..0de6f65 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,161 @@ +name: Release +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +env: + BIN_NAME: mono-diagram + PROJECT_NAME: mono-diagram + REPO_NAME: Wayoung7/mono-diagram + +jobs: + dist: + name: Dist + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + build: [x86_64-linux, aarch64-linux, x86_64-macos, x86_64-windows] + include: + - build: x86_64-linux + os: ubuntu-20.04 + rust: stable + target: x86_64-unknown-linux-gnu + cross: false + - build: aarch64-linux + os: ubuntu-20.04 + rust: stable + target: aarch64-unknown-linux-gnu + cross: true + - build: x86_64-macos + os: macos-latest + rust: stable + target: x86_64-apple-darwin + cross: false + - build: x86_64-windows + os: windows-2019 + rust: stable + target: x86_64-pc-windows-msvc + cross: false + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + with: + submodules: true + + - name: Install ${{ matrix.rust }} toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + + - name: Run cargo test + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.cross }} + command: test + args: --release --locked --target ${{ matrix.target }} + + - name: Build release binary + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.cross }} + command: build + args: --release --locked --target ${{ matrix.target }} + + - name: Strip release binary (linux and macos) + if: matrix.build == 'x86_64-linux' || matrix.build == 'x86_64-macos' + run: strip "target/${{ matrix.target }}/release/$BIN_NAME" + + - name: Strip release binary (arm) + if: matrix.build == 'aarch64-linux' + run: | + docker run --rm -v \ + "$PWD/target:/target:Z" \ + rustembedded/cross:${{ matrix.target }} \ + aarch64-linux-gnu-strip \ + /target/${{ matrix.target }}/release/$BIN_NAME + + - name: Build archive + shell: bash + run: | + mkdir dist + if [ "${{ matrix.os }}" = "windows-2019" ]; then + cp "target/${{ matrix.target }}/release/$BIN_NAME.exe" "dist/" + else + cp "target/${{ matrix.target }}/release/$BIN_NAME" "dist/" + fi + + - uses: actions/upload-artifact@v2.2.4 + with: + name: bins-${{ matrix.build }} + path: dist + + publish: + name: Publish + needs: [dist] + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + with: + submodules: false + + - uses: actions/download-artifact@v2 + - run: ls -al bins-* + + - name: Calculate tag name + run: | + name=dev + if [[ $GITHUB_REF == refs/tags/v* ]]; then + name=${GITHUB_REF:10} + fi + echo ::set-output name=val::$name + echo TAG=$name >> $GITHUB_ENV + id: tagname + + - name: Build archive + shell: bash + run: | + set -ex + + rm -rf tmp + mkdir tmp + mkdir dist + + for dir in bins-* ; do + platform=${dir#"bins-"} + unset exe + if [[ $platform =~ "windows" ]]; then + exe=".exe" + fi + pkgname=$PROJECT_NAME-$TAG-$platform + mkdir tmp/$pkgname + # cp LICENSE README.md tmp/$pkgname + mv bins-$platform/$BIN_NAME$exe tmp/$pkgname + chmod +x tmp/$pkgname/$BIN_NAME$exe + + if [ "$exe" = "" ]; then + tar cJf dist/$pkgname.tar.xz -C tmp $pkgname + else + (cd tmp && 7z a -r ../dist/$pkgname.zip $pkgname) + fi + done + + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: dist/* + file_glob: true + tag: ${{ steps.tagname.outputs.val }} + overwrite: true + + - name: Extract version + id: extract-version + run: | + printf "::set-output name=%s::%s\n" tag-name "${GITHUB_REF#refs/tags/}" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index be0ce3e..c6dd1ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,7 +37,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -47,7 +47,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -56,24 +56,6 @@ version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" -[[package]] -name = "autocfg" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - [[package]] name = "block" version = "0.1.6" @@ -171,31 +153,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crossterm" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" -dependencies = [ - "bitflags 2.5.0", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -277,16 +234,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -308,18 +255,6 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mono-diagram" version = "0.1.0" @@ -328,7 +263,6 @@ dependencies = [ "clap", "clap_derive", "clipboard", - "crossterm", "pest", "pest_derive", "petgraph", @@ -370,29 +304,6 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", -] - [[package]] name = "pest" version = "2.7.9" @@ -502,21 +413,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sha2" version = "0.10.8" @@ -528,42 +424,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "strsim" version = "0.11.1" @@ -659,37 +519,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -698,46 +534,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -750,48 +568,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/Cargo.toml b/Cargo.toml index 17310df..864b875 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,15 +2,21 @@ name = "mono-diagram" version = "0.1.0" edition = "2021" +authors = ["Wayoung7 "] +description = "A cross-platform tool for generating plain-text diagrams from a certain syntax" +readme = "README.md" +repository = "https://github.com/Wayoung7/mono-diagram" +license = "MIT" +keywords = ["diagram", "layout", "ascii-art", "cli", "graph"] +categories = ["command-line-utilities", "visualization", "parser-implementations"] +exclude = ["src/mono-diagram/watch.rs"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "1.0.82" clap = "4.5.4" clap_derive = "4.5.4" clipboard = "0.5.0" -crossterm = "0.27.0" pest = "2.7.9" pest_derive = "2.7.9" petgraph = "0.6.4" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a370caf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Wayoung7 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README-zh-CN.md b/README-zh-CN.md new file mode 100644 index 0000000..b131abf --- /dev/null +++ b/README-zh-CN.md @@ -0,0 +1,263 @@ + + + +``` + + __ __ ______ __ __ ______ _____ __ ______ ______ ______ ______ __ __ +/\ "-./ \ /\ __ \ /\ "-.\ \ /\ __ \ /\ __-. /\ \ /\ __ \ /\ ___\ /\ == \ /\ __ \ /\ "-./ \ +\ \ \-./\ \ \ \ \/\ \ \ \ \-. \ \ \ \/\ \ \ \ \/\ \ \ \ \ \ \ __ \ \ \ \__ \ \ \ __< \ \ __ \ \ \ \-./\ \ + \ \_\ \ \_\ \ \_____\ \ \_\\"\_\ \ \_____\ \ \____- \ \_\ \ \_\ \_\ \ \_____\ \ \_\ \_\ \ \_\ \_\ \ \_\ \ \_\ + \/_/ \/_/ \/_____/ \/_/ \/_/ \/_____/ \/____/ \/_/ \/_/\/_/ \/_____/ \/_/ /_/ \/_/\/_/ \/_/ \/_/ + + +``` +

+
+Mono-Diagram +
+

+ +

+crates.io +License +

+ +
+ +[English](./README.md) / [简体中文](./README-zh-CN.md) + +
+ +mono-diagram 是一个跨平台的生成纯文本图表的工具。纯文本图表的优势在于可以在任何地方显示,比如用在代码注释中。 + +## 使用方法 + +### 定义图表 + +使用 mono-diagram 的基本思路是写一个包含一个或多个图表定义的文件,然后传入程序,程序会生成图表。每个图表开头需要用标签声明图标类型。 + +
+ 二叉树 + +标签: `[binary_tree]` + +输入: + +``` +[binary_tree] // 声明标签 +a->b,c // 节点a有左子树b和右子树c +b->d,f // 节点名字类似于变量名 +f->fa,fb +c->k,m +k->e, // k只有左子树 +m->,x + +a:2 // 给每个节点名赋值 +b:0.42 +c:9.5 +f:-3 +k:abc +m:2 // 不同的节点可以有相同的值 +d:001 +fa:451 +fb:8.90 +x:1.2 +``` + +输出: + +``` + ___2___ + ___/ \___ + 0.42 9.5 + _/ \_ _/ \_ + 001 -3 abc 2 + / \ / \ + 451 8.90 e 1.2 +``` + +
+ +
+ 有向非循环图 (DAG) + +标签: `[dag]` + +输入: + +``` +[dag] +a->b // 节点名->节点名 代表一条边 +a->c // 有向非循环图不能含有循环 +b->d +c->f +c->g +a->f +d->da +d->db +g->gg +a->gg + + +a:Home Page // 赋值 +b:Main Section 1 +c:Main Section 2 +d:Subsection 1 +f:Subsection 2 +g:Subsection 3 +da:Sub-sub +db:Sub-sub +gg:#page# +``` + +输出: + +``` + ┌───────────────────────────────────────────────────┐ + │ Home Page │ + └┬─────────────────┬──┬────────────────────────────┬┘ + ┌V───────────────┐ │ ┌V───────────────┐ │ + │ Main Section 1 │ │ │ Main Section 2 │ │ + └┬───────────────┘ │ └┬────────────┬──┘ │ + ┌V─────────────┐ ┌─V──V─────────┐ ┌V─────────────┐ │ + │ Subsection 1 │ │ Subsection 2 │ │ Subsection 3 │ │ + └┬───────────┬─┘ └──────────────┘ └┬─────────────┘ │ + ┌V────────┐ ┌V────────┐ ┌──────────V───────────────V┐ + │ Sub-sub │ │ Sub-sub │ │ #page# │ + └─────────┘ └─────────┘ └───────────────────────────┘ + +``` + +*注:此生成不稳定,每次生成可能会得到不同的图* + +
+ + + +
+ 表格 + +标签: `[table]` + +输入: + +``` +[table] \\ 每列用 '|' 分隔,每行用换行分隔 +Base Class Member|Public Inheritance|Protected Inheritance|Private Inheritance +Public|Public|Protected|Private +Protected|Protected|Protected|Private +Private|Hidden|Hidden|Hidden +``` + +输出: + +``` ++-------------------+--------------------+-----------------------+---------------------+ +| Base Class Member | Public Inheritance | Protected Inheritance | Private Inheritance | ++-------------------+--------------------+-----------------------+---------------------+ +| Public | Public | Protected | Private | ++-------------------+--------------------+-----------------------+---------------------+ +| Protected | Protected | Protected | Private | ++-------------------+--------------------+-----------------------+---------------------+ +| Private | Hidden | Hidden | Hidden | ++-------------------+--------------------+-----------------------+---------------------+ +``` + +
+ + + +
+ 网格 + +标签: `[grid]` + +输入: + +``` +[grid] {10, 7} // The grid has 10 colums and 7 rows + +1,1:a +6,2:l // The cell at column 6, row 2 has content 'l' +3,3:j +10,5:m +2,7:k +``` + +输出: + +``` ++---+---+---+---+---+---+---+---+---+---+ +| a | | | | | | | | | | ++---+---+---+---+---+---+---+---+---+---+ +| | | | | | l | | | | | ++---+---+---+---+---+---+---+---+---+---+ +| | | j | | | | | | | | ++---+---+---+---+---+---+---+---+---+---+ +| | | | | | | | | | | ++---+---+---+---+---+---+---+---+---+---+ +| | | | | | | | | | m | ++---+---+---+---+---+---+---+---+---+---+ +| | | | | | | | | | | ++---+---+---+---+---+---+---+---+---+---+ +| | k | | | | | | | | | ++---+---+---+---+---+---+---+---+---+---+ +``` + +
+ +### 命令行参数 + +``` +用法: mono-diagram [OPTIONS] + +参数: + + 文件路径 + +选项: + -p, --prefix + 给输出的每行加一个前缀 + + 可用于代码注释 + + -c, --copy + 复制到剪贴板 + + -h, --help + Print help (see a summary with '-h') + + -V, --version + Print version +``` + +#### 示例命令 + +```bash +mono-diagram examples/test -c -p "# " +``` + +## 安装 + +安装 [rust](https://www.rust-lang.org/tools/install) + +然后运行命令: + +~~~bash +cargo install mono-diagram +~~~ + +## TODO + +1. dag 中的节点可以是表格 (这样就可以画类图) +2. 优化 dag 中边的绘制 +3. 增加韦恩图 +4. 增加时序图 +5. 增加表格的单元格合并 + +## 贡献和帮助 + +非常欢迎 contribution,就算只是 contribute idea 也很欢迎 + +如有 bug 请开 issue (dag 应该是有 bug 的) + diff --git a/README.md b/README.md index eae387b..5c4f24b 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,15 @@ + + + ``` - _ _ - | (_) - _ __ ___ ___ _ __ ___ ______ __| |_ __ _ __ _ _ __ __ _ _ __ ___ -| '_ ` _ \ / _ \| '_ \ / _ \______/ _` | |/ _` |/ _` | '__/ _` | '_ ` _ \ -| | | | | | (_) | | | | (_) | | (_| | | (_| | (_| | | | (_| | | | | | | -|_| |_| |_|\___/|_| |_|\___/ \__,_|_|\__,_|\__, |_| \__,_|_| |_| |_| - __/ | - |___/ + + __ __ ______ __ __ ______ _____ __ ______ ______ ______ ______ __ __ +/\ "-./ \ /\ __ \ /\ "-.\ \ /\ __ \ /\ __-. /\ \ /\ __ \ /\ ___\ /\ == \ /\ __ \ /\ "-./ \ +\ \ \-./\ \ \ \ \/\ \ \ \ \-. \ \ \ \/\ \ \ \ \/\ \ \ \ \ \ \ __ \ \ \ \__ \ \ \ __< \ \ __ \ \ \ \-./\ \ + \ \_\ \ \_\ \ \_____\ \ \_\\"\_\ \ \_____\ \ \____- \ \_\ \ \_\ \_\ \ \_____\ \ \_\ \_\ \ \_\ \_\ \ \_\ \ \_\ + \/_/ \/_/ \/_____/ \/_/ \/_/ \/_____/ \/____/ \/_/ \/_/\/_/ \/_____/ \/_/ /_/ \/_/\/_/ \/_/ \/_/ + + ```


@@ -14,6 +17,17 @@ Mono-Diagram

+

+crates.io +License +

+ +
+ +[English](./README.md) / [简体中文](./README-zh-CN.md) + +
+ A cross-platform command line tool for generating plain-text diagrams from a certain syntax. The biggest advantage of plain-text diagrams is that it can fit in anywhere. ## Usage @@ -114,6 +128,8 @@ Output diagram: ``` +*Note: this dag graph is not stable, meaning you may get graph with different looking* + @@ -190,9 +206,42 @@ Output diagram: -### Pass to program (Command Line Arguments) +### Command Line Arguments + +``` +Usage: mono-diagram [OPTIONS] + +Arguments: + + The path to the input file + +Options: + -p, --prefix + Add a prefix to each line in the output + + This is useful when you want to paste the diagram to code comments -Binary name: `mono-diagram` + -c, --copy + Copy the output to your computer clipboard + + -h, --help + Print help (see a summary with '-h') + + -V, --version + Print version +``` + +#### Example commands + +Parse the file examples/test, and output with prefix '# ', then copy to clipboard: + +```bash +mono-diagram examples/test -c -p "# " +``` + +## Examples + +You can find some sample input files in [`examples/`](./examples/) in the project directory. ## Installation @@ -204,4 +253,17 @@ Then, simply run the following commands: cargo install mono-diagram ~~~ +## TODO + +1. Node in dag can be a table. This enables you to draw class diagram (this is easy) +2. Improve edge drawing in dag (this is hard) +3. Add plain-text Venn diagram (this is hard) +4. Add plain-text sequence diagram (this is easy) +5. Make more flexible table with cells spanning multiple rows and columns + +## Contribution and Help + +Any contribution is always welcomed, even just ideas. + +Feel free to open an issue or contact me if you find any bugs. diff --git a/examples/dag b/examples/dag index 4841754..043a02f 100644 --- a/examples/dag +++ b/examples/dag @@ -1,5 +1,4 @@ [dag] - a->b a->c b->d @@ -9,7 +8,10 @@ a->f d->da d->db g->gg -a->gg +b->gg +b->f +c->da +z->g a:Home Page @@ -22,3 +24,4 @@ da:Sub-sub db:Sub-sub gg:#page# +z: Another Entrance diff --git a/examples/dag2 b/examples/dag2 deleted file mode 100644 index 273ea85..0000000 --- a/examples/dag2 +++ /dev/null @@ -1,22 +0,0 @@ -[dag] - -b->h->k -b->k -z->g->k -z->k -a->g -c->i->m->n -a->n -m->k -z->c -z->d -a->c->e -b->c->j -a->j -f->e->j -d->h -d->g -d->i->j -c->d -k->j -f->i \ No newline at end of file diff --git a/examples/dag3 b/examples/dag3 deleted file mode 100644 index 011337c..0000000 --- a/examples/dag3 +++ /dev/null @@ -1,10 +0,0 @@ -[dag] -socks -> shoes -underwear -> shoes -underwear -> pants -pants -> shoes -pants -> belt -belt -> jacket -shirt -> belt -shirt -> tie -tie -> jacket diff --git a/examples/test b/examples/test index 3a63f85..c9e352d 100644 --- a/examples/test +++ b/examples/test @@ -23,6 +23,43 @@ Public|Public|Protected|Private Protected|Protected|Protected|Private Private|Hidden|Hidden|Hidden +[dag] +a->b +a->c +b->d +c->f +c->g +a->f +d->da +d->db +g->gg +b->gg +b->f +c->da +z->g + + +a:Home Page +b:Main Section 1 +c:Main Section 2 +d:Subsection 1 +f:Subsection 2 +g:Subsection 3 +da:Sub-sub +db:Sub-sub +gg:#page# + +z: Another Entrance + +[grid] {10, 7} + +1,1:a +6,2:l +3,3:j +10,5:m +2,7:k + + diff --git a/src/mono-diagram/args.rs b/src/mono-diagram/args.rs index 437277c..cc89251 100644 --- a/src/mono-diagram/args.rs +++ b/src/mono-diagram/args.rs @@ -1,31 +1,18 @@ -use clap_derive::{Parser, Subcommand}; +use clap_derive::Parser; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] #[command(propagate_version = true)] pub struct Cli { - #[clap(subcommand)] - pub command: Commands, + /// Add a prefix to each line in the output + /// + /// This is useful when you want to paste the diagram to code comments #[arg(short, long, value_name = "PREFIX")] pub prefix: Option, -} - -#[derive(Debug, Subcommand)] -pub enum Commands { - Build { - #[clap(help = "file to build")] - file: String, - }, - Print { - #[clap(help = "file to print")] - file: String, - }, - Watch { - #[clap(help = "file to watch")] - file: String, - }, - Copy { - #[clap(help = "file to copy")] - file: String, - }, + /// Copy the output to your computer clipboard + #[arg(short, long)] + pub copy: bool, + /// The path to the input file + #[arg()] + pub file_path: String, } diff --git a/src/mono-diagram/diagram/binary_tree_diagram.rs b/src/mono-diagram/diagram/binary_tree_diagram.rs index 26f6467..3606ad5 100644 --- a/src/mono-diagram/diagram/binary_tree_diagram.rs +++ b/src/mono-diagram/diagram/binary_tree_diagram.rs @@ -12,18 +12,11 @@ use crate::{data_structure::binary_tree::TreeNode, utils::pad_string_center}; use super::Diagram; +#[derive(Default)] pub struct BinaryTreeDiagram { data: Box>, } -impl Default for BinaryTreeDiagram { - fn default() -> Self { - Self { - data: Box::new(TreeNode::default()), - } - } -} - impl Diagram for BinaryTreeDiagram { fn parse_from_str(&mut self, input: &str) -> Result<()> { let mut root: &str = ""; @@ -51,7 +44,7 @@ impl Diagram for BinaryTreeDiagram { let node_variable_name = statement.next().unwrap().as_str(); let childs = parse_childs(statement.next().unwrap().into_inner()); relationship_map.insert(node_variable_name, childs); - if root == "" { + if root.is_empty() { root = node_variable_name; } if let Some(lchild) = childs.0 { @@ -90,7 +83,7 @@ impl Diagram for BinaryTreeDiagram { if i != degree - 1 { // Print arrow assert!(next.len() % 2 == 0); - for (_idx, pair) in next.chunks(2).enumerate() { + for pair in next.chunks(2) { (0..=spacing[i + 1]).try_for_each(|_| write!(&mut buffer, " "))?; if pair[0].is_some() { (0..spacing[i]).try_for_each(|_| write!(&mut buffer, "_"))?; @@ -108,7 +101,7 @@ impl Diagram for BinaryTreeDiagram { (0..=spacing[i + 1]).try_for_each(|_| write!(&mut buffer, " "))?; write!(&mut buffer, " ")?; } - write!(&mut buffer, "\n")?; + writeln!(&mut buffer)?; } // Print data @@ -125,7 +118,7 @@ impl Diagram for BinaryTreeDiagram { write!(&mut buffer, "{:^width$} ", "") } })?; - write!(&mut buffer, "\n")?; + writeln!(&mut buffer)?; let mut childs: Vec>>> = Vec::new(); for node in next.iter() { diff --git a/src/mono-diagram/diagram/dag_diagram.rs b/src/mono-diagram/diagram/dag_diagram.rs index 06c66c9..d1c16b6 100644 --- a/src/mono-diagram/diagram/dag_diagram.rs +++ b/src/mono-diagram/diagram/dag_diagram.rs @@ -1,21 +1,18 @@ use std::{ - borrow::{Borrow, BorrowMut}, - cell::RefCell, cmp::{max, min}, collections::{HashMap, HashSet}, - iter::repeat, }; use anyhow::Error; use pest::Parser; use pest_derive::Parser; use petgraph::{ + algo::is_cyclic_directed, graph::{DiGraph, EdgeIndex, NodeIndex}, - visit::{Dfs, EdgeRef, IntoEdgeReferences, IntoNeighbors, IntoNeighborsDirected}, + visit::EdgeRef, Direction::{self, Incoming, Outgoing}, - Graph, }; -use rand::{seq::IteratorRandom, thread_rng, Rng}; +use rand::{thread_rng, Rng}; use super::Diagram; @@ -56,7 +53,7 @@ impl Diagram for DagGraph { Rule::relationship => { let mut r = line.into_inner(); let mut from = r.next().unwrap(); - while let Some(to) = r.next() { + for to in r { relationship_map.insert((from.as_str(), to.as_str())); from = to; } @@ -66,19 +63,25 @@ impl Diagram for DagGraph { } // println!("{:#?}\n\n{:#?}", relationship_map, assign_map); let dag = init_dag(&relationship_map); + if is_cyclic_directed(&dag) { + return Err(Error::msg( + "diagram error: directed acyclic graph has cycles", + )); + } let (mut dag, max_level) = assign_level(dag); dag = replace_text(add_dummy(dag), &assign_map); let levels = level_cnt(&dag, max_level); dag = permute(dag, &levels); let perm_levels = get_perm_levels(&dag, max_level); let (_w, _, line_height) = place_node(&mut dag, &levels, &perm_levels); - self.max_width = _w; // for i in dag.edge_indices() { // let (s, t) = dag.edge_endpoints(i).unwrap(); // println!("{}->{}", dag[s].value, dag[t].value); // } // print!("\n"); - (self.connections, self.spacing) = add_connections(&mut dag, &perm_levels); + let width_shift; + (self.connections, self.spacing, width_shift) = add_connections(&mut dag, &perm_levels); + self.max_width = _w + width_shift; self.max_height = self .spacing .iter() @@ -118,9 +121,9 @@ impl Diagram for DagGraph { buffer[y][_x] = PALETTE.chars().nth(1).unwrap(); buffer[y + h - 1][_x] = PALETTE.chars().nth(6).unwrap(); } - for _y in (y + 1)..(y + h - 1) { - buffer[_y][x] = PALETTE.chars().nth(3).unwrap(); - buffer[_y][x + w - 1] = PALETTE.chars().nth(4).unwrap(); + for line in buffer.iter_mut().take(y + h - 1).skip(y + 1) { + line[x] = PALETTE.chars().nth(3).unwrap(); + line[x + w - 1] = PALETTE.chars().nth(4).unwrap(); } for (idx, c) in self.data[n].value.chars().enumerate() { buffer[y + 1][x + 2 + idx] = c; @@ -168,7 +171,7 @@ impl Diagram for DagGraph { } // Draw first vertical let mut __y = _y - 1; - while buffer[__y][*x1] == ' ' { + while __y < self.max_height && *x1 < self.max_width && buffer[__y][*x1] == ' ' { buffer[__y][*x1] = PALETTE.chars().nth(9).unwrap(); __y -= 1; } @@ -178,7 +181,7 @@ impl Diagram for DagGraph { // Draw second vertical __y = _y + 1; - while buffer[__y][*x2] == ' ' { + while __y < self.max_height && *x2 < self.max_width && buffer[__y][*x2] == ' ' { buffer[__y][*x2] = PALETTE.chars().nth(9).unwrap(); __y += 1; } @@ -222,7 +225,7 @@ impl Diagram for DagGraph { let mut res: Vec = Vec::new(); for row in buffer.iter() { for c in row.iter() { - res.extend_from_slice(&c.encode_utf8(&mut [0; 4]).as_bytes()); + res.extend_from_slice(c.encode_utf8(&mut [0; 4]).as_bytes()); } res.push(b'\n'); } @@ -245,12 +248,12 @@ fn get_perm_levels(g: &Digraph, max_level: usize) -> Vec> { fn place_node( g: &mut Digraph, - levels: &Vec, + levels: &[usize], perm_levels: &Vec>, ) -> (usize, usize, Vec) { fn shift_right( g: &mut Digraph, - perm_levels: &Vec>, + perm_levels: &[Vec], level: usize, perm: usize, amount: usize, @@ -277,7 +280,7 @@ fn place_node( } let row_height: Vec = vec![3; levels.len()]; - let cl = cal_crossings_levels(&g, levels.len()); + let cl = cal_crossings_levels(g, levels.len()); let mut max_shift_right = 0; const MAX_LOOP: usize = 3; @@ -358,32 +361,32 @@ fn place_node( fn add_connections( g: &mut Digraph, - perm_levels: &Vec>, -) -> (Vec>, Vec<(isize, isize)>) { - fn shift_down(g: &mut Digraph, perm_levels: &Vec>, level: usize, amount: usize) { + perm_levels: &[Vec], +) -> (Vec>, Vec<(isize, isize)>, usize) { + fn shift_down(g: &mut Digraph, perm_levels: &[Vec], level: usize, amount: usize) { for r in &perm_levels[level - 1..] { for c in r { g[*c].pos.1 += amount; } } } - fn shift_right_one(g: &mut Digraph, connections: &mut Vec>, x: usize) { + fn shift_right_one(g: &mut Digraph, connections: &mut [Vec], x: usize) { for n in g.node_weights_mut() { if n.pos.0 >= x { n.pos.0 += 1; - } else if n.pos.0 < x && n.pos.0 + n.width - 1 >= x { + } else if n.pos.0 < x && n.pos.0 + n.width > x { n.width += 1; } } for r in connections.iter_mut() { for con in r.iter_mut() { match con { - Connection::Straight { from, dummy } => { + Connection::Straight { from, .. } => { if from.0 >= x { from.0 += 1; } } - Connection::Bent { x1, x2, y, dummy } => { + Connection::Bent { x1, x2, .. } => { if *x1 >= x { *x1 += 1; } @@ -417,18 +420,16 @@ fn add_connections( } else { None } + } else if x1 + w1 - 1 < x2 + 2 || x2 + w2 - 1 < x1 + 2 { + None } else { - if x1 + w1 - 1 < x2 + 2 || x2 + w2 - 1 < x1 + 2 { - None - } else { - Some(max(x1, x2) + 1) - } + Some(max(x1, x2) + 1) } } let mut ct: Vec> = vec![vec![]; perm_levels.len() - 1]; - fn connections_contain_x(c: &Vec>, i: usize, x: usize, d: Direction) -> bool { + fn connections_contain_x(c: &[Vec], i: usize, x: usize, d: Direction) -> bool { for con in &c[i] { if let Connection::Straight { from, .. } = con { if from.0 == x { @@ -444,11 +445,11 @@ fn add_connections( } } } - return false; + false } fn bent_overlap( - c: &Vec>, + c: &[Vec], i: usize, y_spacing: isize, x3: usize, @@ -470,7 +471,30 @@ fn add_connections( } } // print!(" <({}, ({:?}, ..)) NOT OVERLAP> ", y_spacing, (x3, x4)); - return false; + false + } + + fn vertical_overlap( + c: &[Vec], + i: usize, + y_spacing: isize, + x3: usize, + x4: usize, + ) -> (bool, bool) { + let mut res = (false, false); + for con in &c[i] { + if let Connection::Bent { x1, x2, y, .. } = con { + let (_x1, _x2) = if x1 < x2 { (x1, x2) } else { (x2, x1) }; + let (_x3, _x4) = if x3 < x4 { (x3, x4) } else { (x4, x3) }; + if *_x1 == x4 && y_spacing < *y { + res.1 = true; + } + if *_x2 == x3 && y_spacing > *y { + res.0 = true; + } + } + } + res } // Make straight connections @@ -496,56 +520,53 @@ fn add_connections( // } let mut spacing: Vec<(isize, isize)> = vec![(0, 0); perm_levels.len() - 1]; + let mut shift_right = 0; // println!("{:#?}", connections); // let connections_bent: Vec> = vec![vec![]; perm_levels.len() - 1]; // println!( // "{:?}", // connections.iter().map(|l| l.len()).collect::>() // ); + let mut res = g.clone(); for (idx, r) in perm_levels[..perm_levels.len() - 1].iter().enumerate() { for &c in r { // For each node let mut out_offset = 1; let mut childs = g .edges_directed(c, Outgoing) - .filter(|f| f.weight().visited == false) + .filter(|f| !f.weight().visited) .collect::>(); childs.sort_by(|a, b| g[a.target()].pos.0.cmp(&g[b.target()].pos.0)); - if spacing[idx] == (0, 0) && childs.len() != 0 { + if spacing[idx] == (0, 0) && !childs.is_empty() { spacing[idx] = (0, 1); } - // println!("CurNode: {}", g[c].value); for child_edge in childs.into_iter() { - // print!("Child {}: ", g[child_edge.target()].value); - // if child_edge.weight().visited == false { + // For each unvisited child assert!(!child_edge.weight().visited); let t = child_edge.target(); - out_offset = if g[c].dummy { + out_offset = if res[c].dummy { 0 } else { - while connections_contain_x(&ct, idx, g[c].pos.0 + out_offset, Outgoing) { + while connections_contain_x(&ct, idx, res[c].pos.0 + out_offset, Outgoing) { out_offset += 1; } out_offset }; - // print!("out_offset: {}, ", out_offset); - assert!(out_offset <= g[c].width); - let in_offset = if g[t].dummy { - // println!("target: {} is dummy", g[t].value); + assert!(out_offset <= res[c].width); + let in_offset = if res[t].dummy { 0 } else { - // println!("target: {} is not dummy", g[t].value); let mut _in_offset = 1; - while connections_contain_x(&ct, idx, g[t].pos.0 + _in_offset, Incoming) { + while connections_contain_x(&ct, idx, res[t].pos.0 + _in_offset, Incoming) { _in_offset += 1; } _in_offset }; - // print!("in_offset: {}, ", in_offset); - assert!(in_offset < g[t].width); + assert!(in_offset < res[t].width); + let mut found = false; - let x_from = g[c].pos.0 + out_offset; - let x_to = g[t].pos.0 + in_offset; + let mut x_from = res[c].pos.0 + out_offset; + let mut x_to = res[t].pos.0 + in_offset; assert!(x_from != x_to); for s in if x_to > x_from { spacing[idx].0..spacing[idx].1 @@ -557,7 +578,7 @@ fn add_connections( x1: x_from, x2: x_to, y: s, - dummy: (g[c].dummy, g[t].dummy), + dummy: (res[c].dummy, res[t].dummy), }); found = true; // print!("{}->{}, y: {} ", g[c].value, g[t].value, s); @@ -565,24 +586,34 @@ fn add_connections( } } if !found { + // Space up vertical + let y; if x_to > x_from { - ct[idx].push(Connection::Bent { - x1: x_from, - x2: x_to, - y: spacing[idx].0 - 1, - dummy: (g[c].dummy, g[t].dummy), - }); + y = spacing[idx].0 - 1; spacing[idx].0 -= 1; } else { - ct[idx].push(Connection::Bent { - x1: x_from, - x2: x_to, - y: spacing[idx].1, - dummy: (g[c].dummy, g[t].dummy), - }); + y = spacing[idx].1; spacing[idx].1 += 1; + }; + // Check vertical overlap + let vert_overlap_res = vertical_overlap(&ct, idx, y, x_from, x_to); + if vert_overlap_res.0 { + shift_right_one(&mut res, &mut ct, x_from + 1); + x_from += 1; + shift_right += 1; + } + + if vert_overlap_res.1 { + shift_right_one(&mut res, &mut ct, x_to + 1); + x_to += 1; + shift_right += 1; } - // print!("{}->{} shift, y: {} ", g[c].value, g[t].value, spacing[idx]); + ct[idx].push(Connection::Bent { + x1: x_from, + x2: x_to, + y, + dummy: (res[c].dummy, res[t].dummy), + }); } // println!( // "{:?}", @@ -597,7 +628,7 @@ fn add_connections( // println!("{:?}", spacing); // Shift things down for (idx, shift) in spacing.iter().enumerate() { - shift_down(g, perm_levels, idx + 2, (shift.1 - shift.0) as usize); + shift_down(&mut res, perm_levels, idx + 2, (shift.1 - shift.0) as usize); for r in ct[idx + 1..].iter_mut() { for con in r.iter_mut() { if let Connection::Straight { from, .. } = con { @@ -606,7 +637,7 @@ fn add_connections( } } } - + *g = res; // g.map( // |_, _| {}, // |i, d| { @@ -625,7 +656,7 @@ fn add_connections( // } // } - (ct, spacing) + (ct, spacing, shift_right) } fn init_dag(r: &HashSet<(&str, &str)>) -> Digraph { @@ -743,7 +774,7 @@ fn replace_text(g: Digraph, a: &HashMap<&str, &str>) -> Digraph { res } -fn permute(g: Digraph, levels: &Vec) -> Digraph { +fn permute(g: Digraph, levels: &[usize]) -> Digraph { // let mut permutation: HashMap> = HashMap::new(); // for n in g.node_indices() { // permutation @@ -769,7 +800,7 @@ fn permute(g: Digraph, levels: &Vec) -> Digraph { + 1; let mut outer_optimal = res.clone(); let mut outer_optimal_crossings = cal_crossings(&res, levels.len()); - const OUTER_MAX_LOOP: usize = 3; + const OUTER_MAX_LOOP: usize = 23; let mut outer_loop_cnt = 0; loop { if outer_loop_cnt >= OUTER_MAX_LOOP { @@ -788,7 +819,7 @@ fn permute(g: Digraph, levels: &Vec) -> Digraph { ); // print_layer(&inner_optimal, levels.len()); let mut inner_optimal_crossings = cal_crossings(&inner_optimal, levels.len()); - const INNER_MAX_LOOP: usize = 13; + const INNER_MAX_LOOP: usize = 23; let mut inner_loop_cnt = 0; let mut step: isize = -1; let mut current_level = largest_level; @@ -928,7 +959,7 @@ fn cal_level(g: &Digraph, i: NodeIndex) -> usize { } } -fn crossings(lines: &Vec<(usize, usize)>) -> usize { +fn crossings(lines: &[(usize, usize)]) -> usize { if lines.len() <= 1 { 0 } else { diff --git a/src/mono-diagram/main.rs b/src/mono-diagram/main.rs index 1cb19bf..b419438 100644 --- a/src/mono-diagram/main.rs +++ b/src/mono-diagram/main.rs @@ -3,73 +3,36 @@ mod data_structure; mod diagram; mod parser; mod utils; -mod watch; -use std::io::stdout; - -use args::{Cli, Commands}; +use args::Cli; use clap::Parser; use clipboard::{ClipboardContext, ClipboardProvider}; -use crossterm::{cursor, execute, terminal}; use parser::{parse, write}; use utils::add_prefix; -use watch::watch; fn main() { let cli = Cli::parse(); let prefix = cli.prefix; - match &cli.command { - Commands::Build { file } => build_cmd(file), - Commands::Print { file } => print_cmd(file, prefix), - // Commands::Watch { file } => watch_cmd(file, prefix), - Commands::Copy { file } => copy_cmd(file, prefix), - _ => (), - } -} - -fn build_cmd(file: &str) { - let res = parse(file); - match res { - Ok(_) => println!("Building succeed"), - Err(e) => println!("{}", e), - } -} - -fn print_cmd(file: &str, prefix: Option) { - let result = parse(file).and_then(|d| write(&d)); - match result { - Ok(d) => println!( - "{}", - add_prefix( - String::from_utf8_lossy(&d).to_string(), - &prefix.unwrap_or("".to_string()) - ) - ), - Err(e) => println!("{}", e), - } -} - -fn watch_cmd(file: &str, prefix: Option) { - match watch(file, prefix) { - Err(e) => { - execute!(&mut stdout(), cursor::Show, terminal::LeaveAlternateScreen).unwrap(); - terminal::disable_raw_mode().unwrap(); - println!("{}", e); - } - _ => (), - } -} - -fn copy_cmd(file: &str, prefix: Option) { + let copy = cli.copy; + let file = &cli.file_path; let result = parse(file).and_then(|d| write(&d)); match result { Ok(d) => { - let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); - ctx.set_contents(add_prefix( - String::from_utf8_lossy(&d).to_string(), - &prefix.unwrap_or("".to_string()), - )) - .unwrap(); + println!( + "{}", + add_prefix( + String::from_utf8_lossy(&d).to_string(), + &prefix.clone().unwrap_or("".to_string()) + ) + ); + if copy { + let mut ctx: ClipboardContext = ClipboardProvider::new().unwrap(); + ctx.set_contents(add_prefix( + String::from_utf8_lossy(&d).to_string(), + &prefix.unwrap_or("".to_string()), + )) + .unwrap(); + } } Err(e) => println!("{}", e), } diff --git a/src/mono-diagram/parser.rs b/src/mono-diagram/parser.rs index e84ea78..459d912 100644 --- a/src/mono-diagram/parser.rs +++ b/src/mono-diagram/parser.rs @@ -23,17 +23,13 @@ pub fn parse(script_path: &str) -> Result>> { .next() .unwrap(); for diagram in main.into_inner() { - match diagram.as_rule() { - Rule::diagram => { - let mut diagram_inner = diagram.into_inner(); - let title = diagram_inner.next().unwrap().into_inner().as_str(); - let content = diagram_inner.next().unwrap().as_str(); - let mut diagram = init_diagram(title); - diagram.parse_from_str(content)?; - parsed_diagrams.push(diagram); - } - - _ => (), + if diagram.as_rule() == Rule::diagram { + let mut diagram_inner = diagram.into_inner(); + let title = diagram_inner.next().unwrap().into_inner().as_str(); + let content = diagram_inner.next().unwrap().as_str(); + let mut diagram = init_diagram(title); + diagram.parse_from_str(content)?; + parsed_diagrams.push(diagram); } } Ok(parsed_diagrams) @@ -50,10 +46,10 @@ pub fn write(diagrams: &Vec>) -> Result> { fn init_diagram(title: &str) -> Box { match title { - "binary_tree" => Box::new(BinaryTreeDiagram::default()), - "table" => Box::new(TableDiagram::default()), - "grid" => Box::new(GridDiagram::default()), - "dag" => Box::new(DagGraph::default()), + "binary_tree" => Box::::default(), + "table" => Box::::default(), + "grid" => Box::::default(), + "dag" => Box::::default(), _ => panic!(""), } }