tokei-12.1.2/.cargo_vcs_info.json0000644000000001121377740570100122750ustar { "git": { "sha1": "e2014727c3c1ae98937a4f427613273404e0be21" } } tokei-12.1.2/Cargo.lock0000644000001036201377740570100102600ustar # 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 = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" dependencies = [ "nodrop", ] [[package]] name = "arrayvec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[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 = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blake2b_simd" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" dependencies = [ "arrayref", "arrayvec 0.5.2", "constant_time_eq", ] [[package]] name = "block-buffer" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" dependencies = [ "block-padding", "byte-tools", "byteorder", "generic-array", ] [[package]] name = "block-padding" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" dependencies = [ "byte-tools", ] [[package]] name = "bstr" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "473fc6b38233f9af7baa94fb5852dca389e3d95b8e21c8e3719301462c5d9faf" dependencies = [ "memchr", ] [[package]] name = "byte-tools" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecount" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" [[package]] name = "byteorder" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "cc" version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" dependencies = [ "jobserver", ] [[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 = "chrono" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ "libc", "num-integer", "num-traits", "time", "winapi", ] [[package]] name = "chrono-tz" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2554a3155fec064362507487171dcc4edc3df60cb10f3a1fb10ed8094822b120" dependencies = [ "chrono", "parse-zoneinfo", ] [[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[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 = "dashmap" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" dependencies = [ "cfg-if 1.0.0", "num_cpus", "serde", ] [[package]] name = "deunicode" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" [[package]] name = "digest" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ "generic-array", ] [[package]] name = "dirs" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ "libc", "redox_users", "winapi", ] [[package]] name = "dtoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encoding_rs" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "encoding_rs_io" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" dependencies = [ "encoding_rs", ] [[package]] name = "env_logger" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", "humantime", "log", "regex", "termcolor", ] [[package]] name = "fake-simd" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" dependencies = [ "matches", "percent-encoding", ] [[package]] name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" dependencies = [ "typenum", ] [[package]] name = "getrandom" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4060f4657be78b8e766215b02b18a2e862d83745545de804638e2b545e81aee6" dependencies = [ "cfg-if 1.0.0", "libc", "wasi 0.10.1+wasi-snapshot-preview1", ] [[package]] name = "git2" version = "0.13.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44f267c9da8a4de3c615b59e23606c75f164f84896e97f4dd6c15a4294de4359" dependencies = [ "bitflags", "libc", "libgit2-sys", "log", "url", ] [[package]] name = "globset" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a" dependencies = [ "aho-corasick", "bstr", "fnv", "log", "regex", ] [[package]] name = "globwalk" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" dependencies = [ "bitflags", "ignore", "walkdir", ] [[package]] name = "grep-matcher" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf0e1fd5af17008a918fd868e63ec0226e96ce88b832f00c7fb041e014b9350" dependencies = [ "memchr", ] [[package]] name = "grep-searcher" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19897320890970db77fb9e8110aeafdf5a74f4ee32756db16d44a96d8f454b1b" dependencies = [ "bstr", "bytecount", "encoding_rs", "encoding_rs_io", "grep-matcher", "log", "memmap", ] [[package]] name = "half" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" [[package]] name = "hermit-abi" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "libc", ] [[package]] name = "hex" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" [[package]] name = "humansize" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" [[package]] name = "humantime" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" [[package]] name = "idna" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" dependencies = [ "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "ignore" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c" dependencies = [ "crossbeam-utils", "globset", "lazy_static", "log", "memchr", "regex", "same-file", "thread_local", "walkdir", "winapi-util", ] [[package]] name = "instant" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ "cfg-if 1.0.0", ] [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "jobserver" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" 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.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "libgit2-sys" version = "0.12.17+1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4ebdf65ca745126df8824688637aa0535a88900b83362d8ca63893bcf4e8841" dependencies = [ "cc", "libc", "libz-sys", "pkg-config", ] [[package]] name = "libz-sys" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "linked-hash-map" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" dependencies = [ "cfg-if 0.1.10", ] [[package]] name = "maplit" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matches" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ "libc", "winapi", ] [[package]] name = "memoffset" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" dependencies = [ "autocfg", ] [[package]] name = "nodrop" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-format" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" dependencies = [ "arrayvec 0.4.12", "itoa", ] [[package]] name = "num-integer" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 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 = "once_cell" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "parking_lot" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ccb628cad4f84851442432c60ad8e1f607e29752d0bf072cbd0baf28aa34272" dependencies = [ "cfg-if 1.0.0", "instant", "libc", "redox_syscall 0.1.57", "smallvec", "winapi", ] [[package]] name = "parse-zoneinfo" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" dependencies = [ "regex", ] [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ "ucd-trie", ] [[package]] name = "pest_derive" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" dependencies = [ "pest", "pest_generator", ] [[package]] name = "pest_generator" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", "syn", ] [[package]] name = "pest_meta" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", "sha-1", ] [[package]] name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "ppv-lite86" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[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 = "rand" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c24fcd450d3fa2b592732565aa4f17a27a61c65ece4726353e000939b0edee34" dependencies = [ "libc", "rand_chacha", "rand_core", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c026d7df8b298d90ccbbc5190bd04d85e159eaf5576caeacf8741da93ccbd2e5" dependencies = [ "getrandom 0.2.1", ] [[package]] name = "rand_hc" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" dependencies = [ "rand_core", ] [[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_syscall" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" dependencies = [ "bitflags", ] [[package]] name = "redox_users" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ "getrandom 0.1.16", "redox_syscall 0.1.57", "rust-argon2", ] [[package]] name = "regex" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", "regex-syntax", "thread_local", ] [[package]] name = "regex-syntax" version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "rust-argon2" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" dependencies = [ "base64", "blake2b_simd", "constant_time_eq", "crossbeam-utils", ] [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_yaml" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "971be8f6e4d4a47163b405a3df70d14359186f9ab0f3a3ec37df144ca1ce089f" dependencies = [ "dtoa", "linked-hash-map", "serde", "yaml-rust", ] [[package]] name = "sha-1" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ "block-buffer", "digest", "fake-simd", "opaque-debug", ] [[package]] name = "slug" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" dependencies = [ "deunicode", ] [[package]] name = "smallvec" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] [[package]] name = "tempfile" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if 1.0.0", "libc", "rand", "redox_syscall 0.2.4", "remove_dir_all", "winapi", ] [[package]] name = "tera" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac6ab7eacf40937241959d540670f06209c38ceadb62116999db4a950fbf8dc" dependencies = [ "chrono", "chrono-tz", "globwalk", "humansize", "lazy_static", "percent-encoding", "pest", "pest_derive", "rand", "regex", "serde", "serde_json", "slug", "unic-segment", ] [[package]] name = "term_size" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" dependencies = [ "libc", "winapi", ] [[package]] name = "termcolor" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" dependencies = [ "winapi-util", ] [[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.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb9bc092d0d51e76b2b19d9d85534ffc9ec2db959a2523cdae0697e2972cd447" dependencies = [ "lazy_static", ] [[package]] name = "time" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", "winapi", ] [[package]] name = "tinyvec" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokei" version = "12.1.2" dependencies = [ "aho-corasick", "clap", "crossbeam-channel", "dashmap", "dirs", "encoding_rs_io", "env_logger", "git2", "grep-searcher", "hex", "ignore", "log", "num-format", "once_cell", "parking_lot", "rayon", "regex", "serde", "serde_cbor", "serde_json", "serde_yaml", "tempfile", "tera", "term_size", "toml", ] [[package]] name = "toml" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ "serde", ] [[package]] name = "typenum" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "ucd-trie" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unic-char-property" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" dependencies = [ "unic-char-range", ] [[package]] name = "unic-char-range" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" [[package]] name = "unic-common" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" [[package]] name = "unic-segment" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" dependencies = [ "unic-ucd-segment", ] [[package]] name = "unic-ucd-segment" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" dependencies = [ "unic-char-property", "unic-char-range", "unic-ucd-version", ] [[package]] name = "unic-ucd-version" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" dependencies = [ "unic-common", ] [[package]] name = "unicode-bidi" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" dependencies = [ "matches", ] [[package]] name = "unicode-normalization" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" dependencies = [ "tinyvec", ] [[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 = "url" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" dependencies = [ "form_urlencoded", "idna", "matches", "percent-encoding", ] [[package]] name = "vcpkg" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", "winapi", "winapi-util", ] [[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.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93c6c3420963c5c64bca373b25e77acb562081b9bb4dd5bb864187742186cea9" [[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-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "yaml-rust" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] tokei-12.1.2/Cargo.toml0000644000000050701377740570100103030ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "tokei" version = "12.1.2" authors = ["Erin Power "] build = "build.rs" include = ["Cargo.lock", "Cargo.toml", "LICENCE-APACHE", "LICENCE-MIT", "build.rs", "languages.json", "src/**/*"] description = "Count your code, quickly." homepage = "https://tokei.rs" readme = "README.md" keywords = ["utility", "cli", "cloc", "lines", "statistics"] categories = ["command-line-utilities", "development-tools", "visualization"] license = "MIT/Apache-2.0" repository = "https://github.com/XAMPPRocky/tokei.git" [profile.release] lto = "thin" panic = "abort" [dependencies.aho-corasick] version = "0.7.15" [dependencies.clap] version = "2.33.3" [dependencies.crossbeam-channel] version = "0.5.0" [dependencies.dashmap] version = "4.0.1" features = ["serde"] [dependencies.dirs] version = "3.0.1" [dependencies.encoding_rs_io] version = "0.1.7" [dependencies.env_logger] version = "0.8.2" features = [] [dependencies.grep-searcher] version = "0.1.7" [dependencies.hex] version = "0.4.2" optional = true [dependencies.ignore] version = "0.4.17" [dependencies.log] version = "0.4.11" [dependencies.num-format] version = "0.4.0" [dependencies.once_cell] version = "1.5.2" [dependencies.parking_lot] version = "0.11.1" [dependencies.rayon] version = "1.5.0" [dependencies.regex] version = "1.4.3" [dependencies.serde] version = "1.0.118" features = ["derive", "rc"] [dependencies.serde_cbor] version = "0.11.1" optional = true [dependencies.serde_json] version = "1.0.61" [dependencies.serde_yaml] version = "0.8.15" optional = true [dependencies.term_size] version = "0.3.2" [dependencies.toml] version = "0.5.8" [dev-dependencies.git2] version = "0.13.15" features = [] default-features = false [dev-dependencies.regex] version = "1.4.3" [dev-dependencies.tempfile] version = "3.0.8" [build-dependencies.ignore] version = "0.4.17" [build-dependencies.serde_json] version = "1.0.61" [build-dependencies.tera] version = "1.6.1" [features] all = ["cbor", "yaml"] cbor = ["hex", "serde_cbor"] default = [] yaml = ["serde_yaml"] tokei-12.1.2/Cargo.toml.orig010064400007650000024000000030711377740533300137730ustar 00000000000000[package] authors = ["Erin Power "] build = "build.rs" categories = ["command-line-utilities", "development-tools", "visualization"] description = "Count your code, quickly." edition = "2018" homepage = "https://tokei.rs" include = ["Cargo.lock", "Cargo.toml", "LICENCE-APACHE", "LICENCE-MIT", "build.rs", "languages.json", "src/**/*"] keywords = ["utility", "cli", "cloc", "lines", "statistics"] license = "MIT/Apache-2.0" name = "tokei" readme = "README.md" repository = "https://github.com/XAMPPRocky/tokei.git" version = "12.1.2" [features] all = ["cbor", "yaml"] cbor = ["hex", "serde_cbor"] default = [] yaml = ["serde_yaml"] [profile.release] lto = "thin" panic = "abort" [build-dependencies] tera = "1.6.1" ignore = "0.4.17" serde_json = "1.0.61" [dependencies] aho-corasick = "0.7.15" clap = "2.33.3" crossbeam-channel = "0.5.0" dirs = "3.0.1" encoding_rs_io = "0.1.7" grep-searcher = "0.1.7" ignore = "0.4.17" log = "0.4.11" rayon = "1.5.0" serde = { version = "1.0.118", features = ["derive", "rc"] } term_size = "0.3.2" toml = "0.5.8" parking_lot = "0.11.1" dashmap = { version = "4.0.1", features = ["serde"] } num-format = "0.4.0" once_cell = "1.5.2" regex = "1.4.3" serde_json = "1.0.61" [dependencies.env_logger] features = [] version = "0.8.2" [dependencies.hex] optional = true version = "0.4.2" [dependencies.serde_cbor] optional = true version = "0.11.1" [dependencies.serde_yaml] optional = true version = "0.8.15" [dev-dependencies] regex = "1.4.3" tempfile = "3.0.8" git2 = { version = "0.13.15", default-features = false, features = [] } tokei-12.1.2/LICENCE-APACHE010064400007650000024000000010471372513374000130020ustar 00000000000000Copyright 2016 Erin Power Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tokei-12.1.2/LICENCE-MIT010064400007650000024000000020611372513374000125070ustar 00000000000000MIT License (MIT) Copyright (c) 2016 Erin Power 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. tokei-12.1.2/build.rs010064400007650000024000000104531367373101600125460ustar 00000000000000extern crate ignore; extern crate serde_json; use std::ffi::OsStr; use std::fs::{self, File}; use std::path::Path; use std::{cmp, env, error}; use ignore::Walk; use serde_json::Value; fn main() -> Result<(), Box> { let out_dir = env::var_os("OUT_DIR").expect("No OUT_DIR variable."); generate_languages(&out_dir)?; generate_tests(&out_dir)?; Ok(()) } fn generate_languages(out_dir: &OsStr) -> Result<(), Box> { let mut tera = tera::Tera::default(); let mut json: Value = serde_json::from_reader(File::open(&"languages.json")?)?; for (_key, ref mut item) in json .get_mut("languages") .unwrap() .as_object_mut() .unwrap() .iter_mut() { macro_rules! sort_prop { ($prop:expr) => {{ if let Some(ref mut prop) = item.get_mut($prop) { prop.as_array_mut() .unwrap() .sort_unstable_by(compare_json_str_len) } }}; } sort_prop!("quotes"); sort_prop!("verbatim_quotes"); sort_prop!("multi_line"); } let output_path = Path::new(&out_dir).join("language_type.rs"); let rust_code = tera.render_str( &std::fs::read_to_string("src/language/language_type.tera.rs")?, &tera::Context::from_value(json)?, )?; std::fs::write(output_path, rust_code)?; Ok(()) } fn compare_json_str_len(a: &Value, b: &Value) -> cmp::Ordering { let a = a.as_array().expect("a as array"); let b = b.as_array().expect("b as array"); let max_a_size = a.iter().map(|e| e.as_str().unwrap().len()).max().unwrap(); let max_b_size = b.iter().map(|e| e.as_str().unwrap().len()).max().unwrap(); max_b_size.cmp(&max_a_size) } fn generate_tests(out_dir: &OsStr) -> Result<(), Box> { // Length of string literal below by number of languages const INITIAL_BUFFER_SIZE: usize = 989 * 130; let mut string = String::with_capacity(INITIAL_BUFFER_SIZE); let walker = Walk::new("./tests/data/").filter(|p| match p { Ok(ref p) => { if let Ok(ref p) = p.metadata() { p.is_file() } else { false } } _ => false, }); for path in walker { let path = path?; let path = path.path(); let root = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let name = path.file_stem().unwrap().to_str().unwrap().to_lowercase(); if name == "jupyter" { continue; } string.push_str(&format!( r####" #[test] fn {0}() {{ const _: &str = include_str!(r###"{2}"###); let mut languages = Languages::new(); languages.get_statistics(&["{1}"], &[], &Config::default()); if languages.len() != 1 {{ panic!("wrong languages detected: expected just {0}, found {{:?}}", languages.into_iter().collect::>()); }} let (name, language) = languages.into_iter().next().unwrap(); let mut language = language.summarise(); let contents = fs::read_to_string("{1}").unwrap(); println!("{{}} {1}", name); assert_eq!(get_digit!(LINES, contents), language.lines()); println!("{{}} LINES MATCH", name); assert_eq!(get_digit!(CODE, contents), language.code); println!("{{}} CODE MATCH", name); assert_eq!(get_digit!(COMMENTS, contents), language.comments); println!("{{}} COMMENTS MATCH", name); assert_eq!(get_digit!(BLANKS, contents), language.blanks); println!("{{}} BLANKS MATCH", name); let report = language.reports.pop().unwrap(); let stats = report.stats.summarise(); assert_eq!(language.lines(), stats.lines()); assert_eq!(language.code, stats.code); assert_eq!(language.comments, stats.comments); assert_eq!(language.blanks, stats.blanks); }} "####, name, path.display(), std::fs::canonicalize(root.join(path)).unwrap().display(), )); } Ok(fs::write(Path::new(&out_dir).join("tests.rs"), string)?) } tokei-12.1.2/languages.json010064400007650000024000001126231377740532000137450ustar 00000000000000{ "languages": { "Abap": { "name": "ABAP", "line_comment": ["*", "\\\""], "extensions": ["abap"] }, "ABNF": { "line_comment": [";"], "extensions": ["abnf"] }, "ActionScript": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["as"] }, "Ada": { "line_comment": ["--"], "extensions": ["ada", "adb", "ads", "pad"] }, "Alex": { "extensions": ["x"] }, "Agda": { "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "extensions": ["agda"] }, "Alloy": { "line_comment": ["--", "//"], "multi_line_comments": [["/*", "*/"]], "extensions": ["als"] }, "Arduino": { "name": "Arduino C++", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["ino"] }, "AsciiDoc": { "line_comment": ["//"], "multi_line_comments": [["////", "////"]], "extensions": ["adoc", "asciidoc"] }, "Asn1": { "name": "ASN.1", "line_comment": ["--"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "multi_line_comments": [["/*", "*/"]], "extensions": ["asn1"] }, "Assembly": { "line_comment": [";"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["asm"] }, "AssemblyGAS": { "name": "GNU Style Assembly", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["s"] }, "Asp": { "name": "ASP", "line_comment": ["'", "REM"], "extensions": ["asa", "asp"] }, "AspNet": { "name": "ASP.NET", "multi_line_comments": [[""], ["<%--", "-->"]], "extensions": [ "asax", "ascx", "asmx", "aspx", "master", "sitemap", "webinfo" ] }, "Autoconf": { "line_comment": ["#", "dnl"], "extensions": ["in"] }, "AutoHotKey": { "line_comment": [";"], "multi_line_comments": [["/*", "*/"]], "extensions": ["ahk"] }, "Automake": { "line_comment": ["#"], "extensions": ["am"] }, "Sh": { "name": "Shell", "shebangs": ["#!/bin/sh"], "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["sh"], "extensions": ["sh"] }, "Bash": { "name": "BASH", "shebangs": ["#!/bin/bash"], "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["bash"], "extensions": ["bash"] }, "BrightScript": { "quotes": [["\\\"", "\\\""]], "line_comment": ["'", "REM"], "extensions": ["brs"] }, "Elvish": { "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["elvish"], "extensions": ["elv"] }, "Fish": { "shebangs": ["#!/bin/fish"], "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["fish"], "extensions": ["fish"] }, "Batch": { "line_comment": ["REM", "::"], "extensions": ["bat", "btm", "cmd"] }, "Bean": { "line_comment": [";"], "quotes": [["\\\"", "\\\""]], "extensions": ["bean", "beancount"] }, "C": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["c", "ec", "pgc"] }, "Cabal": { "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "extensions": ["cabal"] }, "Cassius": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["cassius"] }, "Ceylon": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "extensions": ["ceylon"] }, "CHeader": { "name": "C Header", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["h"] }, "Clojure": { "line_comment": [";"], "quotes": [["\\\"", "\\\""]], "extensions": ["clj"] }, "ClojureScript": { "line_comment": [";"], "quotes": [["\\\"", "\\\""]], "extensions": ["cljs"] }, "ClojureC": { "line_comment": [";"], "quotes": [["\\\"", "\\\""]], "extensions": ["cljc"] }, "CMake": { "line_comment": ["#"], "quotes": [["\\\"", "\\\""]], "extensions": ["cmake"], "filenames": ["cmakelists.txt"] }, "Cobol": { "name": "COBOL", "line_comment": ["*"], "extensions": ["cob", "cbl", "ccp", "cobol", "cpy"] }, "CodeQL": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["ql", "qll"] }, "CoffeeScript": { "line_comment": ["#"], "multi_line_comments": [["###", "###"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["coffee", "cjsx"] }, "Cogent": { "line_comment": ["--"], "extensions": ["cogent"] }, "ColdFusion": { "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["cfm"] }, "ColdFusionScript": { "name": "ColdFusion CFScript", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["cfc"] }, "Coq": { "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["(*", "*)"]], "extensions": ["v"] }, "Cpp": { "name": "C++", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "verbatim_quotes": [["R\\\"(", ")\\\""]], "extensions": ["cc", "cpp", "cxx", "c++", "pcc", "tpp"] }, "CppHeader": { "name": "C++ Header", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["hh", "hpp", "hxx", "inl", "ipp"] }, "Crystal": { "line_comment": ["#"], "shebangs": ["#!/usr/bin/crystal"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["crystal"], "extensions": ["cr"] }, "CSharp": { "name": "C#", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "verbatim_quotes": [["@\\\"", "\\\""]], "extensions": ["cs", "csx"] }, "CShell": { "name": "C Shell", "shebangs": ["#!/bin/csh"], "line_comment": ["#"], "env": ["csh"], "extensions": ["csh"] }, "Css": { "name": "CSS", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "mime": ["text/css"], "extensions": ["css"] }, "D": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "nested_comments": [["/+", "+/"]], "extensions": ["d"] }, "Daml": { "name": "DAML", "nested": true, "line_comment": ["-- "], "multi_line_comments": [["{-", "-}"]], "extensions": ["daml"] }, "Dart": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [ ["\\\"", "\\\""], ["'", "'"], ["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["'''", "'''"] ], "extensions": ["dart"] }, "DeviceTree": { "name": "Device Tree", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["dts", "dtsi"] }, "Dhall":{ "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "quotes": [["\\\"", "\\\""], ["''", "''"]], "extensions": ["dhall"] }, "DreamMaker": { "name": "Dream Maker", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "nested": true, "extensions": ["dm", "dme"], "quotes": [["\\\"", "\\\""], ["{\\\"", "\\\"}"], ["'", "'"]] }, "Dockerfile": { "line_comment": ["#"], "extensions": ["dockerfile", "dockerignore"], "filenames": ["dockerfile"], "quotes": [["\\\"", "\\\""], ["'", "'"]] }, "DotNetResource": { "name": ".NET Resource", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""]], "extensions": ["resx"] }, "Dust": { "name": "Dust.js", "multi_line_comments": [["{!", "!}"]], "extensions": ["dust"] }, "Edn": { "line_comment": [";"], "extensions": ["edn"] }, "Elisp": { "name": "Emacs Lisp", "line_comment": [";"], "extensions": ["el"] }, "Elixir": { "line_comment": ["#"], "quotes": [ ["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["\\\"", "\\\""], ["'''", "'''"], ["'", "'"] ], "extensions": ["ex", "exs"] }, "Elm": { "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "extensions": ["elm"] }, "EmacsDevEnv": { "name": "Emacs Dev Env", "line_comment": [";"], "extensions": ["ede"] }, "Emojicode": { "line_comment": ["💭"], "multi_line_comments": [["💭🔜", "🔚💭"], ["📗", "📗"], ["📘", "📘"]], "quotes": [["❌🔤", "❌🔤"]], "extensions": ["emojic", "🍇"] }, "Erlang": { "line_comment": ["%"], "extensions": ["erl", "hrl"] }, "FEN": { "name": "FEN", "blank": true, "extensions": ["fen"] }, "FlatBuffers": { "name": "FlatBuffers Schema", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["fbs"] }, "Fstar": { "name": "F*", "quotes": [["\\\"", "\\\""]], "line_comment": ["//"], "multi_line_comments": [["(*", "*)"]], "extensions": ["fst"] }, "Forth": { "line_comment": ["\\\\"], "multi_line_comments": [["( ", ")"]], "extensions": [ "4th", "forth", "fr", "frt", "fth", "f83", "fb", "fpm", "e4", "rx", "ft" ] }, "FortranLegacy": { "name": "FORTRAN Legacy", "line_comment": ["c", "C", "!", "*"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["f", "for", "ftn", "f77", "pfo"] }, "FortranModern": { "name": "FORTRAN Modern", "line_comment": ["!"], "quotes": [["\\\"", "\\\""]], "extensions": ["f03", "f08", "f90", "f95"] }, "FreeMarker": { "multi_line_comments": [["<#--", "-->"]], "extensions": ["ftl", "ftlh", "ftlx"] }, "FSharp": { "name": "F#", "line_comment": ["//"], "multi_line_comments": [["(*", "*)"]], "quotes": [["\\\"", "\\\""]], "verbatim_quotes": [["@\\\"", "\\\""]], "extensions": ["fs", "fsi", "fsx", "fsscript"] }, "Futhark": { "line_comment": ["--"], "extensions": ["fut"] }, "GDB": { "name": "GDB Script", "line_comment": ["#"], "extensions": ["gdb"] }, "GdScript": { "name": "GDScript", "line_comment": ["#"], "quotes": [ ["\\\"", "\\\""], ["'", "'"], ["\\\"\\\"\\\"", "\\\"\\\"\\\""] ], "extensions": ["gd"] }, "Gherkin": { "name": "Gherkin (Cucumber)", "line_comment": ["#"], "extensions": ["feature"] }, "Gleam": { "name": "Gleam", "line_comment": ["//", "///", "////"], "quotes": [["\\\"", "\\\""]], "extensions": ["gleam"] }, "Glsl": { "name": "GLSL", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["vert", "tesc", "tese", "geom", "frag", "comp", "glsl"] }, "Go": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["go"] }, "Gohtml": { "name": "Go HTML", "multi_line_comments": [[""], ["{{/*", "*/}}"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["gohtml"] }, "Graphql": { "name": "GraphQL", "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "line_comment": ["#"], "extensions": ["gql", "graphql"] }, "Groovy": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["groovy", "grt", "gtpl", "gvy"] }, "Gwion": { "line_comment": ["#!"], "quotes": [["\\\"", "\\\""]], "extensions": ["gw"] }, "Happy": { "extensions": ["y", "ly"] }, "Handlebars": { "multi_line_comments": [[""], ["{{!", "}}"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["hbs", "handlebars"] }, "Haskell": { "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "extensions": ["hs"] }, "Hcl": { "name": "HCL", "line_comment": ["#", "//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["tf", "tfvars"] }, "Headache": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["ha"] }, "Hlsl": { "name": "HLSL", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["hlsl"] }, "HolyC": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["HC", "hc"] }, "Html": { "name": "HTML", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "kind": "html", "important_syntax": [""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["hamlet"] }, "Haxe": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["hx"] }, "Hex": { "name": "HEX", "blank": true, "extensions": ["hex"] }, "Idris": { "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "extensions": ["idr", "lidr"], "nested": true }, "Ini": { "name": "INI", "line_comment": [";", "#"], "extensions": ["ini"] }, "IntelHex": { "name": "Intel HEX", "blank": true, "extensions": ["ihex"] }, "Isabelle": { "line_comment": ["--"], "multi_line_comments": [ ["{*", "*}"], ["(*", "*)"], ["‹", "›"], ["\\\\", "\\\\"] ], "quotes": [["''", "''"]], "extensions": ["thy"] }, "Jai": { "name": "JAI", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["jai"], "nested": true }, "Java": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["java"] }, "JavaScript": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "mime": [ "application/javascript", "application/ecmascript", "application/x-ecmascript", "application/x-javascript", "text/javascript", "text/ecmascript", "text/javascript1.0", "text/javascript1.1", "text/javascript1.2", "text/javascript1.3", "text/javascript1.4", "text/javascript1.5", "text/jscript", "text/livescript", "text/x-ecmascript", "text/x-javascript" ], "extensions": ["js", "mjs"] }, "Json": { "name": "JSON", "blank": true, "mime": ["application/json", "application/manifest+json"], "extensions": ["json"] }, "Jsonnet": { "line_comment": ["//", "#"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["jsonnet", "libsonnet"] }, "Jsx": { "name": "JSX", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "extensions": ["jsx"] }, "Julia": { "line_comment": ["#"], "multi_line_comments": [["#=", "=#"]], "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "nested": true, "extensions": ["jl"] }, "Julius": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "extensions": ["julius"] }, "Jupyter": { "name": "Jupyter Notebooks", "extensions": ["ipynb"] }, "K": { "name": "K", "nested": true, "line_comment": ["/"], "quotes": [["\\\"", "\\\""]], "extensions": ["k"] }, "KakouneScript": { "name": "Kakoune script", "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["kak"] }, "Kotlin": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "nested": true, "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "extensions": ["kt", "kts"] }, "Lean": { "line_comment": ["--"], "multi_line_comments": [["/-", "-/"]], "nested": true, "extensions": ["lean", "hlean"] }, "Less": { "name": "LESS", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "extensions": ["less"], "quotes": [["\\\"", "\\\""], ["'", "'"]] }, "Liquid": { "name": "Liquid", "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["liquid"], "multi_line_comments": [[""], ["{% comment %}", "{% endcomment %}"]] }, "LinkerScript": { "name": "LD Script", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["lds"] }, "Lisp": { "line_comment": [";"], "multi_line_comments": [["#|", "|#"]], "nested": true, "extensions": ["lisp", "lsp"] }, "LiveScript": { "line_comment": ["#"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["ls"] }, "LLVM": { "line_comment": [";"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["ll"] }, "Logtalk": { "line_comment": ["%"], "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["/*", "*/"]], "extensions": ["lgt", "logtalk"] }, "Lua": { "line_comment": ["--"], "multi_line_comments": [["--[[", "]]"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["lua"] }, "Lucius": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["lucius"] }, "Madlang": { "extensions": ["mad"], "line_comment": ["#"], "multi_line_comments": [["{#", "#}"]] }, "Makefile": { "line_comment": ["#"], "extensions": ["makefile", "mak", "mk"], "filenames": ["makefile"] }, "Markdown": { "literate": true, "important_syntax": ["```"], "extensions": ["md", "markdown"] }, "ModuleDef": { "name": "Module-Definition", "extensions": ["def"], "line_comment": [";"] }, "MoonScript": { "line_comment": ["--"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["moon"] }, "Meson": { "line_comment": ["#"], "quotes": [["'", "'"], ["'''", "'''"]], "filenames": ["meson.build", "meson_options.txt"] }, "Mint": { "blank": true, "extensions": ["mint"] }, "Mustache": { "multi_line_comments": [["{{!", "}}"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["mustache"] }, "Nim": { "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "extensions": ["nim"] }, "Nix": { "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "line_comment": ["#"], "extensions": ["nix"] }, "ObjectiveC": { "name": "Objective-C", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["m"] }, "ObjectiveCpp": { "name": "Objective-C++", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["mm"] }, "OCaml": { "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["(*", "*)"]], "extensions": ["ml", "mli", "mll", "mly", "re", "rei"] }, "Odin": { "extensions": ["odin"], "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]] }, "OpenType": { "name": "OpenType Feature File", "line_comment": ["#"], "extensions": ["fea"] }, "Org": { "line_comment": ["# "], "extensions": ["org"] }, "Oz": { "line_comment": ["%"], "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["/*", "*/"]], "extensions": ["oz"] }, "Pan": { "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["pan", "tpl"] }, "Pascal": { "nested": true, "line_comment": ["//"], "multi_line_comments": [["{", "}"], ["(*", "*)"]], "quotes": [["'", "'"]], "extensions": ["pas", "pp"] }, "Perl": { "shebangs": ["#!/usr/bin/perl"], "line_comment": ["#"], "multi_line_comments": [["=pod", "=cut"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["pl", "pm"] }, "Perl6": { "name": "Rakudo", "line_comment": ["#"], "multi_line_comments": [["=begin", "=end"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["pl6", "pm6"] }, "Pest": { "line_comment": ["//"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["pest"] }, "NotQuitePerl": { "name": "Not Quite Perl", "line_comment": ["#"], "multi_line_comments": [["=begin", "=end"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["nqp"] }, "Php": { "name": "PHP", "line_comment": ["#", "//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["php"] }, "Polly": { "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["polly"] }, "Pony": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "doc_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""]], "extensions": ["pony"] }, "PostCss": { "name": "PostCSS", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["pcss", "sss"] }, "Processing": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["pde"] }, "Prolog": { "line_comment": ["%"], "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["/*", "*/"]], "extensions": ["p", "pro"] }, "PowerShell": { "line_comment": ["#"], "multi_line_comments": [["<#", "#>"]], "quotes": [ ["\\\"", "\\\""], ["'", "'"], ["\\\"@", "@\\\""], ["@'", "'@"] ], "extensions": ["ps1", "psm1", "psd1", "ps1xml", "cdxml", "pssc", "psc1"] }, "PSL": { "name": "PSL Assertion", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["psl"] }, "Protobuf": { "name": "Protocol Buffers", "line_comment": ["//"], "extensions": ["proto"] }, "Pug" : { "line_comment": ["//", "//-"], "quotes": [ ["#{\\\"", "\\\"}"], ["#{'", "'}"], ["#{`", "`}"] ], "extensions": ["pug"] }, "PureScript": { "nested": true, "line_comment": ["--"], "multi_line_comments": [["{-", "-}"]], "extensions": ["purs"] }, "Python": { "line_comment": ["#"], "doc_quotes": [["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["'''", "'''"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["python", "python2", "python3"], "mime": ["text/x-python"], "extensions": ["py", "pyw"] }, "Qcl": { "name": "QCL", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["qcl"] }, "Q": { "name": "Q", "nested": true, "line_comment": ["/"], "quotes": [["\\\"", "\\\""]], "extensions": ["q"] }, "Qml": { "name": "QML", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["qml"] }, "R": { "line_comment": ["#"], "extensions": ["r"] }, "Racket": { "line_comment": [";"], "multi_line_comments": [["#|", "|#"]], "nested": true, "extensions": ["rkt"] }, "Rakefile": { "line_comment": ["#"], "multi_line_comments": [["=begin", "=end"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "filenames": ["rakefile"], "extensions": ["rake"] }, "Razor": { "multi_line_comments": [[""], ["@*", "*@"]], "extensions": ["cshtml"] }, "Renpy": { "name": "Ren'Py", "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "extensions": ["rpy"] }, "RON": { "name": "Rusty Object Notation", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "nested": true, "extensions": ["ron"] }, "RPMSpecfile": { "name": "RPM Specfile", "line_comment": ["#"], "extensions": ["spec"] }, "Ruby": { "line_comment": ["#"], "multi_line_comments": [["=begin", "=end"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "env": ["ruby"], "extensions": ["rb"] }, "RubyHtml": { "name": "Ruby HTML", "multi_line_comments": [[""]], "important_syntax": ["", "<'"]], "extensions": ["e"] }, "Spice": { "name": "Spice Netlist", "line_comment": ["*"], "extensions": ["ckt"] }, "Sql": { "name": "SQL", "line_comment": ["--"], "multi_line_comments": [["/*", "*/"]], "quotes": [["'", "'"]], "extensions": ["sql"] }, "SRecode": { "name": "SRecode Template", "line_comment": [";;"], "extensions": ["srt"] }, "Stan": { "line_comment": ["//", "#"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["stan"] }, "Stratego": { "name": "Stratego/XT", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["$[", "]"], ["$<", ">"], ["${", "}"]], "extensions": ["str"] }, "Stylus": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["styl"] }, "Svelte": { "multi_line_comments": [[""]], "important_syntax": [""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "mime": ["image/svg+xml"], "extensions": ["svg"] }, "Swift": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "nested": true, "extensions": ["swift"] }, "Swig": { "name": "SWIG", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "nested": true, "extensions": ["swg", "i"] }, "SystemVerilog": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["sv", "svh"] }, "Tcl": { "name": "TCL", "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["tcl"] }, "Tera": { "multi_line_comments": [[""], ["{#", "#}"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["tera"] }, "Tex": { "name": "TeX", "line_comment": ["%"], "extensions": ["tex", "sty"] }, "Text": { "name": "Plain Text", "literate": true, "mime": ["text/plain"], "extensions": ["text", "txt"] }, "Thrift": { "line_comment": ["#", "//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["thrift"] }, "Toml": { "name": "TOML", "line_comment": ["#"], "quotes": [ ["\\\"", "\\\""], ["'", "'"], ["\\\"\\\"\\\"", "\\\"\\\"\\\""], ["'''", "'''"] ], "extensions": ["toml"] }, "Tsx": { "name": "TSX", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "extensions": ["tsx"] }, "Ttcn": { "name": "TTCN-3", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["ttcn", "ttcn3", "ttcnpp"] }, "Twig": { "name": "Twig", "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["twig"], "multi_line_comments": [[""], ["{#", "#}"]] }, "TypeScript": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "extensions": ["ts"] }, "UnrealPlugin": { "name": "Unreal Plugin", "blank": true, "extensions": ["uplugin"] }, "UnrealProject": { "name": "Unreal Project", "blank": true, "extensions": ["uproject"] }, "UnrealScript": { "name": "Unreal Script", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["uc", "uci", "upkg"] }, "UnrealShader": { "name": "Unreal Shader", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["usf"] }, "UnrealShaderHeader": { "name": "Unreal Shader Header", "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["ush"] }, "UnrealDeveloperMarkdown": { "name": "Unreal Markdown", "important_syntax": ["```"], "extensions": ["udn"] }, "UrWeb": { "name": "Ur/Web", "quotes": [["\\\"", "\\\""]], "multi_line_comments": [["(*", "*)"]], "extensions": ["ur", "urs"] }, "UrWebProject": { "name": "Ur/Web Project", "line_comment": ["#"], "extensions": ["urp"] }, "Vala": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["vala"] }, "VB6": { "name": "VB6", "line_comment": ["'"], "extensions": ["frm", "bas", "cls"] }, "VBScript": { "name": "VBScript", "line_comment": ["'", "REM"], "extensions": ["vbs"] }, "Velocity": { "name": "Apache Velocity", "line_comment": ["##"], "multi_line_comments": [["#*", "*#"]], "extensions": ["vm"], "quotes": [["'", "'"], ["\\\"", "\\\""]] }, "Verilog": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""]], "extensions": ["vg", "vh"] }, "VerilogArgsFile": { "name": "Verilog Args File", "extensions": ["irunargs", "xrunargs"] }, "Vhdl": { "name": "VHDL", "line_comment": ["--"], "extensions": ["vhd", "vhdl"] }, "VisualBasic": { "name": "Visual Basic", "quotes": [["\\\"", "\\\""]], "line_comment": ["'"], "extensions": ["vb"] }, "VisualStudioSolution": { "name": "Visual Studio Solution", "blank": true, "extensions": ["sln"] }, "VisualStudioProject": { "name": "Visual Studio Project", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["vcproj", "vcxproj"] }, "VimScript": { "name": "Vim script", "line_comment": ["\\\""], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["vim"] }, "Vue": { "name": "Vue", "line_comment": ["//"], "multi_line_comments": [[""], ["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["`", "`"]], "important_syntax": [""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["xaml"] }, "XcodeConfig": { "name": "Xcode Config", "line_comment": ["//"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["xcconfig"] }, "Xml": { "name": "XML", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["xml"] }, "XSL": { "name": "XSL", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["xsl", "xslt"] }, "MsBuild": { "name": "MSBuild", "multi_line_comments": [[""]], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["csproj", "vbproj", "fsproj", "props", "targets"] }, "Xtend": { "line_comment": ["//"], "multi_line_comments": [["/*", "*/"]], "quotes": [["\\\"", "\\\""], ["'", "'"], ["'''", "'''"]], "extensions": ["xtend"] }, "Yaml": { "name": "YAML", "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["yaml", "yml"] }, "Zig": { "line_comment": ["//"], "quotes": [["\\\"", "\\\""]], "extensions": ["zig"] }, "Zsh": { "shebangs": ["#!/bin/zsh"], "line_comment": ["#"], "quotes": [["\\\"", "\\\""], ["'", "'"]], "extensions": ["zsh"] } } } tokei-12.1.2/src/cli.rs010064400007650000024000000220611377740532000130040ustar 00000000000000use std::mem; use std::process; use clap::{clap_app, crate_description, ArgMatches}; use tokei::{Config, LanguageType, Sort}; use crate::{cli_utils::*, input::Format}; #[derive(Debug)] pub struct Cli<'a> { matches: ArgMatches<'a>, pub columns: Option, pub files: bool, pub hidden: bool, pub no_ignore: bool, pub no_ignore_parent: bool, pub no_ignore_dot: bool, pub no_ignore_vcs: bool, pub output: Option, pub print_languages: bool, pub sort: Option, pub types: Option>, pub compact: bool, pub number_format: num_format::CustomFormat, pub verbose: u64, } impl<'a> Cli<'a> { pub fn from_args() -> Self { let matches = clap_app!(tokei => (version: &*crate_version()) (author: "Erin P. + Contributors") (about: concat!( crate_description!(), "\n", "Support this project on GitHub Sponsors: https://github.com/sponsors/XAMPPRocky" ) ) (@arg columns: -c --columns +takes_value conflicts_with[output] "Sets a strict column width of the output, only available for \ terminal output.") (@arg exclude: -e --exclude +takes_value +multiple number_of_values(1) "Ignore all files & directories matching the pattern.") (@arg files: -f --files "Will print out statistics on individual files.") (@arg file_input: -i --input +takes_value "Gives statistics from a previous tokei run. Can be given a file path, \ or \"stdin\" to read from stdin.") (@arg hidden: --hidden "Count hidden files.") (@arg input: conflicts_with[languages] ... "The path(s) to the file or directory to be counted.") (@arg languages: -l --languages conflicts_with[input] "Prints out supported languages and their extensions.") (@arg no_ignore: --("no-ignore") "Don't respect ignore files (.gitignore, .ignore, etc.). This implies \ --no-ignore-parent, --no-ignore-dot, and --no-ignore-vcs.") (@arg no_ignore_parent: --("no-ignore-parent") "Don't respect ignore files (.gitignore, .ignore, etc.) in parent \ directories.") (@arg no_ignore_dot: --("no-ignore-dot") "Don't respect .ignore and .tokeignore files, including those in \ parent directories.") (@arg no_ignore_vcs: --("no-ignore-vcs") "Don't respect VCS ignore files (.gitignore, .hgignore, etc.), including \ those in parent directories.") (@arg output: -o --output // `all` is used so to fail later with a better error possible_values(Format::all()) +takes_value "Outputs Tokei in a specific format. Compile with additional features for more \ format support.") (@arg sort: -s --sort possible_values(&["files", "lines", "blanks", "code", "comments"]) case_insensitive(true) +takes_value "Sort languages based on column") (@arg types: -t --type +takes_value "Filters output by language type, seperated by a comma. i.e. -t=Rust,Markdown") (@arg compact: -C --compact "Do not print statistics about embedded languages.") (@arg num_format_style: -n --("num-format") possible_values(NumberFormatStyle::all()) conflicts_with[output] +takes_value "Format of printed numbers, i.e. plain (1234, default), commas (1,234), dots \ (1.234), or underscores (1_234). Cannot be used with --output.") (@arg verbose: -v --verbose ... "Set log output level: 1: to show unknown file extensions, 2: reserved for future debugging, 3: enable file level trace. Not recommended on multiple files") ) .get_matches(); let columns = matches.value_of("columns").map(parse_or_exit::); let files = matches.is_present("files"); let hidden = matches.is_present("hidden"); let no_ignore = matches.is_present("no_ignore"); let no_ignore_parent = matches.is_present("no_ignore_parent"); let no_ignore_dot = matches.is_present("no_ignore_dot"); let no_ignore_vcs = matches.is_present("no_ignore_vcs"); let print_languages = matches.is_present("languages"); let verbose = matches.occurrences_of("verbose"); let compact = matches.is_present("compact"); let types = matches.value_of("types").map(|e| { e.split(',') .map(|t| t.parse::()) .filter_map(Result::ok) .collect() }); let num_format_style: NumberFormatStyle = matches .value_of("num_format_style") .map(parse_or_exit::) .unwrap_or_default(); let number_format = match num_format_style.get_format() { Ok(format) => format, Err(e) => { eprintln!("Error:\n{}", e); process::exit(1); } }; // Sorting category should be restricted by clap but parse before we do // work just in case. let sort = matches.value_of("sort").map(parse_or_exit::); // Format category is overly accepting by clap (so the user knows what // is supported) but this will fail if support is not compiled in and // give a useful error to the user. let output = matches.value_of("output").map(parse_or_exit::); crate::cli_utils::setup_logger(verbose); let cli = Cli { columns, files, hidden, matches, no_ignore, no_ignore_parent, no_ignore_dot, no_ignore_vcs, output, print_languages, sort, types, verbose, number_format, compact, }; debug!("CLI Config: {:#?}", cli); cli } pub fn file_input(&self) -> Option<&str> { self.matches.value_of("file_input") } pub fn ignored_directories(&self) -> Vec<&str> { let mut ignored_directories: Vec<&str> = Vec::new(); if let Some(user_ignored) = self.matches.values_of("exclude") { ignored_directories.extend(user_ignored); } ignored_directories } pub fn input(&self) -> Vec<&str> { match self.matches.values_of("input") { Some(vs) => vs.collect(), None => vec!["."], } } pub fn print_supported_languages() { for key in LanguageType::list() { println!("{:<25}", key); } } /// Overrides the shared options (See `tokei::Config` for option /// descriptions) between the CLI and the config files. CLI flags have /// higher precedence than options present in config files. /// /// #### Shared options /// * `no_ignore` /// * `no_ignore_parent` /// * `no_ignore_dot` /// * `no_ignore_vcs` /// * `types` pub fn override_config(&mut self, mut config: Config) -> Config { config.hidden = if self.hidden { Some(true) } else { config.hidden }; config.no_ignore = if self.no_ignore { Some(true) } else { config.no_ignore }; config.no_ignore_parent = if self.no_ignore_parent { Some(true) } else { config.no_ignore_parent }; config.no_ignore_dot = if self.no_ignore_dot { Some(true) } else { config.no_ignore_dot }; config.no_ignore_vcs = if self.no_ignore_vcs { Some(true) } else { config.no_ignore_vcs }; config.types = mem::replace(&mut self.types, None).or(config.types); config } pub fn print_input_parse_failure(input_filename: &str) { eprintln!("Error:\n Failed to parse input file: {}", input_filename); let not_supported = Format::not_supported(); if !not_supported.is_empty() { eprintln!( " This version of tokei was compiled without serialization support for the following formats: {not_supported} You may want to install any comma separated combination of {all:?}: cargo install tokei --features {all:?} Or use the 'all' feature: cargo install tokei --features all \n", not_supported = not_supported.join(", "), // no space after comma to ease copypaste all = self::Format::all_feature_names().join(",") ); } } } tokei-12.1.2/src/cli_utils.rs010064400007650000024000000340711377740532000142300ustar 00000000000000use std::{ fmt, io::{self, Write}, process, str::FromStr, }; use clap::crate_version; use num_format::ToFormattedString; use crate::input::Format; use tokei::{find_char_boundary, CodeStats, Language, LanguageType, Report}; pub const FALLBACK_ROW_LEN: usize = 79; const NO_LANG_HEADER_ROW_LEN: usize = 67; const NO_LANG_ROW_LEN: usize = 61; const NO_LANG_ROW_LEN_NO_SPACES: usize = 54; const IDENT_INACCURATE: &str = "(!)"; pub fn crate_version() -> String { if Format::supported().is_empty() { format!( "{} compiled without serialization formats.", crate_version!() ) } else { format!( "{} compiled with serialization support: {}", crate_version!(), Format::supported().join(", ") ) } } pub fn setup_logger(verbose_option: u64) { use log::LevelFilter; let mut builder = env_logger::Builder::new(); let filter_level = match verbose_option { 1 => LevelFilter::Warn, 2 => LevelFilter::Debug, 3 => LevelFilter::Trace, _ => LevelFilter::Error, }; builder.filter(None, filter_level); builder.init(); } pub fn parse_or_exit(s: &str) -> T where T: FromStr, T::Err: fmt::Display, { T::from_str(s).unwrap_or_else(|e| { eprintln!("Error:\n{}", e); process::exit(1); }) } #[non_exhaustive] #[derive(Debug, Copy, Clone)] pub enum NumberFormatStyle { // 1234 (Default) Plain, // 1,234 Commas, // 1.234 Dots, // 1_234 Underscores, } impl Default for NumberFormatStyle { fn default() -> Self { Self::Plain } } impl FromStr for NumberFormatStyle { type Err = String; fn from_str(s: &str) -> Result { match s { "plain" => Ok(Self::Plain), "commas" => Ok(Self::Commas), "dots" => Ok(Self::Dots), "underscores" => Ok(Self::Underscores), _ => Err(format!( "Expected 'plain', 'commas', 'underscores', or 'dots' for num-format, but got '{}'", s, )), } } } impl NumberFormatStyle { fn separator(self) -> &'static str { match self { Self::Plain => "", Self::Commas => ",", Self::Dots => ".", Self::Underscores => "_", } } pub fn all() -> &'static [&'static str] { &["commas", "dots", "plain", "underscores"] } pub fn get_format(self) -> Result { num_format::CustomFormat::builder() .grouping(num_format::Grouping::Standard) .separator(self.separator()) .build() } } pub struct Printer { writer: W, columns: usize, path_length: usize, row: String, subrow: String, list_files: bool, number_format: num_format::CustomFormat, } impl Printer { pub fn new( columns: usize, list_files: bool, writer: W, number_format: num_format::CustomFormat, ) -> Self { Self { columns, list_files, path_length: columns - NO_LANG_ROW_LEN_NO_SPACES, writer, row: "=".repeat(columns), subrow: "-".repeat(columns), number_format, } } } impl Printer { pub fn print_header(&mut self) -> io::Result<()> { self.print_row()?; writeln!( self.writer, " {:<6$} {:>12} {:>12} {:>12} {:>12} {:>12}", "Language", "Files", "Lines", "Code", "Comments", "Blanks", self.columns - NO_LANG_HEADER_ROW_LEN )?; self.print_row() } pub fn print_inaccuracy_warning(&mut self) -> io::Result<()> { writeln!( self.writer, "Note: results can be inaccurate for languages marked with '{}'", IDENT_INACCURATE ) } pub fn print_language(&mut self, language: &Language, name: &str) -> io::Result<()> where W: Write, { self.print_language_name(language.inaccurate, name, None)?; write!(self.writer, " ")?; writeln!( self.writer, "{:>6} {:>12} {:>12} {:>12} {:>12}", language .reports .len() .to_formatted_string(&self.number_format), language.lines().to_formatted_string(&self.number_format), language.code.to_formatted_string(&self.number_format), language.comments.to_formatted_string(&self.number_format), language.blanks.to_formatted_string(&self.number_format), ) } fn print_language_in_print_total(&mut self, language: &Language) -> io::Result<()> where W: Write, { self.print_language_name(language.inaccurate, "Total", None)?; write!(self.writer, " ")?; writeln!( self.writer, "{:>6} {:>12} {:>12} {:>12} {:>12}", language .children .values() .map(Vec::len) .sum::() .to_formatted_string(&self.number_format), language.lines().to_formatted_string(&self.number_format), language.code.to_formatted_string(&self.number_format), language.comments.to_formatted_string(&self.number_format), language.blanks.to_formatted_string(&self.number_format), ) } pub fn print_language_name( &mut self, inaccurate: bool, name: &str, prefix: Option<&str>, ) -> io::Result<()> { let mut lang_section_len = self.columns - NO_LANG_ROW_LEN - prefix.as_deref().map_or(0, str::len); if inaccurate { lang_section_len -= IDENT_INACCURATE.len(); } if let Some(prefix) = prefix { write!(self.writer, "{}", prefix)?; } // truncate and replace the last char with a `|` if the name is too long if lang_section_len < name.len() { write!(self.writer, " {:.len$}", name, len = lang_section_len - 1)?; write!(self.writer, "|")?; } else { write!(self.writer, " {: io::Result<()> { self.print_language_name(false, &language_type.to_string(), Some(&(" |-")))?; let mut code = 0; let mut comments = 0; let mut blanks = 0; for stats in stats.iter().map(|s| s.summarise()) { code += stats.code; comments += stats.comments; blanks += stats.blanks; } if !stats.is_empty() { writeln!( self.writer, " {:>6} {:>12} {:>12} {:>12} {:>12}", stats.len().to_formatted_string(&self.number_format), (code + comments + blanks).to_formatted_string(&self.number_format), code.to_formatted_string(&self.number_format), comments.to_formatted_string(&self.number_format), blanks.to_formatted_string(&self.number_format), ) } else { Ok(()) } } fn print_language_total(&mut self, parent: &Language) -> io::Result<()> { for (language, reports) in &parent.children { self.print_code_stats( *language, &reports .iter() .map(|r| r.stats.summarise()) .collect::>(), )?; } let mut subtotal = tokei::Report::new("(Total)".into()); let summary = parent.summarise(); subtotal.stats.code += summary.code; subtotal.stats.comments += summary.comments; subtotal.stats.blanks += summary.blanks; self.print_report_with_name(&subtotal)?; Ok(()) } pub fn print_results<'a, I>(&mut self, languages: I, compact: bool) -> io::Result<()> where I: Iterator, { let (a, b): (Vec<_>, Vec<_>) = languages .filter(|(_, v)| !v.is_empty()) .partition(|(_, l)| compact || l.children.is_empty()); let mut first = true; for languages in &[&a, &b] { for &(name, language) in *languages { let has_children = !(compact || language.children.is_empty()); if first { first = false; } else if has_children || self.list_files { self.print_subrow()?; } self.print_language(language, name.name())?; if has_children { self.print_language_total(language)?; } if self.list_files { self.print_subrow()?; let (a, b): (Vec<_>, Vec<_>) = language .reports .iter() .partition(|r| r.stats.blobs.is_empty()); for reports in &[&a, &b] { let mut first = true; for report in reports.iter() { if !report.stats.blobs.is_empty() { if first && a.is_empty() { writeln!(self.writer, " {}", report.name.display())?; first = false; } else { writeln!( self.writer, "-- {} {}", report.name.display(), "-".repeat( self.columns - 4 - report.name.display().to_string().len() ) )?; } let mut new_report = (*report).clone(); new_report.name = name.to_string().into(); writeln!( self.writer, " |-{:1$}", new_report, self.path_length - 3 )?; self.print_report_total(&report, language.inaccurate)?; } else { writeln!(self.writer, "{:1$}", report, self.path_length)?; } } } } } } Ok(()) } fn print_row(&mut self) -> io::Result<()> { writeln!(self.writer, "{}", self.row) } fn print_subrow(&mut self) -> io::Result<()> { writeln!(self.writer, "{}", self.subrow) } fn print_report( &mut self, language_type: LanguageType, stats: &CodeStats, inaccurate: bool, ) -> io::Result<()> { self.print_language_name(inaccurate, &language_type.to_string(), Some(" |-"))?; writeln!( self.writer, " {:>6} {:>12} {:>12} {:>12} {:>12}", " ", stats.lines().to_formatted_string(&self.number_format), stats.code.to_formatted_string(&self.number_format), stats.comments.to_formatted_string(&self.number_format), stats.blanks.to_formatted_string(&self.number_format), ) } fn print_report_total(&mut self, report: &Report, inaccurate: bool) -> io::Result<()> { if report.stats.blobs.is_empty() { return Ok(()); } let mut subtotal = tokei::Report::new("|- (Total)".into()); subtotal.stats.code += report.stats.code; subtotal.stats.comments += report.stats.comments; subtotal.stats.blanks += report.stats.blanks; for (language_type, stats) in &report.stats.blobs { self.print_report(*language_type, stats, inaccurate)?; subtotal.stats += stats.summarise(); } self.print_report_with_name(&report)?; Ok(()) } fn print_report_with_name(&mut self, report: &Report) -> io::Result<()> { let name = report.name.to_string_lossy(); let name_length = name.len(); if name_length <= self.path_length { self.print_report_total_formatted(name, self.path_length, report)?; } else { let mut formatted = String::from("|"); // Add 1 to the index to account for the '|' we add to the output string let from = find_char_boundary(&name, name_length + 1 - self.path_length); formatted.push_str(&name[from..]); self.print_report_total_formatted(name, self.path_length, report)?; } Ok(()) } fn print_report_total_formatted( &mut self, name: std::borrow::Cow<'_, str>, max_len: usize, report: &Report, ) -> io::Result<()> { writeln!( self.writer, " {: 12} {:>12} {:>12} {:>12}", name, report .stats .lines() .to_formatted_string(&self.number_format), report.stats.code.to_formatted_string(&self.number_format), report .stats .comments .to_formatted_string(&self.number_format), report.stats.blanks.to_formatted_string(&self.number_format), max = max_len ) } pub fn print_total(&mut self, languages: tokei::Languages) -> io::Result<()> { let total = languages.total(); self.print_row()?; self.print_language_in_print_total(&total)?; self.print_row() } } tokei-12.1.2/src/config.rs010064400007650000024000000146661377740532000135160ustar 00000000000000use std::{env, fs, path::PathBuf}; use crate::language::LanguageType; use crate::sort::Sort; /// A configuration struct for how [`Languages::get_statistics`] searches and /// counts languages. /// /// ``` /// use tokei::Config; /// /// let config = Config { /// treat_doc_strings_as_comments: Some(true), /// ..Config::default() /// }; /// ``` /// /// [`Languages::get_statistics`]: struct.Languages.html#method.get_statistics #[derive(Debug, Default, Deserialize)] pub struct Config { /// Width of columns to be printed to the terminal. _This option is ignored /// in the library._ *Default:* Auto detected width of the terminal. pub columns: Option, /// Count hidden files and directories. *Default:* `false`. pub hidden: Option, /// Don't respect ignore files (.gitignore, .ignore, etc.). This implies --no-ignore-parent, /// --no-ignore-dot, and --no-ignore-vcs. *Default:* `false`. pub no_ignore: Option, /// Don't respect ignore files (.gitignore, .ignore, etc.) in parent directories. /// *Default:* `false`. pub no_ignore_parent: Option, /// Don't respect .ignore and .tokeignore files, including those in parent directories. /// *Default:* `false`. pub no_ignore_dot: Option, /// Don't respect VCS ignore files (.gitignore, .hgignore, etc.), including those in /// parent directories. *Default:* `false`. pub no_ignore_vcs: Option, /// Whether to treat doc strings in languages as comments. *Default:* /// `false`. pub treat_doc_strings_as_comments: Option, /// Sort languages. *Default:* `None`. pub sort: Option, /// Filters languages searched to just those provided. E.g. A directory /// containing `C`, `Cpp`, and `Rust` with a `Config.types` of `[Cpp, Rust]` /// will count only `Cpp` and `Rust`. *Default:* `None`. pub types: Option>, // /// A map of individual language configuration. // pub languages: Option>, } impl Config { /// Constructs a new `Config` from either `$base/tokei.toml` or /// `$base/.tokeirc`. `tokei.toml` takes precedence over `.tokeirc` /// as the latter is a hidden file on Unix and not an idiomatic /// filename on Windows. fn get_config(base: PathBuf) -> Option { fs::read_to_string(base.join("tokei.toml")) .ok() .or_else(|| fs::read_to_string(base.join(".tokeirc")).ok()) .and_then(|s| toml::from_str(&s).ok()) } /// Creates a `Config` from three configuration files if they are available. /// Files can have two different names `tokei.toml` and `.tokeirc`. /// Firstly it will attempt to find a config in the configuration directory /// (see below), secondly from the home directory, `$HOME/`, /// and thirdly from the current directory, `./`. /// The current directory's configuration will take priority over the configuratio /// directory. /// /// |Platform | Value | Example | /// | ------- | ----- | ------- | /// | Linux | `$XDG_DATA_HOME` or `$HOME`/.local/share | /home/alice/.local/share | /// | macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support | /// | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming | /// /// # Example /// ```toml /// columns = 80 /// types = ["Python"] /// treat_doc_strings_as_comments = true // /// // /// [[languages.Python]] // /// extensions = ["py3"] /// ``` pub fn from_config_files() -> Self { let conf_dir = dirs::config_dir() .and_then(Self::get_config) .unwrap_or_else(Self::default); let home_dir = dirs::home_dir() .and_then(Self::get_config) .unwrap_or_else(Self::default); let current_dir = env::current_dir() .ok() .and_then(Self::get_config) .unwrap_or_else(Self::default); #[allow(clippy::or_fun_call)] Config { columns: current_dir .columns .or(home_dir.columns.or(conf_dir.columns)), //languages: current_dir.languages.or(conf_dir.languages), treat_doc_strings_as_comments: current_dir.treat_doc_strings_as_comments.or(home_dir .treat_doc_strings_as_comments .or(conf_dir.treat_doc_strings_as_comments)), sort: current_dir.sort.or(home_dir.sort.or(conf_dir.sort)), types: current_dir.types.or(home_dir.types.or(conf_dir.types)), no_ignore: current_dir .no_ignore .or(home_dir.no_ignore.or(conf_dir.no_ignore)), no_ignore_parent: current_dir .no_ignore_parent .or(home_dir.no_ignore_parent.or(conf_dir.no_ignore_parent)), no_ignore_dot: current_dir .no_ignore_dot .or(home_dir.no_ignore_dot.or(conf_dir.no_ignore_dot)), no_ignore_vcs: current_dir .no_ignore_vcs .or(home_dir.no_ignore_vcs.or(conf_dir.no_ignore_vcs)), ..Self::default() } } } /* /// Configuration for a individual [`LanguageType`]. /// /// ``` /// use std::collections::HashMap; /// use tokei::{Config, LanguageConfig, LanguageType}; /// /// let config = Config { /// languages: { /// let cpp_conf = LanguageConfig { /// extensions: vec![String::from("c")], /// }; /// /// let mut languages_config = HashMap::new(); /// languages_config.insert(LanguageType::Cpp, cpp_conf); /// /// Some(languages_config) /// }, /// /// ..Config::default() /// }; /// /// ``` /// /// [`LanguageType`]: enum.LanguageType.html #[derive(Debug, Default, Deserialize)] pub struct LanguageConfig { /// Additional extensions for a language. Any extensions that overlap with /// already defined extensions from `tokei` will be ignored. pub extensions: Vec, } impl LanguageConfig { /// Creates a new empty configuration. By default this will not change /// anything from the default. pub fn new() -> Self { Self::default() } /// Accepts a `Vec` representing additional extensions for a /// language. Any extensions that overlap with already defined extensions /// from `tokei` will be ignored. pub fn extensions(&mut self, extensions: Vec) { self.extensions = extensions; } } */ tokei-12.1.2/src/input.rs010064400007650000024000000137701377074642100134060ustar 00000000000000use std::{collections::BTreeMap, error::Error, str::FromStr}; use serde_json::{json, Map}; use tokei::{Language, LanguageType, Languages}; type LanguageMap = BTreeMap; macro_rules! supported_formats { ($( ($name:ident, $feature:expr, $variant:ident [$($krate:ident),+]) => $parse_kode:expr, $print_kode:expr, )+) => ( $( // for each format $( // for each required krate #[cfg(feature = $feature)] extern crate $krate; )+ )+ /// Supported serialization formats. /// /// To enable all formats compile with the `all` feature. #[derive(Debug)] pub enum Format { Json, $( #[cfg(feature = $feature)] $variant ),+ // TODO: Allow adding format at runtime when used as a lib? } impl Format { pub fn supported() -> &'static [&'static str] { &[ "json", $( #[cfg(feature = $feature)] stringify!($name) ),+ ] } pub fn all() -> &'static [&'static str] { &[ $( stringify!($name) ),+ ] } pub fn all_feature_names() -> &'static [&'static str] { &[ $( $feature ),+ ] } pub fn not_supported() -> &'static [&'static str] { &[ $( #[cfg(not(feature = $feature))] stringify!($name) ),+ ] } pub fn parse(input: &str) -> Option { if input.is_empty() { return None } if let Ok(result) = serde_json::from_str(input) { return Some(result) } $( // attributes are not yet allowed on `if` expressions #[cfg(feature = $feature)] { let parse = &{ $parse_kode }; if let Ok(result) = parse(input) { return Some(result) } } )+ // Didn't match any of the compiled serialization formats None } pub fn print(&self, languages: &Languages) -> Result> { // To serde_json Map and add summary let mut map = Map::new(); for (language_type, language) in languages.into_iter() { map.insert(language_type.to_string(), json!(language)); } map.insert(String::from("Total"), json!(languages.total())); match *self { Format::Json => Ok(serde_json::to_string(&map)?), $( #[cfg(feature = $feature)] Format::$variant => { let print= &{ $print_kode }; Ok(print(&map)?) } ),+ } } } impl FromStr for Format { type Err = String; fn from_str(format: &str) -> Result { match format { "json" => Ok(Format::Json), $( stringify!($name) => { #[cfg(feature = $feature)] return Ok(Format::$variant); #[cfg(not(feature = $feature))] return Err(format!( "This version of tokei was compiled without \ any '{format}' serialization support, to enable serialization, \ reinstall tokei with the features flag. cargo install tokei --features {feature} If you want to enable all supported serialization formats, you can use the 'all' feature. cargo install tokei --features all\n", format = stringify!($name), feature = $feature) ); } ),+ format => Err(format!("{:?} is not a supported serialization format", format)), } } } ) } // The ordering of these determines the attempted order when parsing. supported_formats!( (cbor, "cbor", Cbor [serde_cbor, hex]) => |input| { hex::FromHex::from_hex(input) .map_err(|e: hex::FromHexError| >::from(e)) .and_then(|hex: Vec<_>| Ok(serde_cbor::from_slice(&hex)?)) }, |languages| serde_cbor::to_vec(&languages).map(hex::encode), (json, "json", Json [serde_json]) => serde_json::from_str, serde_json::to_string, (yaml, "yaml", Yaml [serde_yaml]) => serde_yaml::from_str, serde_yaml::to_string, ); pub fn add_input(input: &str, languages: &mut Languages) -> bool { use std::fs::File; use std::io::Read; let map = match File::open(input) { Ok(mut file) => { let contents = { let mut contents = String::new(); file.read_to_string(&mut contents) .expect("Couldn't read file"); contents }; convert_input(&contents) } Err(_) => { if input == "stdin" { let mut stdin = ::std::io::stdin(); let mut buffer = String::new(); let _ = stdin.read_to_string(&mut buffer); convert_input(&buffer) } else { convert_input(input) } } }; if let Some(map) = map { *languages += map; true } else { false } } fn convert_input(contents: &str) -> Option { self::Format::parse(&contents) } tokei-12.1.2/src/language/language_type.rs010064400007650000024000000227741377740532000166570ustar 00000000000000use std::{ borrow::Cow, fmt, fs::File, io::{self, BufRead, BufReader, Read}, path::{Path, PathBuf}, str::FromStr, }; use crate::{ config::Config, language::syntax::{FileContext, LanguageContext, SyntaxCounter}, stats::{CodeStats, Report}, utils::{ext::SliceExt, fs as fsutils}, }; use encoding_rs_io::DecodeReaderBytesBuilder; use grep_searcher::{LineIter, LineStep}; use rayon::prelude::*; use self::LanguageType::*; include!(concat!(env!("OUT_DIR"), "/language_type.rs")); impl LanguageType { /// Parses a given `Path` using the `LanguageType`. Returning `Report` /// on success and giving back ownership of PathBuf on error. pub fn parse(self, path: PathBuf, config: &Config) -> Result { let text = { let f = match File::open(&path) { Ok(f) => f, Err(e) => return Err((e, path)), }; let mut s = Vec::new(); let mut reader = DecodeReaderBytesBuilder::new().build(f); if let Err(e) = reader.read_to_end(&mut s) { return Err((e, path)); } s }; let mut stats = Report::new(path); stats += self.parse_from_slice(&text, config); Ok(stats) } /// Parses the text provided as the given `LanguageType`. pub fn parse_from_str>(self, text: A, config: &Config) -> CodeStats { self.parse_from_slice(text.as_ref().as_bytes(), config) } /// Parses the bytes provided as the given `LanguageType`. pub fn parse_from_slice>(self, text: A, config: &Config) -> CodeStats { let text = text.as_ref(); if self == LanguageType::Jupyter { return self .parse_jupyter(text.as_ref(), config) .unwrap_or_else(CodeStats::new); } let syntax = SyntaxCounter::new(self); if let Some(end) = syntax .shared .important_syntax .earliest_find(text) .and_then(|m| { // Get the position of the last line before the important // syntax. text[..=m.start()] .iter() .rev() .position(|&c| c == b'\n') .filter(|&p| p != 0) .map(|p| m.start() - p) }) { let (skippable_text, rest) = text.split_at(end + 1); let is_fortran = syntax.shared.is_fortran; let is_literate = syntax.shared.is_literate; let comments = syntax.shared.line_comments; trace!( "Using Simple Parse on {:?}", String::from_utf8_lossy(skippable_text) ); let parse_lines = move || self.parse_lines(config, rest, CodeStats::new(), syntax); let simple_parse = move || { LineIter::new(b'\n', skippable_text) .par_bridge() .map(|line| { // FORTRAN has a rule where it only counts as a comment if it's the // first character in the column, so removing starting whitespace // could cause a miscount. let line = if is_fortran { line } else { line.trim() }; if line.trim().is_empty() { (1, 0, 0) } else if is_literate || comments.iter().any(|c| line.starts_with(c.as_bytes())) { (0, 0, 1) } else { (0, 1, 0) } }) .reduce(|| (0, 0, 0), |a, b| (a.0 + b.0, a.1 + b.1, a.2 + b.2)) }; let (mut stats, (blanks, code, comments)) = rayon::join(parse_lines, simple_parse); stats.blanks += blanks; stats.code += code; stats.comments += comments; stats } else { self.parse_lines(config, text, CodeStats::new(), syntax) } } #[inline] fn parse_lines( self, config: &Config, lines: &[u8], mut stats: CodeStats, mut syntax: SyntaxCounter, ) -> CodeStats { let mut stepper = LineStep::new(b'\n', 0, lines.len()); while let Some((start, end)) = stepper.next(lines) { let line = &lines[start..end]; // FORTRAN has a rule where it only counts as a comment if it's the // first character in the column, so removing starting whitespace // could cause a miscount. let line = if syntax.shared.is_fortran { line } else { line.trim() }; trace!("{}", String::from_utf8_lossy(line)); if syntax.can_perform_single_line_analysis(line, &mut stats) { continue; } let started_in_comments = !syntax.stack.is_empty() || (config.treat_doc_strings_as_comments == Some(true) && syntax.quote.is_some() && syntax.quote_is_doc_quote); let ended_with_comments = match syntax.perform_multi_line_analysis(lines, start, end, config) { crate::language::syntax::AnalysisReport::Normal(end) => end, crate::language::syntax::AnalysisReport::ChildLanguage(FileContext { language, end, stats: blob, }) => { match language { LanguageContext::Markdown { balanced, language } => { // Add the lines for the code fences. stats.comments += if balanced { 2 } else { 1 }; // Add the code inside the fence to the stats. *stats.blobs.entry(language).or_default() += blob; } LanguageContext::Rust => { // Add all the markdown blobs. *stats.blobs.entry(LanguageType::Markdown).or_default() += blob; } LanguageContext::Html { language } => { stats.code += 1; // Add all the markdown blobs. *stats.blobs.entry(language).or_default() += blob; } } // Advance to after the language code and the delimiter.. stepper = LineStep::new(b'\n', end, lines.len()); continue; } }; trace!("{}", String::from_utf8_lossy(line)); if syntax.shared.is_literate || syntax.line_is_comment(line, config, ended_with_comments, started_in_comments) { stats.comments += 1; trace!("Comment No.{}", stats.comments); trace!("Was the Comment stack empty?: {}", !started_in_comments); } else { stats.code += 1; trace!("Code No.{}", stats.code); } } stats } fn parse_jupyter(&self, json: &[u8], config: &Config) -> Option { #[derive(Deserialize)] struct Jupyter { cells: Vec, metadata: JupyterMetadata, } #[derive(Clone, Copy, Deserialize, PartialEq, Eq)] #[serde(rename_all = "lowercase")] enum CellType { Markdown, Code, } #[derive(Deserialize)] struct JupyterCell { cell_type: CellType, source: Vec, } #[derive(Deserialize)] struct JupyterMetadata { kernelspec: serde_json::Value, language_info: serde_json::Value, } let jupyter: Jupyter = serde_json::from_slice(json).ok()?; let mut jupyter_stats = CodeStats::new(); let language = jupyter .metadata .kernelspec .get("language") .and_then(serde_json::Value::as_str) .and_then(|v| LanguageType::from_str(v).ok()) .or_else(|| { jupyter .metadata .language_info .get("file_extension") .and_then(serde_json::Value::as_str) .and_then(LanguageType::from_file_extension) }) .unwrap_or(LanguageType::Python); let iter = jupyter .cells .par_iter() .map(|cell| match cell.cell_type { CellType::Markdown => ( LanguageType::Markdown, LanguageType::Markdown.parse_from_str(cell.source.join(""), config), ), CellType::Code => ( language, language.parse_from_str(cell.source.join(""), config), ), }) .collect::>(); for (language, stats) in iter { *jupyter_stats.blobs.entry(language).or_default() += stats; } Some(jupyter_stats) } } #[cfg(test)] mod tests { use super::*; #[test] fn rust_allows_nested() { assert!(LanguageType::Rust.allows_nested()); } } tokei-12.1.2/src/language/language_type.tera.rs010064400007650000024000000345101367366712300176060ustar 00000000000000/// Represents a individual programming language. Can be used to provide /// information about the language, such as multi line comments, single line /// comments, string literal syntax, whether a given language allows nesting /// comments. #[derive(Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[non_exhaustive] pub enum LanguageType { {% for key, _ in languages -%} #[allow(missing_docs)] {{key}}, {% endfor %} } impl LanguageType { /// Returns the display name of a language. /// /// ``` /// # use tokei::*; /// let bash = LanguageType::Bash; /// /// assert_eq!(bash.name(), "BASH"); /// ``` pub fn name(self) -> &'static str { match self { {% for key, value in languages -%} {{key}} => {% if value.name %}"{{value.name}}"{% else %}"{{key}}"{% endif %}, {% endfor %} } } pub(crate) fn _is_blank(self) -> bool { match self { {% for key, v in languages -%} {{key}} => {{ v.blank | default(value=false) }}, {% endfor %} } } pub(crate) fn is_fortran(self) -> bool { self == LanguageType::FortranModern || self == LanguageType::FortranLegacy } /// Returns whether the language is "literate", meaning that it considered /// to primarily be documentation and is counted primarily as comments /// rather than procedural code. pub fn is_literate(self) -> bool { match self { {% for key, v in languages -%} {{key}} => {{ v.literate | default(value=false) }}, {% endfor %} } } /// Provides every variant in a Vec pub fn list() -> &'static [Self] { &[{% for key, _ in languages %}{{key}}, {%- endfor %}] } /// Returns the single line comments of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::Rust; /// assert_eq!(lang.line_comments(), &["//"]); /// ``` pub fn line_comments(self) -> &'static [&'static str] { match self { {% for key, value in languages -%} {{key}} => &[{% for item in value.line_comment | default(value=[]) %}"{{item}}",{% endfor %}], {% endfor %} } } /// Returns the single line comments of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::Rust; /// assert_eq!(lang.multi_line_comments(), &[("/*", "*/")]); /// ``` pub fn multi_line_comments(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {%- for items in value.multi_line_comments | default(value=[]) -%} ({% for item in items %}"{{item}}",{% endfor %}), {%- endfor -%} ], {% endfor %} } } /// Returns whether the language allows nested multi line comments. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::Rust; /// assert!(lang.allows_nested()); /// ``` pub fn allows_nested(self) -> bool { match self { {% for key, v in languages -%} {{key}} => {{ v.nested | default(value=false) }}, {% endfor %} } } /// Returns what nested comments the language has. (Currently only D has /// any of this type.) /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::D; /// assert_eq!(lang.nested_comments(), &[("/+", "+/")]); /// ``` pub fn nested_comments(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {%- for items in value.nested_comments | default(value=[]) -%} ({% for item in items %}"{{item}}",{% endfor %}), {%- endfor -%} ], {% endfor %} } } /// Returns the quotes of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::C; /// assert_eq!(lang.quotes(), &[("\"", "\"")]); /// ``` pub fn quotes(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {%- for items in value.quotes | default(value=[]) -%} ({% for item in items %}"{{item}}",{% endfor %}), {%- endfor -%} ], {% endfor %} } } /// Returns the verbatim quotes of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::CSharp; /// assert_eq!(lang.verbatim_quotes(), &[("@\"", "\"")]); /// ``` pub fn verbatim_quotes(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {%- for items in value.verbatim_quotes | default(value=[]) -%} ({% for item in items %}"{{item}}",{% endfor %}), {%- endfor -%} ], {% endfor %} } } /// Returns the doc quotes of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::Python; /// assert_eq!(lang.doc_quotes(), &[("\"\"\"", "\"\"\""), ("'''", "'''")]); /// ``` pub fn doc_quotes(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {% for items in value.doc_quotes | default(value=[])-%} ({% for item in items %}"{{item}}",{% endfor %}), {%- endfor %} ], {%- endfor %} } } /// Returns the shebang of a language. /// ``` /// use tokei::LanguageType; /// let lang = LanguageType::Bash; /// assert_eq!(lang.shebangs(), &["#!/bin/bash"]); /// ``` pub fn shebangs(self) -> &'static [&'static str] { match self { {% for key, lang in languages -%} {{key}} => &[{% for item in lang.shebangs | default(value=[]) %}"{{item}}",{% endfor %}], {% endfor %} } } pub(crate) fn any_multi_line_comments(self) -> &'static [(&'static str, &'static str)] { match self { {% for key, value in languages -%} {{key}} => &[ {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) -%} {%- set starting_nested_comments = value.nested_comments | default(value=[]) -%} {%- for item in starting_multi_line_comments | concat(with=starting_nested_comments) -%} ("{{item.0}}", "{{item.1}}"), {%- endfor -%} ], {% endfor %} } } pub(crate) fn any_comments(self) -> &'static [&'static str] { match self { {% for key, value in languages -%} {{key}} => &[ {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) -%} {%- set starting_nested_comments = value.nested_comments | default(value=[]) -%} {%- for item in starting_multi_line_comments | concat(with=starting_nested_comments) -%} "{{item.0}}", "{{item.1}}", {%- endfor -%} {%- for item in value.line_comment | default(value=[]) -%} "{{item}}", {%- endfor -%} ], {% endfor %} } } /// Returns the parts of syntax that determines whether tokei can skip large /// parts of analysis. pub fn important_syntax(self) -> &'static [&'static str] { match self { {% for key, value in languages -%} {%- set starting_quotes = value.quotes | default(value=[]) | map(attribute="0") -%} {%- set starting_doc_quotes = value.doc_quotes | default(value=[]) | map(attribute="0") -%} {%- set starting_multi_line_comments = value.multi_line_comments | default(value=[]) | map(attribute="0") -%} {%- set starting_nested_comments = value.nested_comments | default(value=[]) | map(attribute="0") -%} {%- set important_syntax = value.important_syntax | default(value=[]) -%} {{key}} => &[ {%- for item in starting_quotes | concat(with=starting_doc_quotes) | concat(with=starting_multi_line_comments) | concat(with=starting_nested_comments) | concat(with=important_syntax) -%} "{{item}}", {%- endfor -%} {%- for context in value.contexts | default(value=[]) -%} {% if value.kind == "html" %} "<{{context.tag}}", {% endif %} {%- endfor -%} ], {% endfor %} } } /// Get language from a file path. May open and read the file. /// /// ```no_run /// use tokei::{Config, LanguageType}; /// /// let rust = LanguageType::from_path("./main.rs", &Config::default()); /// /// assert_eq!(rust, Some(LanguageType::Rust)); /// ``` pub fn from_path>(entry: P, _config: &Config) -> Option { let entry = entry.as_ref(); if let Some(filename) = fsutils::get_filename(&entry) { match &*filename { {% for key, value in languages -%} {%- if value.filenames -%} {%- for item in value.filenames -%} | "{{item}}" {%- endfor -%} => return Some({{key}}), {% endif -%} {%- endfor %} _ => () } } match fsutils::get_extension(&entry) { Some(extension) => LanguageType::from_file_extension(extension.as_str()), None => LanguageType::from_shebang(&entry), } } /// Get language from a file extension. /// /// ```no_run /// use tokei::LanguageType; /// /// let rust = LanguageType::from_file_extension("rs"); /// /// assert_eq!(rust, Some(LanguageType::Rust)); /// ``` pub fn from_file_extension(extension: &str) -> Option { match extension { {% for key, value in languages -%} {%- if value.extensions -%} {%- for item in value.extensions %}| "{{item}}" {% endfor %}=> Some({{key}}), {% endif -%} {%- endfor %} extension => { warn!("Unknown extension: {}", extension); None }, } } /// Get language from its MIME type if available. /// /// ```no_run /// use tokei::LanguageType; /// /// let lang = LanguageType::from_mime("application/javascript"); /// /// assert_eq!(lang, Some(LanguageType::JavaScript)); /// ``` pub fn from_mime(mime: &str) -> Option { match mime { {% for key, value in languages -%} {%- if value.mime -%} {%- for item in value.mime %}| "{{item}}" {% endfor %}=> Some({{key}}), {% endif -%} {%- endfor %} _ => { warn!("Unknown MIME: {}", mime); None }, } } /// Get language from a shebang. May open and read the file. /// /// ```no_run /// use tokei::LanguageType; /// /// let rust = LanguageType::from_shebang("./main.rs"); /// /// assert_eq!(rust, Some(LanguageType::Rust)); /// ``` pub fn from_shebang>(entry: P) -> Option { let file = match File::open(entry) { Ok(file) => file, _ => return None, }; let mut buf = BufReader::new(file); let mut line = String::new(); let _ = buf.read_line(&mut line); let mut words = line.split_whitespace(); match words.next() { {# First match against any shebang paths, and then check if the language matches any found in the environment shebang path. #} {% for key, value in languages -%} {%- if value.shebangs %} {%- for item in value.shebangs %}| Some("{{item}}") {% endfor %}=> Some({{key}}), {% endif -%} {%- endfor %} Some("#!/usr/bin/env") => { if let Some(word) = words.next() { match word { {% for key, value in languages -%} {%- if value.env -%} {%- for item in value.env %}| "{{item}}" {% endfor %}=> Some({{key}}), {% endif -%} {%- endfor %} env => { warn!("Unknown environment: {:?}", env); None } } } else { None } } _ => None, } } } impl FromStr for LanguageType { type Err = &'static str; fn from_str(from: &str) -> Result { match &*from.to_lowercase() { {% for key, value in languages %} {% if value.name %}"{{value.name | lower}}"{% else %}"{{key | lower}}"{% endif %} => Ok({{key}}), {% endfor %} _ => Err("Language not found, please use `-l` to see all available\ languages."), } } } impl fmt::Display for LanguageType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.name()) } } impl<'a> From for Cow<'a, LanguageType> { fn from(from: LanguageType) -> Self { Cow::Owned(from) } } impl<'a> From<&'a LanguageType> for Cow<'a, LanguageType> { fn from(from: &'a LanguageType) -> Self { Cow::Borrowed(from) } } tokei-12.1.2/src/language/languages.rs010064400007650000024000000106111377074642100157670ustar 00000000000000use std::{ collections::{btree_map, BTreeMap}, iter::IntoIterator, ops::{AddAssign, Deref, DerefMut}, path::Path, }; use rayon::prelude::*; use crate::{ config::Config, language::{Language, LanguageType}, utils, }; /// A newtype representing a list of languages counted in the provided /// directory. /// ([_List of /// Languages_](https://github.com/XAMPPRocky/tokei#supported-languages)) #[derive(Debug, Default)] pub struct Languages { inner: BTreeMap, } impl serde::Serialize for Languages { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { self.inner.serialize(serializer) } } impl<'de> serde::Deserialize<'de> for Languages { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let map = <_>::deserialize(deserializer)?; Ok(Self::from_previous(map)) } } impl Languages { fn from_previous(map: BTreeMap) -> Self { use std::collections::btree_map::Entry::*; let mut _self = Self::new(); for (name, input_language) in map { match _self.entry(name) { Occupied(mut entry) => { *entry.get_mut() += input_language; } Vacant(entry) => { entry.insert(input_language); } } } _self } /// Populates the `Languages` struct with statistics about languages /// provided by [`Language`]. /// /// Takes a `&[&str]` of paths to recursively traverse, paths can be /// relative, absolute or glob paths. a second `&[&str]` of paths to ignore, /// these strings use the `.gitignore` syntax, such as `target` /// or `**/*.bk`. /// /// ```no_run /// use tokei::{Config, Languages}; /// /// let mut languages = Languages::new(); /// languages.get_statistics(&["."], &[".git", "target"], &Config::default()); /// ``` /// /// [`Language`]: struct.Language.html pub fn get_statistics>( &mut self, paths: &[A], ignored: &[&str], config: &Config, ) { utils::fs::get_all_files(paths, ignored, &mut self.inner, config); self.inner.par_iter_mut().for_each(|(_, l)| l.total()); } /// Constructs a new, Languages struct. Languages is always empty and does /// not allocate. /// /// ```rust /// # use tokei::*; /// let languages = Languages::new(); /// ``` pub fn new() -> Self { Languages::default() } /// Summary of the Languages struct. pub fn total(self: &Languages) -> Language { let mut total = Language::new(); for (ty, language) in self { total.comments += language.comments; total.blanks += language.blanks; total.code += language.code; total.inaccurate |= language.inaccurate; total.children.insert(*ty, language.reports.clone()); } total } } impl IntoIterator for Languages { type Item = as IntoIterator>::Item; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.inner.into_iter() } } impl<'a> IntoIterator for &'a Languages { type Item = (&'a LanguageType, &'a Language); type IntoIter = btree_map::Iter<'a, LanguageType, Language>; fn into_iter(self) -> Self::IntoIter { self.inner.iter() } } impl<'a> IntoIterator for &'a mut Languages { type Item = (&'a LanguageType, &'a mut Language); type IntoIter = btree_map::IterMut<'a, LanguageType, Language>; fn into_iter(self) -> Self::IntoIter { self.inner.iter_mut() } } impl AddAssign> for Languages { fn add_assign(&mut self, rhs: BTreeMap) { for (name, language) in rhs { if let Some(result) = self.inner.get_mut(&name) { *result += language; } } } } impl Deref for Languages { type Target = BTreeMap; fn deref(&self) -> &Self::Target { &self.inner } } impl DerefMut for Languages { fn deref_mut(&mut self) -> &mut BTreeMap { &mut self.inner } } tokei-12.1.2/src/language/mod.rs010064400007650000024000000123111367372413100145730ustar 00000000000000pub mod language_type; pub mod languages; mod syntax; use std::{collections::BTreeMap, mem, ops::AddAssign}; pub use self::{language_type::*, languages::Languages}; use crate::{ sort::Sort::{self, *}, stats::Report, }; /// A struct representing statistics about a single Language. #[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize)] pub struct Language { /// The total number of blank lines. pub blanks: usize, /// The total number of lines of code. pub code: usize, /// The total number of comments(both single, and multi-line) pub comments: usize, /// A collection of statistics of individual files. pub reports: Vec, /// A map of any languages found in the reports. pub children: BTreeMap>, /// Whether this language had problems with file parsing pub inaccurate: bool, } impl Language { /// Constructs a new empty Language with the comments provided. /// /// ``` /// # use tokei::*; /// let mut rust = Language::new(); /// ``` pub fn new() -> Self { Self::default() } /// Returns the total number of lines. #[inline] pub fn lines(&self) -> usize { self.blanks + self.code + self.comments } /// Add a `Report` to the Language. This will not update the totals in the /// Language struct. pub fn add_report(&mut self, report: Report) { for (lang, stats) in &report.stats.blobs { let mut new_report = Report::new(report.name.clone()); new_report.stats = stats.clone(); self.children.entry(*lang).or_default().push(new_report); } self.reports.push(report); } /// Marks this language as possibly not reflecting correct stats. #[inline] pub fn mark_inaccurate(&mut self) { self.inaccurate = true; } /// Creates a new `Language` from `self`, which is a summarised version /// of the language that doesn't contain any children. It will count /// non-blank lines in child languages as code unless the child language is /// considered "literate" then it will be counted as comments. pub fn summarise(&self) -> Language { let mut summary = self.clone(); for reports in self.children.values() { for stats in reports.iter().map(|r| r.stats.summarise()) { summary.comments += stats.comments; summary.code += stats.code; summary.blanks += stats.blanks; } } summary } /// Totals up the statistics of the `Stat` structs currently contained in /// the language. /// /// ```no_run /// use std::{collections::BTreeMap, path::PathBuf}; /// use tokei::Language; /// /// let mut language = Language::new(); /// /// // Add stats... /// /// assert_eq!(0, language.lines()); /// /// language.total(); /// /// assert_eq!(10, language.lines()); /// ``` pub fn total(&mut self) { let mut blanks = 0; let mut code = 0; let mut comments = 0; for report in &self.reports { blanks += report.stats.blanks; code += report.stats.code; comments += report.stats.comments; } self.blanks = blanks; self.code = code; self.comments = comments; } /// Checks if the language is empty. Empty meaning it doesn't have any /// statistics. /// /// ``` /// # use tokei::*; /// let rust = Language::new(); /// /// assert!(rust.is_empty()); /// ``` pub fn is_empty(&self) -> bool { self.code == 0 && self.comments == 0 && self.blanks == 0 && self.children.is_empty() } /// Sorts each of the `Report`s contained in the language based /// on what category is provided. /// /// ```no_run /// use std::{collections::BTreeMap, path::PathBuf}; /// use tokei::{Language, Sort}; /// /// let mut language = Language::new(); /// /// // Add stats... /// /// language.sort_by(Sort::Lines); /// assert_eq!(20, language.reports[0].stats.lines()); /// /// language.sort_by(Sort::Code); /// assert_eq!(8, language.reports[0].stats.code); /// ``` pub fn sort_by(&mut self, category: Sort) { match category { Blanks => self .reports .sort_by(|a, b| b.stats.blanks.cmp(&a.stats.blanks)), Comments => self .reports .sort_by(|a, b| b.stats.comments.cmp(&a.stats.comments)), Code => self.reports.sort_by(|a, b| b.stats.code.cmp(&a.stats.code)), Files => self.reports.sort_by(|a, b| a.name.cmp(&b.name)), Lines => self .reports .sort_by(|a, b| b.stats.lines().cmp(&a.stats.lines())), } } } impl AddAssign for Language { fn add_assign(&mut self, mut rhs: Self) { self.comments += rhs.comments; self.blanks += rhs.blanks; self.code += rhs.code; self.reports .extend(mem::replace(&mut rhs.reports, Vec::new())); self.children .extend(mem::replace(&mut rhs.children, BTreeMap::new())); self.inaccurate |= rhs.inaccurate } } tokei-12.1.2/src/language/syntax.rs010064400007650000024000000565121377740532000153560ustar 00000000000000use std::sync::Arc; use aho_corasick::{AhoCorasick, AhoCorasickBuilder}; use dashmap::DashMap; use grep_searcher::LineStep; use log::Level::Trace; use once_cell::sync::Lazy; use regex::bytes::Regex; use crate::{stats::CodeStats, utils::ext::SliceExt, Config, LanguageType}; /// Tracks the syntax of the language as well as the current state in the file. /// Current has what could be consider three types of mode. /// - `plain` mode: This is the normal state, blanks are counted as blanks, /// string literals can trigger `string` mode, and comments can trigger /// `comment` mode. /// - `string` mode: This when the state machine is current inside a string /// literal for a given language, comments cannot trigger `comment` mode while /// in `string` mode. /// - `comment` mode: This when the state machine is current inside a comment /// for a given language, strings cannot trigger `string` mode while in /// `comment` mode. #[derive(Clone, Debug)] pub(crate) struct SyntaxCounter { pub(crate) shared: Arc, pub(crate) quote: Option<&'static str>, pub(crate) quote_is_doc_quote: bool, pub(crate) stack: Vec<&'static str>, pub(crate) quote_is_verbatim: bool, } #[derive(Clone, Debug)] pub(crate) struct FileContext { pub(crate) language: LanguageContext, pub(crate) stats: CodeStats, pub(crate) end: usize, } impl FileContext { pub fn new(language: LanguageContext, end: usize, stats: CodeStats) -> Self { Self { language, stats, end, } } } #[derive(Clone, Debug)] pub(crate) enum LanguageContext { Html { language: LanguageType, }, Markdown { balanced: bool, language: LanguageType, }, Rust, } #[derive(Clone, Debug)] pub(crate) struct SharedMatchers { pub language: LanguageType, pub allows_nested: bool, pub doc_quotes: &'static [(&'static str, &'static str)], pub important_syntax: AhoCorasick, pub any_comments: &'static [&'static str], pub is_fortran: bool, pub is_literate: bool, pub line_comments: &'static [&'static str], pub any_multi_line_comments: &'static [(&'static str, &'static str)], pub multi_line_comments: &'static [(&'static str, &'static str)], pub nested_comments: &'static [(&'static str, &'static str)], pub string_literals: &'static [(&'static str, &'static str)], pub verbatim_string_literals: &'static [(&'static str, &'static str)], } impl SharedMatchers { pub fn new(language: LanguageType) -> Arc { static MATCHERS: Lazy>> = Lazy::new(DashMap::new); MATCHERS .entry(language) .or_insert_with(|| Arc::new(Self::init(language))) .value() .clone() } pub fn init(language: LanguageType) -> Self { fn init_corasick(pattern: &[&'static str], anchored: bool) -> AhoCorasick { let mut builder = AhoCorasickBuilder::new(); builder .anchored(anchored) .byte_classes(false) .dfa(true) .prefilter(true); builder.build_with_size(pattern).unwrap() } Self { language, allows_nested: language.allows_nested(), doc_quotes: language.doc_quotes(), is_fortran: language.is_fortran(), is_literate: language.is_literate(), important_syntax: init_corasick(language.important_syntax(), false), any_comments: language.any_comments(), line_comments: language.line_comments(), multi_line_comments: language.multi_line_comments(), any_multi_line_comments: language.any_multi_line_comments(), nested_comments: language.nested_comments(), string_literals: language.quotes(), verbatim_string_literals: language.verbatim_quotes(), } } } #[derive(Debug)] pub(crate) enum AnalysisReport { /// No child languages were found, contains a boolean representing whether /// the line ended with comments or not. Normal(bool), ChildLanguage(FileContext), } impl SyntaxCounter { pub(crate) fn new(language: LanguageType) -> Self { Self { shared: SharedMatchers::new(language), quote_is_doc_quote: false, quote_is_verbatim: false, stack: Vec::with_capacity(1), quote: None, } } /// Returns whether the syntax is currently in plain mode. pub(crate) fn is_plain_mode(&self) -> bool { self.quote.is_none() && self.stack.is_empty() } /// Returns whether the syntax is currently in string mode. pub(crate) fn _is_string_mode(&self) -> bool { self.quote.is_some() } /// Returns whether the syntax is currently in comment mode. pub(crate) fn _is_comment_mode(&self) -> bool { !self.stack.is_empty() } #[inline] pub(crate) fn parse_line_comment(&self, window: &[u8]) -> bool { if self.quote.is_some() || !self.stack.is_empty() { false } else if let Some(comment) = self .shared .line_comments .iter() .find(|c| window.starts_with(c.as_bytes())) { trace!("Start {:?}", comment); true } else { false } } /// Try to see if we can determine what a line is from examining the whole /// line at once. Returns `true` if sucessful. pub(crate) fn can_perform_single_line_analysis( &self, line: &[u8], stats: &mut crate::stats::CodeStats, ) -> bool { if self.is_plain_mode() { if line.trim().is_empty() { stats.blanks += 1; trace!("Blank No.{}", stats.blanks); return true; } else if !self.shared.important_syntax.is_match(line) { trace!("^ Skippable"); if self.shared.is_literate || self .shared .line_comments .iter() .any(|c| line.starts_with(c.as_bytes())) { stats.comments += 1; trace!("Comment No.{}", stats.comments); } else { stats.code += 1; trace!("Code No.{}", stats.code); } return true; } } false } pub(crate) fn perform_multi_line_analysis( &mut self, lines: &[u8], start: usize, end: usize, config: &Config, ) -> AnalysisReport { let mut ended_with_comments = false; let mut skip = 0; macro_rules! skip { ($skip:expr) => {{ skip = $skip - 1; }}; } for i in start..end { if skip != 0 { skip -= 1; continue; } let window = &lines[i..]; if window.trim().is_empty() { break; } ended_with_comments = false; let is_end_of_quote_or_multi_line = self .parse_end_of_quote(window) .or_else(|| self.parse_end_of_multi_line(window)); if let Some(skip_amount) = is_end_of_quote_or_multi_line { ended_with_comments = true; skip!(skip_amount); continue; } else if self.quote.is_some() { continue; } if let Some(child) = self.parse_context(lines, i, end, config) { return AnalysisReport::ChildLanguage(child); } let is_quote_or_multi_line = self .parse_quote(window) .or_else(|| self.parse_multi_line_comment(window)); if let Some(skip_amount) = is_quote_or_multi_line { skip!(skip_amount); continue; } if self.parse_line_comment(window) { ended_with_comments = true; break; } } AnalysisReport::Normal(ended_with_comments) } /// Performs a set of heuristics to determine whether a line is a comment or /// not. The procedure is as follows. /// /// - Yes/No: Counted as Comment /// /// 1. Check if we're in string mode /// 1. Check if string literal is a doc string and whether tokei has /// been configured to treat them as comments. /// - Yes: When the line starts with the doc string or when we are /// continuing from a previous line. /// - No: The string is a normal string literal or tokei isn't /// configured to count them as comments. /// 2. If we're not in string mode, check if we left it this on this line. /// - Yes: When we found a doc quote and we started in comments. /// 3. Yes: When the whole line is a comment e.g. `/* hello */` /// 4. Yes: When the previous line started a multi-line comment. /// 5. Yes: When the line starts with a comment. /// 6. No: Any other input. pub(crate) fn line_is_comment( &self, line: &[u8], config: &crate::Config, _ended_with_comments: bool, started_in_comments: bool, ) -> bool { let trimmed = line.trim(); let whole_line_is_comment = || { self.shared .line_comments .iter() .any(|c| trimmed.starts_with(c.as_bytes())) || self .shared .any_multi_line_comments .iter() .any(|(start, end)| { trimmed.starts_with(start.as_bytes()) && trimmed.ends_with(end.as_bytes()) }) }; let starts_with_comment = || { let quote = match self.stack.last() { Some(q) => q, _ => return false, }; self.shared .any_multi_line_comments .iter() .any(|(start, end)| end == quote && trimmed.starts_with(start.as_bytes())) }; // `Some(true)` in order to respect the current configuration. #[allow(clippy::if_same_then_else)] if self.quote.is_some() { if self.quote_is_doc_quote && config.treat_doc_strings_as_comments == Some(true) { self.quote.map_or(false, |q| line.starts_with(q.as_bytes())) || (self.quote.is_some()) } else { false } } else if self .shared .doc_quotes .iter() .any(|(_, e)| line.contains_slice(e.as_bytes())) && started_in_comments { true } else if (whole_line_is_comment)() { true } else if started_in_comments { true } else { (starts_with_comment)() } } #[inline] pub(crate) fn parse_context( &mut self, lines: &[u8], start: usize, end: usize, config: &Config, ) -> Option { use std::str::FromStr; // static TYPE_REGEX: Lazy = Lazy::new(|| Regex::new(r#"type="(.*)".*>"#).unwrap()); if self.quote.is_some() || !self.stack.is_empty() { return None; } match self.shared.language { LanguageType::Markdown | LanguageType::UnrealDeveloperMarkdown => { static STARTING_MARKDOWN_REGEX: Lazy = Lazy::new(|| Regex::new(r#"^```\S+\s"#).unwrap()); static ENDING_MARKDOWN_REGEX: Lazy = Lazy::new(|| Regex::new(r#"```\s?"#).unwrap()); if !lines[start..end].contains_slice(b"```") { return None; } let opening_fence = STARTING_MARKDOWN_REGEX.find(&lines[start..end])?; let start_of_code = start + opening_fence.end(); let closing_fence = ENDING_MARKDOWN_REGEX.find(&lines[start_of_code..]); if let Some(m) = &closing_fence { trace!("{:?}", String::from_utf8_lossy(m.as_bytes())) } let end_of_code = closing_fence .map(|fence| start_of_code + fence.start()) .unwrap_or_else(|| lines.len()); let end_of_code_block = closing_fence .map(|fence| start_of_code + fence.end()) .unwrap_or_else(|| lines.len()); let balanced = closing_fence.is_some(); let identifier = &opening_fence.as_bytes().trim()[3..]; let language = identifier .split(|&b| b == b',') .filter_map(|s| LanguageType::from_str(&String::from_utf8_lossy(s)).ok()) .next()?; trace!( "{} BLOCK: {:?}", language, String::from_utf8_lossy(&lines[start_of_code..end_of_code]) ); let stats = language.parse_from_slice(&lines[start_of_code..end_of_code].trim(), config); Some(FileContext::new( LanguageContext::Markdown { balanced, language }, end_of_code_block, stats, )) } LanguageType::Rust => { let rest = &lines[start..]; let comment_syntax = if rest.trim_start().starts_with(b"///") { b"///" } else if rest.trim_start().starts_with(b"//!") { b"//!" } else { return None; }; let mut stepper = LineStep::new(b'\n', start, lines.len()); let mut markdown = Vec::new(); let mut end_of_block = lines.len(); while let Some((start, end)) = stepper.next(lines) { if lines[start..].trim().starts_with(comment_syntax) { trace!("{}", String::from_utf8_lossy(&lines[start..end])); let line = lines[start..end].trim_start(); let stripped_line = &line[3.min(line.len())..]; markdown.extend_from_slice(stripped_line); end_of_block = end; } else { end_of_block = start; break; } } trace!("Markdown found: {:?}", String::from_utf8_lossy(&markdown)); let doc_block = LanguageType::Markdown.parse_from_slice(markdown.trim(), config); Some(FileContext::new( LanguageContext::Rust, end_of_block, doc_block, )) } #[allow(clippy::trivial_regex)] LanguageType::Html | LanguageType::RubyHtml | LanguageType::Svelte | LanguageType::Vue => { static START_SCRIPT: Lazy = Lazy::new(|| Regex::new(r#"^"#).unwrap()); static END_SCRIPT: Lazy = Lazy::new(|| Regex::new(r#""#).unwrap()); static START_STYLE: Lazy = Lazy::new(|| Regex::new(r#"^"#).unwrap()); static END_STYLE: Lazy = Lazy::new(|| Regex::new(r#""#).unwrap()); static START_TEMPLATE: Lazy = Lazy::new(|| Regex::new(r#"^"#).unwrap()); static END_TEMPLATE: Lazy = Lazy::new(|| Regex::new(r#""#).unwrap()); if let Some(captures) = START_SCRIPT.captures(&lines[start..end]) { let start_of_code = start + captures.get(0).unwrap().end(); let closing_tag = END_SCRIPT.find(&lines[start_of_code..])?; let end_of_code = start_of_code + closing_tag.start(); let language = captures .get(1) .and_then(|m| { LanguageType::from_mime(&String::from_utf8_lossy(m.as_bytes().trim())) }) .unwrap_or(LanguageType::JavaScript); let script_contents = &lines[start_of_code..end_of_code]; if script_contents.trim().is_empty() { return None; } let stats = language.parse_from_slice( script_contents.trim_first_and_last_line_of_whitespace(), config, ); Some(FileContext::new( LanguageContext::Html { language }, end_of_code, stats, )) } else if let Some(captures) = START_STYLE.captures(&lines[start..end]) { let start_of_code = start + captures.get(0).unwrap().end(); let closing_tag = END_STYLE.find(&lines[start_of_code..])?; let end_of_code = start_of_code + closing_tag.start(); let language = captures .get(1) .and_then(|m| { LanguageType::from_str( &String::from_utf8_lossy(m.as_bytes().trim()).to_lowercase(), ) .ok() }) .unwrap_or(LanguageType::Css); let style_contents = &lines[start_of_code..end_of_code]; if style_contents.trim().is_empty() { return None; } let stats = language.parse_from_slice( style_contents.trim_first_and_last_line_of_whitespace(), config, ); Some(FileContext::new( LanguageContext::Html { language }, end_of_code, stats, )) } else if let Some(captures) = START_TEMPLATE.captures(&lines[start..end]) { let start_of_code = start + captures.get(0).unwrap().end(); let closing_tag = END_TEMPLATE.find(&lines[start_of_code..])?; let end_of_code = start_of_code + closing_tag.start(); let language = captures .get(1) .and_then(|m| { LanguageType::from_str( &String::from_utf8_lossy(m.as_bytes().trim()).to_lowercase(), ) .ok() }) .unwrap_or(LanguageType::Html); let template_contents = &lines[start_of_code..end_of_code]; if template_contents.trim().is_empty() { return None; } let stats = language.parse_from_slice( template_contents.trim_first_and_last_line_of_whitespace(), config, ); Some(FileContext::new( LanguageContext::Html { language }, end_of_code, stats, )) } else { None } } _ => None, } } #[inline] pub(crate) fn parse_quote(&mut self, window: &[u8]) -> Option { if !self.stack.is_empty() { return None; } if let Some((start, end)) = self .shared .doc_quotes .iter() .find(|(s, _)| window.starts_with(s.as_bytes())) { trace!("Start Doc {:?}", start); self.quote = Some(end); self.quote_is_verbatim = false; self.quote_is_doc_quote = true; return Some(start.len()); } if let Some((start, end)) = self .shared .verbatim_string_literals .iter() .find(|(s, _)| window.starts_with(s.as_bytes())) { trace!("Start verbatim {:?}", start); self.quote = Some(end); self.quote_is_verbatim = true; self.quote_is_doc_quote = false; return Some(start.len()); } if let Some((start, end)) = self .shared .string_literals .iter() .find(|(s, _)| window.starts_with(s.as_bytes())) { trace!("Start {:?}", start); self.quote = Some(end); self.quote_is_verbatim = false; self.quote_is_doc_quote = false; return Some(start.len()); } None } #[inline] pub(crate) fn parse_end_of_quote(&mut self, window: &[u8]) -> Option { if self._is_string_mode() && window.starts_with(self.quote?.as_bytes()) { let quote = self.quote.take().unwrap(); trace!("End {:?}", quote); Some(quote.len()) } else if !self.quote_is_verbatim && window.starts_with(br"\") && self .shared .string_literals .iter() .any(|(start, _)| window[1..].starts_with(start.as_bytes())) { // Tell the state machine to skip the next character because it // has been escaped if the string isn't a verbatim string. Some(2) } else { None } } #[inline] pub(crate) fn parse_multi_line_comment(&mut self, window: &[u8]) -> Option { if self.quote.is_some() { return None; } let iter = self .shared .multi_line_comments .iter() .chain(self.shared.nested_comments); for &(start, end) in iter { if window.starts_with(start.as_bytes()) { if self.stack.is_empty() || self.shared.allows_nested || self.shared.nested_comments.contains(&(start, end)) { self.stack.push(end); if log_enabled!(Trace) && self.shared.allows_nested { trace!("Start nested {:?}", start); } else { trace!("Start {:?}", start); } } return Some(start.len()); } } None } #[inline] pub(crate) fn parse_end_of_multi_line(&mut self, window: &[u8]) -> Option { if self .stack .last() .map_or(false, |l| window.starts_with(l.as_bytes())) { let last = self.stack.pop().unwrap(); if log_enabled!(Trace) { if self.stack.is_empty() { trace!("End {:?}", last); } else { trace!("End {:?}. Still in comments.", last); } } Some(last.len()) } else { None } } } tokei-12.1.2/src/lib.rs010064400007650000024000000032021370554614300127770ustar 00000000000000//! # Tokei: Count your code quickly. //! //! A simple, efficient library for counting code in directories. This //! functionality is also provided as a //! [CLI utility](//github.com/XAMPPRocky/tokei). Tokei uses a small state //! machine rather than regular expressions found in other code counters. Tokei //! can accurately count a lot more edge cases such as nested comments, or //! comment syntax inside string literals. //! //! # Examples //! //! Gets the total lines of code from all rust files in current directory, //! and all subdirectories. //! //! ```no_run //! use std::collections::BTreeMap; //! use std::fs::File; //! use std::io::Read; //! //! use tokei::{Config, Languages, LanguageType}; //! //! // The paths to search. Accepts absolute, relative, and glob paths. //! let paths = &["src", "tests"]; //! // Exclude any path that contains any of these strings. //! let excluded = &["target"]; //! // `Config` allows you to configure what is searched and counted. //! let config = Config::default(); //! //! let mut languages = Languages::new(); //! languages.get_statistics(paths, excluded, &config); //! let rust = &languages[&LanguageType::Rust]; //! //! println!("Lines of code: {}", rust.code); //! ``` #![deny( trivial_casts, trivial_numeric_casts, unused_variables, unstable_features, unused_import_braces, missing_docs )] #[macro_use] extern crate log; #[macro_use] extern crate serde; #[macro_use] mod utils; mod config; mod language; mod sort; mod stats; pub use self::{ config::Config, language::{Language, LanguageType, Languages}, sort::Sort, stats::{find_char_boundary, CodeStats, Report}, }; tokei-12.1.2/src/main.rs010064400007650000024000000050351377740532000131630ustar 00000000000000#[macro_use] extern crate log; mod cli; mod cli_utils; mod input; use std::{error::Error, io, process}; use tokei::{Config, Languages, Sort}; use crate::{cli::Cli, cli_utils::*, input::*}; fn main() -> Result<(), Box> { let mut cli = Cli::from_args(); if cli.print_languages { Cli::print_supported_languages(); process::exit(0); } let config = cli.override_config(Config::from_config_files()); let mut languages = Languages::new(); if let Some(input) = cli.file_input() { if !add_input(input, &mut languages) { Cli::print_input_parse_failure(input); process::exit(1); } } let input = cli.input(); for path in &input { if ::std::fs::metadata(path).is_err() { eprintln!("Error: '{}' not found.", path); process::exit(1); } } let columns = cli .columns .or(config.columns) .or_else(|| { if cli.files { term_size::dimensions().map(|(w, _)| w) } else { None } }) .unwrap_or(FALLBACK_ROW_LEN) .max(FALLBACK_ROW_LEN); languages.get_statistics(&input, &cli.ignored_directories(), &config); if let Some(format) = cli.output { print!("{}", format.print(&languages).unwrap()); process::exit(0); } let mut printer = Printer::new( columns, cli.files, io::BufWriter::new(io::stdout()), cli.number_format, ); if languages.iter().any(|(_, lang)| lang.inaccurate) { printer.print_inaccuracy_warning()?; } printer.print_header()?; if let Some(sort_category) = cli.sort.or(config.sort) { for (_, ref mut language) in &mut languages { language.sort_by(sort_category) } let mut languages: Vec<_> = languages.iter().collect(); match sort_category { Sort::Blanks => languages.sort_by(|a, b| b.1.blanks.cmp(&a.1.blanks)), Sort::Comments => languages.sort_by(|a, b| b.1.comments.cmp(&a.1.comments)), Sort::Code => languages.sort_by(|a, b| b.1.code.cmp(&a.1.code)), Sort::Files => languages.sort_by(|a, b| b.1.reports.len().cmp(&a.1.reports.len())), Sort::Lines => languages.sort_by(|a, b| b.1.lines().cmp(&a.1.lines())), } printer.print_results(languages.into_iter(), cli.compact)? } else { printer.print_results(languages.iter(), cli.compact)? } printer.print_total(languages)?; Ok(()) } tokei-12.1.2/src/sort.rs010064400007650000024000000025141366566725000132350ustar 00000000000000use std::{borrow::Cow, str::FromStr}; use serde::de::{self, Deserialize, Deserializer}; /// Used for sorting languages. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum Sort { /// Sort by number blank lines. Blanks, /// Sort by number comments lines. Comments, /// Sort by number code lines. Code, /// Sort by number files lines. Files, /// Sort by number of lines. Lines, } impl FromStr for Sort { type Err = String; fn from_str(s: &str) -> Result { Ok(match s.to_lowercase().as_ref() { "blanks" => Sort::Blanks, "comments" => Sort::Comments, "code" => Sort::Code, "files" => Sort::Files, "lines" => Sort::Lines, s => return Err(format!("Unsupported sorting option: {}", s)), }) } } impl<'de> Deserialize<'de> for Sort { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { String::deserialize(deserializer)? .parse() .map_err(de::Error::custom) } } impl<'a> From for Cow<'a, Sort> { fn from(from: Sort) -> Self { Cow::Owned(from) } } impl<'a> From<&'a Sort> for Cow<'a, Sort> { fn from(from: &'a Sort) -> Self { Cow::Borrowed(from) } } tokei-12.1.2/src/stats.rs010064400007650000024000000071501370554614300133750ustar 00000000000000use std::{collections::BTreeMap, fmt, ops, path::PathBuf}; use crate::LanguageType; /// A struct representing stats about a single blob of code. #[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)] #[non_exhaustive] pub struct CodeStats { /// The blank lines in the blob. pub blanks: usize, /// The lines of code in the blob. pub code: usize, /// The lines of comments in the blob. pub comments: usize, /// Language blobs that were contained inside this blob. pub blobs: BTreeMap, } impl CodeStats { /// Creates a new blank `CodeStats`. pub fn new() -> Self { Self::default() } /// Get the total lines in a blob of code. pub fn lines(&self) -> usize { self.blanks + self.code + self.comments } /// Creates a new `CodeStats` from an existing one with all of the child /// blobs merged. pub fn summarise(&self) -> Self { let mut summary = self.clone(); for (_, stats) in std::mem::replace(&mut summary.blobs, BTreeMap::new()) { let child_summary = stats.summarise(); summary.blanks += child_summary.blanks; summary.comments += child_summary.comments; summary.code += child_summary.code; } summary } } impl ops::Add for CodeStats { type Output = Self; fn add(mut self, rhs: Self) -> Self::Output { self += rhs; self } } impl ops::AddAssign for CodeStats { fn add_assign(&mut self, rhs: Self) { self.blanks += rhs.blanks; self.code += rhs.code; self.comments += rhs.comments; for (language, stats) in rhs.blobs { *self.blobs.entry(language).or_default() += stats; } } } /// A struct representing the statistics of a file. #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct Report { /// The code statistics found in the file. pub stats: CodeStats, /// File name. pub name: PathBuf, } impl Report { /// Create a new `Report` from a [`PathBuf`]. /// /// [`PathBuf`]: //doc.rust-lang.org/std/path/struct.PathBuf.html pub fn new(name: PathBuf) -> Self { Report { name, ..Self::default() } } } impl ops::AddAssign for Report { fn add_assign(&mut self, rhs: CodeStats) { self.stats += rhs; } } #[doc(hidden)] pub fn find_char_boundary(s: &str, index: usize) -> usize { for i in 0..4 { if s.is_char_boundary(index + i) { return index + i; } } unreachable!(); } macro_rules! display_stats { ($f:expr, $this:expr, $name:expr, $max:expr) => { write!( $f, " {: 12} {:>12} {:>12} {:>12}", $name, $this.stats.lines(), $this.stats.code, $this.stats.comments, $this.stats.blanks, max = $max ) }; } impl fmt::Display for Report { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let name = self.name.to_string_lossy(); let name_length = name.len(); let max_len = f.width().unwrap_or(25); if name_length <= max_len { display_stats!(f, self, name, max_len) } else { let mut formatted = String::from("|"); // Add 1 to the index to account for the '|' we add to the output string let from = find_char_boundary(&name, name_length + 1 - max_len); formatted.push_str(&name[from..]); display_stats!(f, self, formatted, max_len) } } } tokei-12.1.2/src/utils/ext.rs010064400007650000024000000073711367464030500142040ustar 00000000000000//! Various extensions to Rust std types. pub(crate) trait AsciiExt { fn is_whitespace(self) -> bool; fn is_not_line_ending_whitespace(self) -> bool; fn is_line_ending_whitespace(self) -> bool; } impl AsciiExt for u8 { fn is_whitespace(self) -> bool { self == b' ' || (b'\x09'..=b'\x0d').contains(&self) } fn is_not_line_ending_whitespace(self) -> bool { self.is_whitespace() && !self.is_line_ending_whitespace() } fn is_line_ending_whitespace(self) -> bool { self == b'\n' } } pub(crate) trait SliceExt { fn trim_first_and_last_line_of_whitespace(&self) -> &Self; fn trim_start(&self) -> &Self; fn trim(&self) -> &Self; fn contains_slice(&self, needle: &Self) -> bool; } impl SliceExt for [u8] { fn trim_first_and_last_line_of_whitespace(&self) -> &Self { let start = self .iter() .position(|c| c.is_line_ending_whitespace() || !c.is_whitespace()) .map_or(0, |i| (i + 1).min(self.len().saturating_sub(1))); let end = self .iter() .rposition(|c| c.is_line_ending_whitespace() || !c.is_whitespace()) .map_or_else( || self.len(), |i| { if self[i.saturating_sub(1)] == b'\r' { i - 1 } else { i } }, ); if self[start..].is_empty() { return &[]; } &self[start..=end] } fn trim_start(&self) -> &Self { let length = self.len(); if length == 0 { return &self; } let start = match self.iter().position(|c| !c.is_whitespace()) { Some(start) => start, None => return &[], }; &self[start..] } fn trim(&self) -> &Self { let length = self.len(); if length == 0 { return &self; } let start = match self.iter().position(|c| !c.is_whitespace()) { Some(start) => start, None => return &[], }; let end = match self.iter().rposition(|c| !c.is_whitespace()) { Some(end) => end.max(start), _ => length, }; &self[start..=end] } fn contains_slice(&self, needle: &Self) -> bool { let self_length = self.len(); let needle_length = needle.len(); if needle_length == 0 || needle_length > self_length { return false; } else if needle_length == self_length { return self == needle; } for window in self.windows(needle_length) { if needle == window { return true; } } false } } #[cfg(test)] mod tests { use super::*; #[test] fn is_whitespace() { assert!(b' '.is_whitespace()); assert!(b'\r'.is_whitespace()); assert!(b'\n'.is_whitespace()); } #[test] fn trim() { assert!([b' ', b' ', b' '].trim().is_empty()); assert!([b' ', b'\r', b'\n'].trim().is_empty()); assert!([b'\n'].trim().is_empty()); assert!([].trim().is_empty()); assert_eq!([b'a', b'b'], [b'a', b'b'].trim()); assert_eq!([b'h', b'i'], [b' ', b'h', b'i'].trim()); assert_eq!([b'h', b'i'], [b'h', b'i', b' '].trim()); assert_eq!([b'h', b'i'], [b' ', b'h', b'i', b' '].trim()); } #[test] fn contains() { assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3, 4, 5])); assert!([1, 2, 3, 4, 5].contains_slice(&[1, 2, 3])); assert!([1, 2, 3, 4, 5].contains_slice(&[3, 4, 5])); assert!([1, 2, 3, 4, 5].contains_slice(&[2, 3, 4])); assert!(![1, 2, 3, 4, 5].contains_slice(&[])); } } tokei-12.1.2/src/utils/fs.rs010064400007650000024000000331171377740532000140110ustar 00000000000000use std::{collections::BTreeMap, path::Path}; use ignore::{overrides::OverrideBuilder, DirEntry, WalkBuilder, WalkState::Continue}; use rayon::prelude::*; use crate::{ config::Config, language::{Language, LanguageType}, }; const IGNORE_FILE: &str = ".tokeignore"; pub fn get_all_files>( paths: &[A], ignored_directories: &[&str], languages: &mut BTreeMap, config: &Config, ) { let languages = parking_lot::Mutex::new(languages); let (tx, rx) = crossbeam_channel::unbounded(); let mut paths = paths.iter(); let mut walker = WalkBuilder::new(paths.next().unwrap()); for path in paths { walker.add(path); } if !ignored_directories.is_empty() { let mut overrides = OverrideBuilder::new("."); for ignored in ignored_directories { rs_error!(overrides.add(&format!("!{}", ignored))); } walker.overrides(overrides.build().expect("Excludes provided were invalid")); } let ignore = config.no_ignore.map(|b| !b).unwrap_or(true); let ignore_dot = ignore && config.no_ignore_dot.map(|b| !b).unwrap_or(true); let ignore_vcs = ignore && config.no_ignore_vcs.map(|b| !b).unwrap_or(true); // Custom ignore files always work even if the `ignore` option is false, // so we only add if that option is not present. if ignore_dot { walker.add_custom_ignore_filename(IGNORE_FILE); } walker .git_exclude(ignore_vcs) .git_global(ignore_vcs) .git_ignore(ignore_vcs) .hidden(config.hidden.map(|b| !b).unwrap_or(true)) .ignore(ignore_dot) .parents(ignore && config.no_ignore_parent.map(|b| !b).unwrap_or(true)); walker.build_parallel().run(move || { let tx = tx.clone(); Box::new(move |entry| { let entry = match entry { Ok(entry) => entry, Err(error) => { use ignore::Error; if let Error::WithDepth { err: ref error, .. } = error { if let Error::WithPath { ref path, err: ref error, } = **error { error!("{} reading {}", error, path.display()); return Continue; } } error!("{}", error); return Continue; } }; if entry.file_type().map_or(false, |ft| ft.is_file()) { tx.send(entry).unwrap(); } Continue }) }); let rx_iter = rx .into_iter() .par_bridge() .filter_map(|e| LanguageType::from_path(e.path(), &config).map(|l| (e, l))); let process = |(entry, language): (DirEntry, LanguageType)| { let result = language.parse(entry.into_path(), &config); let mut lock = languages.lock(); let entry = lock.entry(language).or_insert_with(Language::new); match result { Ok(stats) => entry.add_report(stats), Err((error, path)) => { entry.mark_inaccurate(); error!("Error reading {}:\n{}", path.display(), error); } } }; if let Some(types) = config.types.as_deref() { rx_iter.filter(|(_, l)| types.contains(l)).for_each(process) } else { rx_iter.for_each(process) } } pub(crate) fn get_extension(path: &Path) -> Option { path.extension().map(|e| e.to_string_lossy().to_lowercase()) } pub(crate) fn get_filename(path: &Path) -> Option { path.file_name().map(|e| e.to_string_lossy().to_lowercase()) } #[cfg(test)] mod tests { use std::fs; use tempfile::TempDir; use super::IGNORE_FILE; use crate::{ config::Config, language::{languages::Languages, LanguageType}, }; const FILE_CONTENTS: &[u8] = &*b"fn main() {}"; const FILE_NAME: &str = "main.rs"; const IGNORE_PATTERN: &str = "*.rs"; const LANGUAGE: &LanguageType = &LanguageType::Rust; #[test] fn ignore_directory_with_extension() { let mut languages = Languages::new(); let tmp_dir = TempDir::new().expect("Couldn't create temp dir"); let path_name = tmp_dir.path().join("directory.rs"); fs::create_dir(&path_name).expect("Couldn't create directory.rs within temp"); super::get_all_files( &[tmp_dir.into_path().to_str().unwrap()], &[], &mut languages, &Config::default(), ); assert!(languages.get(LANGUAGE).is_none()); } #[test] fn hidden() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); fs::write(dir.path().join(".hidden.rs"), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.hidden = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_implies_dot() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); fs::write(dir.path().join(".ignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_implies_vcs_gitignore() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); git2::Repository::init(dir.path()).expect("Couldn't create git repo."); fs::write(dir.path().join(".gitignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_parent() { let parent_dir = TempDir::new().expect("Couldn't create temp dir."); let child_dir = parent_dir.path().join("child/"); let mut config = Config::default(); let mut languages = Languages::new(); fs::create_dir_all(&child_dir) .unwrap_or_else(|_| panic!("Couldn't create {:?}", child_dir)); fs::write(parent_dir.path().join(".ignore"), IGNORE_PATTERN) .expect("Couldn't create .gitignore."); fs::write(child_dir.join(FILE_NAME), FILE_CONTENTS).expect("Couldn't create child.rs"); super::get_all_files( &[child_dir.as_path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore_parent = Some(true); super::get_all_files( &[child_dir.as_path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_dot() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); fs::write(dir.path().join(".ignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore_dot = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_dot_still_vcs_gitignore() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); git2::Repository::init(dir.path()).expect("Couldn't create git repo."); fs::write(dir.path().join(".gitignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); config.no_ignore_dot = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); } #[test] fn no_ignore_dot_includes_custom_ignore() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); fs::write(dir.path().join(IGNORE_FILE), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore_dot = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_vcs_gitignore() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); git2::Repository::init(dir.path()).expect("Couldn't create git repo."); fs::write(dir.path().join(".gitignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore_vcs = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn no_ignore_vcs_gitignore_still_dot() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); fs::write(dir.path().join(".ignore"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); config.no_ignore_vcs = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); } #[test] fn no_ignore_vcs_gitexclude() { let dir = TempDir::new().expect("Couldn't create temp dir."); let mut config = Config::default(); let mut languages = Languages::new(); git2::Repository::init(dir.path()).expect("Couldn't create git repo."); fs::write(dir.path().join(".git/info/exclude"), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); config.no_ignore_vcs = Some(true); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } #[test] fn custom_ignore() { let dir = TempDir::new().expect("Couldn't create temp dir."); let config = Config::default(); let mut languages = Languages::new(); git2::Repository::init(dir.path()).expect("Couldn't create git repo."); fs::write(dir.path().join(IGNORE_FILE), IGNORE_PATTERN).unwrap(); fs::write(dir.path().join(FILE_NAME), FILE_CONTENTS).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_none()); fs::remove_file(dir.path().join(IGNORE_FILE)).unwrap(); super::get_all_files( &[dir.path().to_str().unwrap()], &[], &mut languages, &config, ); assert!(languages.get(LANGUAGE).is_some()); } } tokei-12.1.2/src/utils/macros.rs010064400007650000024000000041321362377312400146600ustar 00000000000000#![allow(unused_macros)] macro_rules! opt_warn { ($option:expr, $message:expr) => { match $option { Some(result) => result, None => { warn!($message); continue; } } }; } macro_rules! rs_warn { ($result:expr, $message: expr) => { match $result { Ok(result) => result, Err(error) => { warn!("{}", error); continue; } } }; } macro_rules! opt_error { ($option:expr, $message:expr) => { match $option { Some(result) => result, None => { error!($message); continue; } } }; } macro_rules! rs_error { ($result:expr) => { match $result { Ok(result) => result, Err(error) => { error!("{}", error); continue; } } }; } macro_rules! opt_ret_warn { ($option:expr, $message:expr) => { match $option { Some(result) => result, None => { warn!($message); return None; } } }; } macro_rules! rs_ret_warn { ($result:expr, $message: expr) => { match $result { Ok(result) => result, Err(error) => { warn!("{}", error); return None; } } }; } macro_rules! opt_ret_error { ($option:expr, $message:expr) => { match $option { Some(result) => result, None => { error!($message); return None; } } }; } macro_rules! rs_ret_error { ($result:expr) => { match $result { Ok(result) => result, Err(error) => { error!("{}", error); return None; } } }; } macro_rules! debug { ($fmt:expr) => (if cfg!(debug_assertions) {println!($fmt)}); ($fmt:expr, $($arg:tt)*) => (if cfg!(debug_assertions) {println!($fmt, $($arg)*)}); } tokei-12.1.2/src/utils/mod.rs010064400007650000024000000000711362377312400141510ustar 00000000000000#[macro_use] mod macros; pub(crate) mod ext; pub mod fs;