From 39622756af4eb0681e8a61d8b82fa7654471e244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Co=CC=82te=CC=81?= Date: Wed, 23 Dec 2020 18:42:37 -0500 Subject: [PATCH] Initial commit --- .github/workflows/ci.yml | 76 ++++++ .github/workflows/release.yml | 79 ++++++ .gitignore | 1 + .vscode/settings.json | 4 + Cargo.lock | 468 ++++++++++++++++++++++++++++++++++ Cargo.toml | 13 + LICENSE.md | 21 ++ README.md | 35 +++ d.sh | 29 +++ src/commands/cd.rs | 63 +++++ src/commands/clone.rs | 29 +++ src/commands/mod.rs | 5 + src/main.rs | 19 ++ src/utils/mod.rs | 3 + src/utils/path.rs | 21 ++ 15 files changed, 866 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 d.sh create mode 100644 src/commands/cd.rs create mode 100644 src/commands/clone.rs create mode 100644 src/commands/mod.rs create mode 100644 src/main.rs create mode 100644 src/utils/mod.rs create mode 100644 src/utils/path.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..37bfdd4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,76 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +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 + + - name: Run cargo check + uses: actions-rs/cargo@v1 + with: + command: check + + test: + name: Test Suite + 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 + + - 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 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt, clippy + + - 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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..f29f8f0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + release: + name: Create Github Release + runs-on: ubuntu-latest + steps: + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Release ${{ github.ref }} + draft: false + prerelease: false + + - name: Output Release URL File + run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt + + - name: Save Release URL File for publish + uses: actions/upload-artifact@v1 + with: + name: release_url + path: release_url.txt + + publish: + needs: [release] + name: Build for ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + artifact_name: d + asset_name: d-linux-amd64 + - os: macos-latest + artifact_name: d + asset_name: d-macos-amd64 + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --release --locked + + - name: Generate assets + run: | + tar -cvzf ${{ matrix.asset_name }}.tar.gz ${{ matrix.artifact_name }}.sh -C target/release ${{ matrix.artifact_name }} + + - name: Load Release URL File from release job + uses: actions/download-artifact@v1 + with: + name: release_url + + - name: Get Release File Name & Upload URL + id: get_release_info + run: | + value=`cat release_url/release_url.txt` + echo ::set-output name=upload_url::$value + env: + TAG_REF_NAME: ${{ github.ref }} + REPOSITORY_NAME: ${{ github.repository }} + + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release_info.outputs.upload_url }} + asset_path: ./${{ matrix.asset_name }}.tar.gz + asset_name: ${{ matrix.asset_name }}.tar.gz + asset_content_type: application/tar+gzip \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c95a33d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.defaultFormatter": "rust-lang.rust", + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2b0be33 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,468 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "const_fn" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" +dependencies = [ + "cfg-if 1.0.0", + "const_fn", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "d" +version = "0.0.1" +dependencies = [ + "regex", + "rff", + "shellexpand", + "structopt", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99de365f605554ae33f115102a02057d4fc18b01f3284d6870be0938743cfe7d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "rff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce6626007d6253f887f6767a61efc034b45498ecf8d8371c7c25388e8a99d46" +dependencies = [ + "clap", + "libc", + "rayon", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "shellexpand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdb7831b2d85ddf4a7b148aa19d0587eddbe8671a436b7bd1182eaad0f2829" +dependencies = [ + "dirs-next", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..79001fa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "d" +version = "0.0.1" +authors = ["Alexandre Côté "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rff = "0.3" +regex = "1" +structopt = "0.3" +shellexpand = "2.1" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..50f131b --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Alexandre Côté + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e13a788 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +## What is `d`? + +`d` is a tool to standardize your projects structure on your local machine. + +## Installation + +```bash +eval "$(curl -sSL https://bit.ly/2Kzj4Xd)" +``` + +Add this in your `.bashrc` or `.zshrc` file. + +```bash +if [ -f /opt/d/d.sh ]; then + source /opt/d/d.sh +fi +``` + +## Usage + +You can easily clone a new git repository by running: +``` +d clone https://github.com/alexandcote/d +``` +`d` will clone the repository in `~/src/github.com/alexandcote/d`. + +You can use `d` to navigate between your projects by running: +``` +d cd alexandcote/d +``` +`d` use fuzzy finder to find the right directory so you can simply run: + +``` +d cd ad +``` \ No newline at end of file diff --git a/d.sh b/d.sh new file mode 100644 index 0000000..d0cfb00 --- /dev/null +++ b/d.sh @@ -0,0 +1,29 @@ +case "$(basename "$(ps -p $$ | awk 'NR > 1 { sub(/^-/, "", $4); print $4 }')")" in + zsh) __source_dir="$(dirname "$0:A")" ;; + bash) __source_dir="$(builtin cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ;; + *) echo "unsupported shell" >&2 ; return 1 ;; +esac + +d() { + local tmp ret finalizer + + tmp="$(mktemp -u)" + exec 9>"${tmp}" + exec 8<"${tmp}" + rm -f "${tmp}" + + "${__source_dir}/d" "$@" + ret=$? + + while read finalizer; do + case "${finalizer}" in + cd:*) cd "${finalizer//cd:/}" ;; + *) ;; + esac + done <&8 + + exec 8<&- # close FD 8. + exec 9<&- # close FD 9. + + return ${ret} +} \ No newline at end of file diff --git a/src/commands/cd.rs b/src/commands/cd.rs new file mode 100644 index 0000000..5445ed7 --- /dev/null +++ b/src/commands/cd.rs @@ -0,0 +1,63 @@ +use rff::scorer::score; +use std::f64::NEG_INFINITY; +use std::path::PathBuf; + +use crate::utils::{chdir, get_base_path}; + +struct Candidate { + score: f64, + repository: String, +} + +pub fn cd(path: String) { + if path == "-" { + return chdir(&path); + } + + let repositories = list_all_repositories(); + let mut candidates: Vec = vec![]; + for repository in repositories { + let score = score(path.as_str(), &repository); + if score != NEG_INFINITY { + candidates.push(Candidate { score, repository }) + } + } + + if !candidates.is_empty() { + candidates.sort_by(|a, b| b.score.partial_cmp(&a.score).unwrap()); + return chdir(&candidates[0].repository); + } +} + +fn list_all_repositories() -> Vec { + let base_path = get_base_path().unwrap(); + let services = get_directories(&base_path).unwrap(); + let mut repositories: Vec = vec![]; + for service in services.iter() { + let owners = get_directories(&service).unwrap(); + for owner in owners.iter() { + let repo = get_directories(owner) + .unwrap() + .iter() + .map(|repo| repo.display().to_string()) + .collect::>(); + + repositories.extend(repo); + } + } + + repositories +} + +fn get_directories(path: &PathBuf) -> Result, std::io::Error> { + match path.read_dir() { + Ok(entries) => { + let directories = entries + .map(|entry| entry.unwrap().path()) + .filter(|path| path.is_dir()) + .collect::>(); + Ok(directories) + } + Err(error) => Err(error), + } +} diff --git a/src/commands/clone.rs b/src/commands/clone.rs new file mode 100644 index 0000000..8b8dabd --- /dev/null +++ b/src/commands/clone.rs @@ -0,0 +1,29 @@ +use regex::Regex; +use std::process::Command; + +use crate::utils::{chdir, get_base_path}; + +pub fn clone(repository: String) { + let target = get_target(&repository).unwrap(); + Command::new("git") + .arg("clone") + .arg(&repository) + .arg(&target) + .output() + .expect("failed to execute process"); + + chdir(&target); +} + +fn get_target(repository: &str) -> Result { + let base_path = get_base_path().unwrap(); + let re = Regex::new(r"[/@]([A-z]+.*)").unwrap(); + let path = re.captures(&repository).unwrap(); + let repository_path = path[1].replace(".git", "").replace(":", "/"); + if path.len() > 1 { + let value = format!("{}/{}", base_path.display(), repository_path); + return Ok(value); + } + + Err("Unsupported path") +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..2654c08 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,5 @@ +mod cd; +mod clone; + +pub use cd::cd; +pub use clone::clone; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0e9ef4c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,19 @@ +use structopt::StructOpt; + +mod commands; +mod utils; + +use commands::{cd, clone}; + +#[derive(StructOpt)] +enum Command { + Clone { repository: String }, + Cd { path: String }, +} + +fn main() { + match Command::from_args() { + Command::Clone { repository } => clone(repository), + Command::Cd { path } => cd(path), + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..d8ee63e --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1,3 @@ +mod path; + +pub use path::{chdir, get_base_path}; diff --git a/src/utils/path.rs b/src/utils/path.rs new file mode 100644 index 0000000..17413bb --- /dev/null +++ b/src/utils/path.rs @@ -0,0 +1,21 @@ +use shellexpand::{full, LookupError}; +use std::env::VarError; +use std::fs::File; +use std::io::Write; +use std::os::unix::io::FromRawFd; +use std::path::PathBuf; + +pub fn chdir(path: &str) { + let mut f = unsafe { File::from_raw_fd(9) }; + if let Err(e) = f.write_fmt(format_args!("cd:{}\n", path)) { + panic!(e) + } +} + +const D_PATH: &str = "~/src"; +pub fn get_base_path() -> Result> { + match full(D_PATH) { + Ok(value) => Ok(PathBuf::from(value.to_string())), + Err(e) => Err(e), + } +}