diff --git a/Cargo.lock b/Cargo.lock index 61a51d78..75d6387d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,9 @@ # It is not intended for manual editing. [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] @@ -24,29 +24,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allfmt" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "console", - "curl", - "dialoguer", - "env_logger", - "glob", - "human-panic", - "log", - "parking_lot", - "serde", - "serde_json", - "structopt", - "toml", - "walkdir", - "which", - "xshell", -] - [[package]] name = "ansi_term" version = "0.11.0" @@ -58,9 +35,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.36" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68803225a7b13e47191bab76f2687382b60d259e8cf37f6e1893658b84bb9479" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "atty" @@ -99,6 +76,15 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "cc" version = "1.0.66" @@ -161,6 +147,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "curl" version = "0.4.34" @@ -203,6 +195,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -218,15 +219,37 @@ dependencies = [ "log", ] +[[package]] +name = "filetime" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.1.57", + "winapi", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" -version = "0.1.15" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -243,22 +266,28 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "heck" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "human-panic" version = "1.0.3" @@ -297,9 +326,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libz-sys" @@ -324,9 +353,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" dependencies = [ "cfg-if 0.1.10", ] @@ -372,6 +401,12 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl-probe" version = "0.1.2" @@ -380,9 +415,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.59" +version = "0.9.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe" +checksum = "921fc71883267538946025deffb622905ecad223c28efbfdef9bb59a0175f3e6" dependencies = [ "autocfg", "cc", @@ -413,14 +448,14 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0" +checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.1.57", "smallvec", "winapi", ] @@ -437,6 +472,32 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "prjfmt" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "console", + "curl", + "dialoguer", + "env_logger", + "filetime", + "glob", + "hex", + "human-panic", + "log", + "parking_lot", + "serde", + "serde_json", + "sha-1", + "structopt", + "toml", + "walkdir", + "which", + "xshell", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -481,11 +542,10 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.3" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +checksum = "18519b42a40024d661e1714153e9ad0c3de27cd495760ceb09710920f1098b1e" dependencies = [ - "getrandom", "libc", "rand_chacha", "rand_core", @@ -494,9 +554,9 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", @@ -504,18 +564,18 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ "rand_core", ] @@ -526,11 +586,20 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +[[package]] +name = "redox_syscall" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", @@ -540,9 +609,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remove_dir_all" @@ -592,18 +661,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.118" +version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" dependencies = [ "proc-macro2", "quote", @@ -621,17 +690,30 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpuid-bool", + "digest", + "opaque-debug", +] + [[package]] name = "smallvec" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e0e9fd577458a4f61fb91fcb559ea2afecc54c934119421f9f5d3d5b1a1057" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" dependencies = [ "cfg-if 1.0.0", "libc", @@ -670,9 +752,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.55" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" +checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", @@ -681,14 +763,14 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "rand", - "redox_syscall", + "redox_syscall 0.2.4", "remove_dir_all", "winapi", ] @@ -723,18 +805,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e" +checksum = "76cc616c6abf8c8928e2fdcc0dbfab37175edd8fb49a4641066ad1364fdab146" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56" +checksum = "9be73a2caec27583d0046ef3796c3794f868a5bc813db689eed00c7631275cd1" dependencies = [ "proc-macro2", "quote", @@ -743,21 +825,20 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" dependencies = [ "lazy_static", ] [[package]] name = "time" -version = "0.1.44" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -770,6 +851,12 @@ dependencies = [ "serde", ] +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + [[package]] name = "unicode-segmentation" version = "1.7.1" @@ -790,11 +877,11 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "uuid" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "rand", + "getrandom", ] [[package]] @@ -828,15 +915,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +version = "0.10.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" [[package]] name = "which" diff --git a/Cargo.toml b/Cargo.toml index da3c19c7..530d8e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "allfmt" +name = "prjfmt" version = "0.1.0" authors = ["Andika Demas Riyandi "] edition = "2018" @@ -13,12 +13,15 @@ console = "0.13" curl = "0.4" dialoguer = "0.7" env_logger = { version = "0.8", default-features = false } +filetime = "0.2" glob = "0.3" +hex = "0.4" human-panic = "1.0" log = "0.4" parking_lot = "0.11" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +sha-1 = "0.9.2" structopt = "0.3" toml = "0.5" walkdir = "2.3" diff --git a/devshell.nix b/devshell.nix index cc359e2b..ca9b7aa4 100644 --- a/devshell.nix +++ b/devshell.nix @@ -6,9 +6,9 @@ with pkgs; # # Documentation: https://github.com/numtide/devshell mkDevShell { - name = "allfmt"; + name = "prjfmt"; motd = '' - Welcome to the allfmt development environment. + Welcome to the prjfmt development environment. ''; commands = [ ]; @@ -17,6 +17,8 @@ mkDevShell { export LD_INCLUDE_PATH="$DEVSHELL_DIR/include" export LD_LIBRARY_PATH="$DEVSHELL_DIR/lib" export PKG_CONFIG_PATH="$DEVSHELL_DIR/lib/pkgconfig" + export GO111MODULE=on + unset GOPATH GOROOT ''; }; @@ -24,7 +26,7 @@ mkDevShell { packages = [ # Build tools - allfmt.rust + prjfmt.rust # Code formatters haskellPackages.ormolu @@ -32,8 +34,8 @@ mkDevShell { haskellPackages.ghc nixpkgs-fmt go - # gopls - # gopkgs - # gocode + gopls + gopkgs + gocode ]; } diff --git a/examples/monorepo/haskell/Main.hs b/examples/monorepo/haskell/Main.hs index c2300a56..07a0935e 100644 --- a/examples/monorepo/haskell/Main.hs +++ b/examples/monorepo/haskell/Main.hs @@ -1,5 +1,4 @@ module Main where main :: IO () -main = - putStrLn "Hello, Haskell!" +main = putStrLn "Hello, Riyan!" diff --git a/examples/monorepo/fmt.toml b/examples/monorepo/prjfmt.toml similarity index 100% rename from examples/monorepo/fmt.toml rename to examples/monorepo/prjfmt.toml diff --git a/flake.lock b/flake.lock index 19850d0d..c9f97260 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "devshell": { "locked": { - "lastModified": 1607956014, - "narHash": "sha256-kcmh1ZO56GFYStDeM2VngJSvx/e8aKhFSYqLwfiq+Es=", + "lastModified": 1610294226, + "narHash": "sha256-OTgmGRlziJwuw4hFaDl3Kar1qpwXScvQs2eWnN3jTwI=", "owner": "numtide", "repo": "devshell", - "rev": "17f46732ce299daa2977be2978f60d258c2d1b41", + "rev": "17a8c6b64127a19b5136f9ce4dad17ca2934f4df", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1605370193, - "narHash": "sha256-YyMTf3URDL/otKdKgtoMChu4vfVL3vCMkRqpGifhUn0=", + "lastModified": 1610051610, + "narHash": "sha256-U9rPz/usA1/Aohhk7Cmc2gBrEEKRzcW4nwPWMPwja4Y=", "owner": "numtide", "repo": "flake-utils", - "rev": "5021eac20303a61fafe17224c087f5519baed54d", + "rev": "3982c9903e93927c2164caa727cd3f6a0e6d14cc", "type": "github" }, "original": { @@ -49,11 +49,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1608282524, - "narHash": "sha256-7J8Qkqx4yMkjcvQF4vh8xaaR2PAN2781mYj/3jCdBnw=", + "lastModified": 1611020796, + "narHash": "sha256-JnPVjA4Ylh1NckWy+B3tQuKfSg2Ot/8R92Q/O6MZKbQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d294aea6b8a758d4bf7d796004f4aa1275396dc6", + "rev": "513b2647dc5fe5178d31054b72ce6d6a0cba000c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 088c52c7..f9008110 100644 --- a/flake.nix +++ b/flake.nix @@ -37,9 +37,9 @@ }; in { - legacyPackages = pkgs.allfmt; + legacyPackages = pkgs.prjfmt; - packages = flake-utils.lib.flattenTree pkgs.allfmt; + packages = flake-utils.lib.flattenTree pkgs.prjfmt; devShell = import ./devshell.nix { inherit pkgs; }; diff --git a/overlay.nix b/overlay.nix index 109a14e1..c95df429 100644 --- a/overlay.nix +++ b/overlay.nix @@ -1,6 +1,6 @@ final: prev: { - allfmt = rec { + prjfmt = rec { rust = prev.callPackage ./rust { }; }; } diff --git a/src/command/init.rs b/src/command/init.rs index a1ec7c7f..efe30551 100644 --- a/src/command/init.rs +++ b/src/command/init.rs @@ -4,17 +4,19 @@ use anyhow::Context; use console::style; use log::info; use std::fs; -use std::path::Path; +use std::path::PathBuf; -pub fn init_fmt() -> anyhow::Result<()> { - info!("Creating new allfmt configuration..."); - - let file = &format!("fmt.toml"); - let file_path = Path::new(file); +pub fn init_prjfmt(path: Option) -> anyhow::Result<()> { + info!("Creating new prjfmt configuration..."); + let file = match path { + Some(loc) => loc, + None => PathBuf::from("."), + }; + let file_path = file.as_path().join("prjfmt.toml"); // TODO: detect if file exists fs::write( - file_path, - r#"# fmt is the universal code formatter - https://github.com/numtide/fmt + file_path.as_path(), + r#"# prjfmt is the universal code formatter - https://github.com/numtide/prjfmt [formats.] includes = [ "*." ] excludes = [] @@ -31,7 +33,7 @@ args = [] ) })?; - let msg = format!("Generated allfmt template at ./."); + let msg = format!("Generated prjfmt template at {}", file_path.display()); CLOG.info(&msg); Ok(()) } diff --git a/src/command/mod.rs b/src/command/mod.rs index ec910724..ffa47133 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -3,34 +3,28 @@ mod init; -use self::init::init_fmt; +use self::init::init_prjfmt; +use std::path::PathBuf; use log::info; -#[derive(Debug)] -struct Initialize {} - #[derive(Debug, StructOpt)] -/// The various kinds of commands that `allfmt` can execute. +/// The various kinds of commands that `prjfmt` can execute. pub enum Command { #[structopt(name = "--init")] /// init a new project with a default config - Init, - #[structopt(name = "--dry-run")] - /// creating formating plan - Dry, + Init { + /// path to file or folder + path: Option, + }, } /// Run a command with the given logger! -pub fn run_all_fmt(command: Command) -> anyhow::Result<()> { +pub fn run_prjfmt_cli(command: Command) -> anyhow::Result<()> { match command { - Command::Init => { - info!("creating fmt.toml"); - init_fmt() - } - Command::Dry => { - println!("running dry-run"); - Ok(()) + Command::Init { path } => { + info!("creating prjfmt.toml"); + init_prjfmt(path) } } } diff --git a/src/customlog.rs b/src/customlog.rs index 297e81f6..e8522fab 100644 --- a/src/customlog.rs +++ b/src/customlog.rs @@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; #[repr(u8)] #[derive(Debug, Clone, Copy)] -/// The log level for allfmt +/// The log level for prjfmt // Important! the least verbose must be at // the top and the most verbose at the bottom pub enum LogLevel { @@ -66,7 +66,7 @@ impl CustomLogOutput { (level as u8) <= self.log_level.load(Ordering::SeqCst) } - /// Sets the log level for allfmt + /// Sets the log level for prjfmt pub fn set_log_level(&self, log_level: LogLevel) { self.log_level.store(log_level as u8, Ordering::SeqCst); } @@ -101,7 +101,7 @@ impl CustomLogOutput { pub fn error(&self, message: &str) { if self.is_log_enabled(LogLevel::Error) { let err = format!( - "{}{}: {}", + "{} {}: {}", emoji::ERROR, style("[ERR]").bold().dim(), message diff --git a/src/emoji.rs b/src/emoji.rs index 94abc687..55a033f5 100644 --- a/src/emoji.rs +++ b/src/emoji.rs @@ -1,10 +1,10 @@ -//! Emoji contants used by `allfmt`. +//! Emoji contants used by `prjfmt`. #![allow(missing_docs)] use console::Emoji; -pub static FOLDER: Emoji = Emoji("📂 ", ""); -pub static WARN: Emoji = Emoji("⚠ī¸ ", ":-)"); -pub static ERROR: Emoji = Emoji("⛔ ", ""); -pub static INFO: Emoji = Emoji("ℹī¸ ", ""); +pub static FOLDER: Emoji = Emoji("📂", ""); +pub static WARN: Emoji = Emoji("⚠ī¸", ":-)"); +pub static ERROR: Emoji = Emoji("⛔", ""); +pub static INFO: Emoji = Emoji("ℹī¸", ""); diff --git a/src/formatters/check.rs b/src/formatters/check.rs new file mode 100644 index 00000000..32cf7f26 --- /dev/null +++ b/src/formatters/check.rs @@ -0,0 +1,49 @@ +use super::tool::FileMeta; +use crate::formatters::manifest::RootManifest; +use crate::formatters::tool::{create_command_context, CmdContext}; +use crate::CLOG; +use anyhow::{anyhow, Error, Result}; +use std::collections::BTreeSet; +use std::path::PathBuf; +use std::vec::Vec; + +/// Checking content of cache's file and current prjfmt runs +pub fn check_prjfmt( + prjfmt_toml: &PathBuf, + cmd_context: &Vec, + cache: &RootManifest, +) -> Result> { + let cache_context = cache.manifest.values().map(|b| b).into_iter(); + let results = cmd_context.into_iter().zip(cache_context); + + let cache_context: Vec = results + .clone() + .map(|(a, b)| { + Ok(CmdContext { + command: a.command.clone(), + args: a.args.clone(), + metadata: a.metadata.difference(&b.metadata).cloned().collect(), + }) + }) + .collect::, Error>>()?; + + if cache_context.iter().all(|f| f.metadata.is_empty()) { + CLOG.warn(&format!("No changes found in {}", prjfmt_toml.display())); + return Ok(Vec::new()); + } + + CLOG.warn(&format!("The following file has changed or newly added:")); + for cmd in &cache_context { + if !cmd.metadata.is_empty() { + for p in &cmd.metadata { + CLOG.warn(&format!( + " - {} last modification time: {}", + p.path.display(), + p.mtimes + )); + } + } + } + // return Err(anyhow!("prjfmt failed to run.")); + Ok(cache_context) +} diff --git a/src/formatters/manifest.rs b/src/formatters/manifest.rs new file mode 100644 index 00000000..8896f157 --- /dev/null +++ b/src/formatters/manifest.rs @@ -0,0 +1,95 @@ +use crate::{emoji, CLOG}; + +use super::tool::CmdContext; +use anyhow::{anyhow, Result}; +use hex; +use serde::{Deserialize, Serialize}; +use sha1::{Digest, Sha1}; +use std::collections::BTreeMap; +use std::fs::{read_to_string, File}; +use std::io::Write; +use std::path::PathBuf; +use std::str; +use xshell::cmd; + +/// Create .toml and put it in $XDG_CACHE_DIR/prjfmt/eval-cache/ +pub fn create_prjfmt_manifest( + prjfmt_toml: PathBuf, + cache_dir: PathBuf, + cmd_ctx: Vec, +) -> Result<()> { + let hash_toml = create_prjfmt_hash(&prjfmt_toml)?; + + let mut f = File::create(cache_dir.as_path().join(hash_toml))?; + let map_manifest: BTreeMap = cmd_ctx + .into_iter() + .map(|cmd| { + let prjfmt = cmd.command; + let manifest = CmdContext { + command: prjfmt.to_string(), + args: cmd.args, + metadata: cmd.metadata, + }; + (prjfmt.to_string(), manifest) + }) + .collect(); + let manifest_toml = RootManifest { + manifest: map_manifest, + }; + f.write_all( + format!( + "# {} DO NOT HAND EDIT THIS FILE {}\n\n{}", + emoji::WARN, + emoji::WARN, + toml::to_string_pretty(&manifest_toml)? + ) + .as_bytes(), + )?; + Ok(()) +} + +/// Read the .toml and return list of config's cache evaluation +pub fn read_prjfmt_manifest(prjfmt_toml: &PathBuf, path: &PathBuf) -> Result { + let hash_toml = create_prjfmt_hash(&prjfmt_toml)?; + let manifest_toml = path.as_path().join(&hash_toml); + + if manifest_toml.as_path().exists() { + CLOG.info(&format!("Found {} in: {}", hash_toml, path.display())); + let open_file = match read_to_string(manifest_toml.as_path()) { + Ok(file) => file, + Err(err) => { + return Err(anyhow!( + "cannot open {} due to {}.", + manifest_toml.display(), + err + )) + } + }; + + let manifest_content: RootManifest = toml::from_str(&open_file)?; + Ok(manifest_content) + } else { + CLOG.warn(&format!("{} not found!", hash_toml)); + Ok(RootManifest { + manifest: BTreeMap::new(), + }) + } +} + +fn create_prjfmt_hash(prjfmt_toml: &PathBuf) -> Result { + let prjfmt_str = match prjfmt_toml.to_str() { + Some(str) => str.as_bytes(), + None => return Err(anyhow!("{}cannot convert to string slice", emoji::ERROR)), + }; + let prjfmt_hash = Sha1::digest(prjfmt_str); + let result = hex::encode(prjfmt_hash); + let manifest_toml = format!("{}.toml", result); + Ok(manifest_toml) +} + +#[derive(Debug, Deserialize, Serialize)] +/// RootManifest +pub struct RootManifest { + /// Map of manifests config based on its formatter + pub manifest: BTreeMap, +} diff --git a/src/formatters/mod.rs b/src/formatters/mod.rs index 3d99ea51..575e37c2 100644 --- a/src/formatters/mod.rs +++ b/src/formatters/mod.rs @@ -1,5 +1,9 @@ //! Functionality related to installing prebuilt binaries #![deny(missing_docs)] +/// File checking utility +pub mod check; +/// Manifest configuration +pub mod manifest; /// Formatter utility pub mod tool; diff --git a/src/formatters/tool.rs b/src/formatters/tool.rs index 0c4eb3a3..93513ea0 100644 --- a/src/formatters/tool.rs +++ b/src/formatters/tool.rs @@ -1,27 +1,23 @@ -use crate::emoji; -use console::style; - -// use super::tools; -use anyhow::{anyhow, Result}; -use glob::{glob, Paths}; -use serde::Deserialize; -use std::collections::BTreeMap; +use super::manifest::create_prjfmt_manifest; +use crate::formatters::check::check_prjfmt; +use crate::formatters::manifest::{read_prjfmt_manifest, RootManifest}; +use crate::{emoji, CLOG}; +use anyhow::{anyhow, Error, Result}; +use filetime::FileTime; +use glob; +use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs::{metadata, read_to_string}; use std::iter::Iterator; use std::path::PathBuf; -use std::vec::IntoIter; -// use tools::{ -// go::{deserialize_go, Gfmt}, -// haskell::{deserialize_haskell, Hfmt}, -// rust::{deserialize_rust, Rfmt}, -// }; use xshell::cmd; -// TODO: This module provides functions to install all formatter -// that is not available in user's $PATH -/// Make sure that rustfmt exists. This also for other formatter -pub fn check_fmt(command: String) -> Result<()> { +/// Make sure that formatter binary exists. This also for other formatter +pub fn check_bin(command: &str) -> Result<()> { let cmd_bin = command.split_ascii_whitespace().next().unwrap_or(""); if let Ok(str) = cmd!("which {cmd_bin}").read() { + CLOG.info(&format!("Found {} at {}", cmd_bin, str)); return Ok(()); } anyhow::bail!( @@ -31,70 +27,188 @@ pub fn check_fmt(command: String) -> Result<()> { ) } -/// Running the fmt -pub fn run_fmt(cmd_arg: &str, args: &Vec, path: PathBuf) -> anyhow::Result<()> { - cmd!("{cmd_arg} {args...} {path}").run()?; +/// Run the prjfmt +pub fn run_prjfmt(cwd: PathBuf, cache_dir: PathBuf) -> anyhow::Result<()> { + let prjfmt_toml = cwd.as_path().join("prjfmt.toml"); + + // Once the prjfmt found the $XDG_CACHE_DIR/prjfmt/eval-cache/ folder, + // it will try to scan the manifest and passed it into check_prjfmt function + let manifest: RootManifest = read_prjfmt_manifest(&prjfmt_toml, &cache_dir)?; + let old_ctx = create_command_context(&prjfmt_toml)?; + let ctxs = check_prjfmt(&prjfmt_toml, &old_ctx, &manifest)?; + + let context = if manifest.manifest.is_empty() && ctxs.is_empty() { + &old_ctx + } else { + &ctxs + }; + + if !prjfmt_toml.as_path().exists() { + return Err(anyhow!( + "{}prjfmt.toml not found, please run --init command", + emoji::ERROR + )); + } + + for c in context { + check_bin(&c.command)?; + } + + println!("==========================="); + for c in context { + if !c.metadata.is_empty() { + println!("Command: {}", c.command); + println!("Files:"); + for m in &c.metadata { + let path = &m.path; + println!(" - {}", path.display()); + } + println!("==========================="); + } + } + + for c in context { + for m in &c.metadata { + let arg = &c.args; + let cmd_arg = &c.command; + let path = &m.path; + cmd!("{cmd_arg} {arg...} {path}").read()?; + } + } + + let new_ctx: Vec = old_ctx + .iter() + .flat_map(|octx| { + ctxs.iter().clone().map(move |c| { + if c.command == octx.command { + CmdContext { + command: c.command.clone(), + args: c.args.clone(), + metadata: octx.metadata.union(&c.metadata).cloned().collect(), + } + } else { + octx.clone() + } + }) + }) + .collect(); + + if manifest.manifest.is_empty() || ctxs.is_empty() { + create_prjfmt_manifest(prjfmt_toml, cache_dir, old_ctx)?; + } else { + println!("Format successful"); + println!("capturing formatted file's state..."); + create_prjfmt_manifest(prjfmt_toml, cache_dir, new_ctx)?; + } + Ok(()) } /// Convert glob pattern into list of pathBuf -pub fn glob_to_path(cwd: PathBuf, extensions: FileExtensions) -> Result { +pub fn glob_to_path( + cwd: &PathBuf, + extensions: &FileExtensions, + _includes: &Option>, + _excludes: &Option>, +) -> anyhow::Result> { + let dir = cwd.to_str().unwrap_or(""); + + let glob_ext = |extension| -> anyhow::Result<_> { + let pat = format!("{}/**/{}", dir, extension); + let globs = glob::glob(&pat).map_err(|err| { + anyhow::anyhow!( + "{} Error at position: {} due to {}", + emoji::ERROR, + err.pos, + err.msg + ) + })?; + + Ok(globs.map(|glob_res| Ok(glob_res?))) + }; + match extensions { - FileExtensions::SingleFile(sfile) => { - let dir = cwd.as_path().to_str().unwrap_or(""); - let pat = format!("{}**/{}", dir, &sfile); - match glob(&pat) { - Ok(paths) => Ok(paths), - Err(err) => { - anyhow::bail!( - "{} Error at position: {} due to {}", - emoji::ERROR, - err.pos, - err.msg - ) - } - } - } + FileExtensions::SingleFile(sfile) => glob_ext(sfile)?.collect(), FileExtensions::MultipleFile(strs) => { - let files = strs - .into_iter() - .map(|str| { - let dir = cwd.as_path().to_str().unwrap_or(""); - let pat = format!("{}**/{}", dir, &str); - match glob(&pat) { - Ok(paths) => Ok(paths), - Err(err) => { - anyhow::bail!( - "{} Error at position: {} due to {}", - emoji::ERROR, - err.pos, - err.msg - ) - } + strs.iter() + .map(glob_ext) + .try_fold(Vec::new(), |mut v, globs| { + for glob in globs? { + v.push(glob?) } + Ok(v) }) - .flatten() - .nth(0); - match files { - Some(paths) => Ok(paths), - None => { - anyhow::bail!("{} Blob not found", emoji::ERROR) - } - } } } } -/// Convert glob's Paths into list of PathBuf -pub fn paths_to_pathbuf( - inc: Vec, - excl: Vec, - paths_list: Vec, -) -> Vec { - Vec::new() +/// Convert each PathBuf into FileMeta +/// FileMeta consist of file's path and its modification times +pub fn path_to_filemeta(paths: Vec) -> Result> { + let mut filemeta = BTreeSet::new(); + for p in paths { + let metadata = metadata(&p)?; + let mtime = FileTime::from_last_modification_time(&metadata).unix_seconds(); + if !filemeta.insert(FileMeta { + mtimes: mtime, + path: p.clone(), + }) { + CLOG.warn(&format!("Duplicated file detected:")); + CLOG.warn(&format!(" - {:?} ", p.display())); + CLOG.warn(&format!( + "Maybe you want to format one file with different formatter?" + )); + // return Err(anyhow!("prjfmt failed to run.")); + } + } + Ok(filemeta) } -/// fmt.toml structure +/// Creating command configuration based on prjfmt.toml +pub fn create_command_context(prjfmt_toml: &PathBuf) -> Result> { + let open_prjfmt = match read_to_string(prjfmt_toml.as_path()) { + Ok(file) => file, + Err(err) => { + return Err(anyhow!( + "cannot open {} due to {}.", + prjfmt_toml.display(), + err + )) + } + }; + + let cwd = match prjfmt_toml.parent() { + Some(path) => path, + None => { + return Err(anyhow!( + "{}prjfmt.toml not found, please run --init command", + emoji::ERROR + )) + } + }; + + let toml_content: Root = toml::from_str(&open_prjfmt)?; + let cmd_context: Vec = toml_content + .formatters + .values() + .map(|config| { + let list_files = glob_to_path( + &cwd.to_path_buf(), + &config.files, + &config.includes, + &config.excludes, + )?; + Ok(CmdContext { + command: config.command.clone().unwrap_or_default(), + args: config.args.clone().unwrap_or_default(), + metadata: path_to_filemeta(list_files)?, + }) + }) + .collect::, Error>>()?; + Ok(cmd_context) +} + +/// prjfmt.toml structure #[derive(Debug, Deserialize)] pub struct Root { /// Map of formatters into the config @@ -126,3 +240,49 @@ pub enum FileExtensions { /// List of file type MultipleFile(Vec), } + +#[derive(Debug, Deserialize, Serialize, Clone)] +/// Each context of the formatter config +pub struct CmdContext { + /// formatter command to run + pub command: String, + /// formatter arguments or flags + pub args: Vec, + /// formatter target path + pub metadata: BTreeSet, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +/// File metadata created after the first prjfmt run +pub struct FileMeta { + /// Last modification time listed in the file's metadata + pub mtimes: i64, + /// Path to the formatted file + pub path: PathBuf, +} + +impl Ord for FileMeta { + fn cmp(&self, other: &Self) -> Ordering { + if self.eq(other) { + return Ordering::Equal; + } + if self.mtimes.eq(&other.mtimes) { + return self.path.cmp(&other.path); + } + self.mtimes.cmp(&other.mtimes) + } +} + +impl PartialOrd for FileMeta { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for FileMeta { + fn eq(&self, other: &Self) -> bool { + self.mtimes == other.mtimes && self.path == other.path + } +} + +impl Eq for FileMeta {} diff --git a/src/lib.rs b/src/lib.rs index e5c0db6d..c33ea1ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,10 @@ extern crate structopt; extern crate chrono; extern crate curl; extern crate dialoguer; +extern crate hex; extern crate log; extern crate serde; +extern crate sha1; extern crate toml; extern crate walkdir; @@ -22,14 +24,16 @@ pub mod customlog; pub mod emoji; pub mod formatters; -use command::run_all_fmt; -use formatters::tool::{check_fmt, glob_to_path, run_fmt, Root}; -use glob::Paths; -use std::fs::read_to_string; -use std::path::PathBuf; -use std::vec; +use anyhow::anyhow; +use command::run_prjfmt_cli; +use formatters::check::check_prjfmt; +use formatters::manifest::{read_prjfmt_manifest, RootManifest}; +use formatters::tool::{run_prjfmt, CmdContext}; +use std::env; +use std::path::{Path, PathBuf}; use customlog::{CustomLogOutput, LogLevel}; +use xshell::cmd; /// The global custom log and user-facing message output. pub static CLOG: CustomLogOutput = CustomLogOutput::new(); @@ -50,7 +54,7 @@ pub struct Cli { pub quiet: bool, #[structopt(long = "log-level", default_value = "info")] - /// The maximum level of messages that should be logged by allfmt. [possible values: info, warn, error] + /// The maximum level of messages that should be logged by prjfmt. [possible values: info, warn, error] pub log_level: LogLevel, /// Files to format @@ -60,79 +64,39 @@ pub struct Cli { /// Run a command with the given logger! pub fn run_cli(cli: Cli) -> anyhow::Result<()> { if let Some(command) = cli.cmd { - run_all_fmt(command)?; + run_prjfmt_cli(command)?; return Ok(()); } let cwd = match cli.files { Some(path) => path, - None => PathBuf::from("."), + None => env::current_dir()?, }; - let fmt_toml = cwd.as_path().join("fmt.toml"); - - if let true = fmt_toml.as_path().exists() { - println!("found fmt.toml"); - println!("set path to {}", cwd.to_str().unwrap_or("")); - let open_file = match read_to_string(fmt_toml.as_path()) { - Ok(file) => file, - Err(err) => return Err(anyhow::Error::new(err)), - }; - let toml_content: Root = toml::from_str(&open_file)?; - - let commands = toml_content - .formatters - .values() - .map(|c| c.command.clone().unwrap_or("".into())); - - let args = toml_content.formatters.values().map(|c| { - let arg = match c.args.clone() { - Some(vstr) => vstr, - None => Vec::new(), - }; - arg - }); - - let all_files = toml_content - .formatters - .values() - .map(|f| f.files.clone()) - .filter_map(|glob| glob_to_path(cwd.clone(), glob).ok()); - - for cmd in commands.clone() { - check_fmt(cmd)?; + let prjfmt_toml = cwd.as_path().join("prjfmt.toml"); + let xdg_cache_dir = match env::var("XDG_CACHE_DIR") { + Ok(path) => path, + Err(err) => { + CLOG.warn(&format!("{}", err)); + CLOG.warn(&format!("Set the $XDG_CACHE_DIR to {}", cwd.display())); + cwd.as_path().display().to_string() } + }; - let cmd_args = commands.zip(args).zip(all_files); - - // TODO: implement `filtered_files: Vec` for `[includes]` and `[exclude]` - println!("==========================="); - for ((cmd, args), paths) in cmd_args.clone() { - println!("Command: {}", cmd); - println!("Files:"); - for f in paths { - println!(" - {}", f?.display()); - } - println!("==========================="); - } - - for ((cmd, args), paths) in cmd_args.clone() { - for f in paths { - let path = f?; - run_fmt(&cmd.clone(), &args, path)? - } - } - - println!("Format successful"); - - // .and_then(|config| config.includes).unwrap_or(Vec::new()); - // for dir in rustdir { - // cwd.push(Path::new(&dir)); - // run_rustfmt(Mode::Overwrite, cwd.clone())?; - // } + if prjfmt_toml.as_path().exists() { + CLOG.info(&format!( + "Found {} at {}", + prjfmt_toml.display(), + cwd.display() + )); + CLOG.info(&format!("Change current directory into: {}", cwd.display())); + let cache_dir = Path::new(&xdg_cache_dir).join("prjfmt/eval-cache"); + cmd!("mkdir -p {cache_dir}").read()?; + run_prjfmt(cwd, cache_dir)?; } else { - println!("file fmt.toml couldn't be found. Run `--init` to generate the default setting"); + println!( + "file prjfmt.toml couldn't be found. Run `--init` to generate the default setting" + ); } - Ok(()) } diff --git a/src/main.rs b/src/main.rs index a7689ed6..c3953e62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ #![allow(clippy::redundant_closure, clippy::redundant_pattern_matching)] -extern crate env_logger; - -extern crate allfmt; extern crate anyhow; +extern crate env_logger; extern crate human_panic; extern crate log; +extern crate prjfmt; extern crate structopt; extern crate which; -use allfmt::{run_cli, Cli, CLOG}; +use prjfmt::{run_cli, Cli, CLOG}; use structopt::StructOpt; fn main() {