diff options
author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-05-20 16:10:21 +0200 |
---|---|---|
committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2024-05-20 16:14:26 +0200 |
commit | 368cb6b0d25db2ae23be42ad51584de059997e51 (patch) | |
tree | 3282e45d3ebced63c8498a47e83a255c35de620b /pkgs | |
parent | refactor(hm): Rename to `modules/home` (diff) | |
download | nixos-config-368cb6b0d25db2ae23be42ad51584de059997e51.tar.gz nixos-config-368cb6b0d25db2ae23be42ad51584de059997e51.zip |
refactor(sys): Modularize and move to `modules/system` or `pkgs`
Diffstat (limited to 'pkgs')
168 files changed, 14293 insertions, 0 deletions
diff --git a/pkgs/default.nix b/pkgs/default.nix new file mode 100644 index 00000000..38c58a95 --- /dev/null +++ b/pkgs/default.nix @@ -0,0 +1,24 @@ +{ + lib, + system, + overlays ? [], + sysLib, + homeConfig, + nixosConfig, +}: let + additionalPackages = (import ./pkgs) {inherit homeConfig nixosConfig sysLib;}; + complete_overlays = overlays ++ additionalPackages; +in { + # TODO: inheriting system here is discouraged, localSystem or hostSystem should be inspected + inherit system; + overlays = complete_overlays; + config = { + # TODO: this fails because of the root tempsize, which should be increased + # contentAddressedByDefault = true; + + allowUnfreePredicate = pkg: + builtins.elem (lib.getName pkg) [ + "pypemicro" # required by pynitrokey + ]; + }; +} diff --git a/pkgs/sources/comments/.envrc b/pkgs/sources/comments/.envrc new file mode 100644 index 00000000..2b5fbb29 --- /dev/null +++ b/pkgs/sources/comments/.envrc @@ -0,0 +1,4 @@ +use flake + +PATH_add ./target/debug +PATH_add ./target/release diff --git a/pkgs/sources/comments/.gitignore b/pkgs/sources/comments/.gitignore new file mode 100644 index 00000000..c84fa049 --- /dev/null +++ b/pkgs/sources/comments/.gitignore @@ -0,0 +1,3 @@ +# build dirs +/target +/result diff --git a/pkgs/sources/comments/Cargo.lock b/pkgs/sources/comments/Cargo.lock new file mode 100644 index 00000000..54f19c46 --- /dev/null +++ b/pkgs/sources/comments/Cargo.lock @@ -0,0 +1,446 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[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.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", +] + +[[package]] +name = "cli-log" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2ab00dc4c82ec28af25ac085aecc11ffeabf353755715a3113a7aa044ca5cc" +dependencies = [ + "chrono", + "file-size", + "log", + "proc-status", +] + +[[package]] +name = "comments" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "chrono-humanize", + "cli-log", + "log", + "regex", + "serde", + "serde_json", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "file-size" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9544f10105d33957765016b8a9baea7e689bf1f0f2f32c2fa2f568770c38d2b3" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-status" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e0c0ac915e7b76b47850ba4ffc377abde6c6ff9eeace61d0a89623db449712" +dependencies = [ + "thiserror", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/pkgs/sources/comments/Cargo.toml b/pkgs/sources/comments/Cargo.toml new file mode 100644 index 00000000..3ae3aa4c --- /dev/null +++ b/pkgs/sources/comments/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "comments" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +chrono = "0.4.38" +chrono-humanize = "0.2.3" +cli-log = "2.0.0" +log = "0.4.21" +regex = "1.10.4" +serde = { version = "1.0.200", features = ["derive"] } +serde_json = "1.0.116" diff --git a/pkgs/sources/comments/comments.nix b/pkgs/sources/comments/comments.nix new file mode 100644 index 00000000..e8a33bff --- /dev/null +++ b/pkgs/sources/comments/comments.nix @@ -0,0 +1,25 @@ +{ + lib, + rustPlatform, + makeWrapper, + less, + coreutils, +}: +rustPlatform.buildRustPackage { + pname = "comments"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + + nativeBuildInputs = [ + makeWrapper + ]; + + postInstall = '' + wrapProgram $out/bin/comments \ + --set PATH ${lib.makeBinPath [less coreutils]} + ''; +} diff --git a/pkgs/sources/comments/default.nix b/pkgs/sources/comments/default.nix new file mode 100644 index 00000000..6205dcbe --- /dev/null +++ b/pkgs/sources/comments/default.nix @@ -0,0 +1,18 @@ +[ + ( + final: prev: { + comments = import ./comments.nix { + inherit + (prev) + lib + makeWrapper + rustPlatform + # dependencies + + less + coreutils + ; + }; + } + ) +] diff --git a/pkgs/sources/comments/flake.lock b/pkgs/sources/comments/flake.lock new file mode 100644 index 00000000..50494465 --- /dev/null +++ b/pkgs/sources/comments/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1715087517, + "narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b211b392b8486ee79df6cdfb1157ad2133427a29", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/comments/flake.nix b/pkgs/sources/comments/flake.nix new file mode 100644 index 00000000..f5e44a65 --- /dev/null +++ b/pkgs/sources/comments/flake.nix @@ -0,0 +1,31 @@ +{ + description = "comments"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + }: (flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages."${system}"; + in { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + # rust stuff + cargo + clippy + rustc + rustfmt + + cargo-edit + cargo-expand + cargo-audit + ]; + }; + })); +} diff --git a/pkgs/sources/comments/src/info_json.rs b/pkgs/sources/comments/src/info_json.rs new file mode 100644 index 00000000..eca4fae3 --- /dev/null +++ b/pkgs/sources/comments/src/info_json.rs @@ -0,0 +1,223 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Deserializer}; + +#[derive(Debug, Deserialize)] +pub struct InfoJson { + pub id: String, + pub title: String, + pub formats: Vec<Format>, + pub thumbnails: Vec<ThumbNail>, + pub thumbnail: String, + pub description: String, + pub channel_id: String, + pub channel_url: String, + pub duration: u32, + pub view_count: u32, + pub age_limit: u32, + pub webpage_url: String, + pub categories: Vec<String>, + pub tags: Vec<String>, + pub playable_in_embed: bool, + pub live_status: String, + _format_sort_fields: Vec<String>, + pub automatic_captions: HashMap<String, Vec<Caption>>, + pub subtitles: Subtitles, + pub comment_count: u32, + pub like_count: u32, + pub channel: String, + pub channel_follower_count: u32, + pub channel_is_verified: Option<bool>, + pub uploader: String, + pub uploader_id: String, + pub uploader_url: String, + pub upload_date: String, + pub availability: String, + pub webpage_url_basename: String, + pub webpage_url_domain: String, + pub extractor: String, + pub extractor_key: String, + pub display_id: String, + pub fulltitle: String, + pub duration_string: String, + pub is_live: bool, + pub was_live: bool, + pub epoch: u32, + pub comments: Vec<Comment>, + pub sponsorblock_chapters: Option<Vec<SponsorblockChapter>>, + pub format: String, + pub format_id: String, + pub ext: String, + pub protocol: String, + pub language: Option<String>, + pub format_note: String, + pub filesize_approx: u64, + pub tbr: f64, + pub width: u32, + pub height: u32, + pub resolution: String, + pub fps: f64, + pub dynamic_range: String, + pub vcodec: String, + pub vbr: f64, + pub aspect_ratio: f64, + pub acodec: String, + pub abr: f64, + pub asr: u32, + pub audio_channels: u32, + _type: String, + _version: Version, +} + +#[derive(Debug, Deserialize)] +pub struct Subtitles {} + +#[derive(Debug, Deserialize)] +pub struct Version { + pub version: String, + pub release_git_head: String, + pub repository: String, +} + +#[derive(Debug, Deserialize)] +pub struct SponsorblockChapter {} + +#[derive(Debug, Deserialize, Clone)] +#[serde(from = "String")] +pub enum Parent { + Root, + Id(String), +} + +impl Parent { + pub fn id(&self) -> Option<&str> { + if let Self::Id(id) = self { + Some(id) + } else { + None + } + } +} + +impl From<String> for Parent { + fn from(value: String) -> Self { + if value == "root" { + Self::Root + } else { + Self::Id(value) + } + } +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(from = "String")] +pub struct Id { + pub id: String, +} +impl From<String> for Id { + fn from(value: String) -> Self { + Self { + // Take the last element if the string is split with dots, otherwise take the full id + id: value.split('.').last().unwrap_or(&value).to_owned(), + } + } +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Comment { + pub id: Id, + pub text: String, + #[serde(default = "zero")] + pub like_count: u32, + pub author_id: String, + #[serde(default = "unknown")] + pub author: String, + pub author_thumbnail: String, + pub parent: Parent, + #[serde(deserialize_with = "edited_from_time_text", alias = "_time_text")] + pub edited: bool, + // Can't also be deserialized, as it's already used in 'edited' + // _time_text: String, + pub timestamp: i64, + pub author_url: String, + pub author_is_uploader: bool, + pub is_favorited: bool, +} +fn unknown() -> String { + "<Unknown>".to_string() +} +fn zero() -> u32 { + 0 +} +fn edited_from_time_text<'de, D>(d: D) -> Result<bool, D::Error> +where + D: Deserializer<'de>, +{ + let s = String::deserialize(d)?; + if s.contains(" (edited)") { + Ok(true) + } else { + Ok(false) + } +} + +#[derive(Debug, Deserialize)] +pub struct Caption { + pub ext: String, + pub url: String, + pub name: Option<String>, + pub protocol: Option<String>, +} + +#[derive(Debug, Deserialize)] +pub struct ThumbNail { + pub url: String, + pub preference: i32, + pub id: String, + pub height: Option<u32>, + pub width: Option<u32>, + pub resolution: Option<String>, +} + +#[derive(Debug, Deserialize)] +pub struct Format { + pub format_id: String, + pub format_note: Option<String>, + pub ext: String, + pub protocol: String, + pub acodec: Option<String>, + pub vcodec: String, + pub url: String, + pub width: Option<u32>, + pub height: Option<u32>, + pub fps: Option<f64>, + pub rows: Option<u32>, + pub columns: Option<u32>, + pub fragments: Option<Vec<Fragment>>, + pub resolution: String, + pub aspect_ratio: Option<f64>, + pub http_headers: HttpHeader, + pub audio_ext: String, + pub video_ext: String, + pub vbr: Option<f64>, + pub abr: Option<f64>, + pub format: String, +} + +#[derive(Debug, Deserialize)] +pub struct HttpHeader { + #[serde(alias = "User-Agent")] + pub user_agent: String, + #[serde(alias = "Accept")] + pub accept: String, + #[serde(alias = "Accept-Language")] + pub accept_language: String, + #[serde(alias = "Sec-Fetch-Mode")] + pub sec_fetch_mode: String, +} + +#[derive(Debug, Deserialize)] +pub struct Fragment { + pub url: String, + pub duration: f64, +} diff --git a/pkgs/sources/comments/src/main.rs b/pkgs/sources/comments/src/main.rs new file mode 100644 index 00000000..6e4f72e9 --- /dev/null +++ b/pkgs/sources/comments/src/main.rs @@ -0,0 +1,322 @@ +use std::{ + env, + fmt::Display, + fs::{self, File}, + io::{BufReader, Write}, + mem, + path::PathBuf, + process::{Command, Stdio}, +}; + +use anyhow::Context; +use chrono::{Local, TimeZone}; +use chrono_humanize::{Accuracy, HumanTime, Tense}; +use info_json::{Comment, InfoJson, Parent}; +use regex::Regex; + +mod info_json; + +fn get_runtime_path(component: &'static str) -> anyhow::Result<PathBuf> { + let out: PathBuf = format!( + "{}/{}", + env::var("XDG_RUNTIME_DIR").expect("This should always exist"), + component + ) + .into(); + fs::create_dir_all(out.parent().expect("Parent should exist"))?; + Ok(out) +} + +const STATUS_PATH: &str = "ytcc/running"; +pub fn status_path() -> anyhow::Result<PathBuf> { + get_runtime_path(STATUS_PATH) +} + +#[derive(Debug, Clone)] +pub struct CommentExt { + pub value: Comment, + pub replies: Vec<CommentExt>, +} + +#[derive(Debug, Default)] +pub struct Comments { + vec: Vec<CommentExt>, +} + +impl Comments { + pub fn new() -> Self { + Self::default() + } + pub fn push(&mut self, value: CommentExt) { + self.vec.push(value); + } + pub fn get_mut(&mut self, key: &str) -> Option<&mut CommentExt> { + self.vec.iter_mut().filter(|c| c.value.id.id == key).last() + } + pub fn insert(&mut self, key: &str, value: CommentExt) { + let parent = self + .vec + .iter_mut() + .filter(|c| c.value.id.id == key) + .last() + .expect("One of these should exist"); + parent.push_reply(value); + } +} +impl CommentExt { + pub fn push_reply(&mut self, value: CommentExt) { + self.replies.push(value) + } + pub fn get_mut_reply(&mut self, key: &str) -> Option<&mut CommentExt> { + self.replies + .iter_mut() + .filter(|c| c.value.id.id == key) + .last() + } +} + +impl From<Comment> for CommentExt { + fn from(value: Comment) -> Self { + Self { + replies: vec![], + value, + } + } +} + +impl Display for Comments { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + macro_rules! c { + ($color:expr, $write:ident) => { + $write.write_str(concat!("\x1b[", $color, "m"))? + }; + } + + fn format( + comment: &CommentExt, + f: &mut std::fmt::Formatter<'_>, + ident_count: u32, + ) -> std::fmt::Result { + let ident = &(0..ident_count).map(|_| " ").collect::<String>(); + let value = &comment.value; + + f.write_str(ident)?; + + if value.author_is_uploader { + c!("91;1", f); + } else { + c!("35", f); + } + + f.write_str(&value.author)?; + c!("0", f); + if value.edited || value.is_favorited { + f.write_str("[")?; + if value.edited { + f.write_str("")?; + } + if value.edited && value.is_favorited { + f.write_str(" ")?; + } + if value.is_favorited { + f.write_str("")?; + } + f.write_str("]")?; + } + + c!("36;1", f); + write!( + f, + " {}", + HumanTime::from( + Local + .timestamp_opt(value.timestamp, 0) + .single() + .expect("This should be valid") + ) + .to_text_en(Accuracy::Rough, Tense::Past) + )?; + c!("0", f); + + // c!("31;1", f); + // f.write_fmt(format_args!(" [{}]", comment.value.like_count))?; + // c!("0", f); + + f.write_str(":\n")?; + f.write_str(ident)?; + + f.write_str(&value.text.replace('\n', &format!("\n{}", ident)))?; + f.write_str("\n")?; + + if !comment.replies.is_empty() { + let mut children = comment.replies.clone(); + children.sort_by(|a, b| a.value.timestamp.cmp(&b.value.timestamp)); + + for child in children { + format(&child, f, ident_count + 4)?; + } + } else { + f.write_str("\n")?; + } + + Ok(()) + } + + if !&self.vec.is_empty() { + let mut children = self.vec.clone(); + children.sort_by(|a, b| b.value.like_count.cmp(&a.value.like_count)); + + for child in children { + format(&child, f, 0)? + } + } + Ok(()) + } +} + +fn main() -> anyhow::Result<()> { + cli_log::init_cli_log!(); + let args: Option<String> = env::args().skip(1).last(); + let mut info_json: InfoJson = { + let status_path = if let Some(arg) = args { + PathBuf::from(arg) + } else { + status_path().context("Failed to get status path")? + }; + + let reader = + BufReader::new(File::open(&status_path).with_context(|| { + format!("Failed to open status file at {}", status_path.display()) + })?); + + serde_json::from_reader(reader)? + }; + + let base_comments = mem::take(&mut info_json.comments); + drop(info_json); + + let mut comments = Comments::new(); + base_comments.into_iter().for_each(|c| { + if let Parent::Id(id) = &c.parent { + comments.insert(&(id.clone()), CommentExt::from(c)); + } else { + comments.push(CommentExt::from(c)); + } + }); + + comments.vec.iter_mut().for_each(|comment| { + let replies = mem::take(&mut comment.replies); + let mut output_replies: Vec<CommentExt> = vec![]; + + let re = Regex::new(r"\u{200b}?(@[^\t\s]+)\u{200b}?").unwrap(); + for reply in replies { + if let Some(replyee_match) = re.captures(&reply.value.text){ + let full_match = replyee_match.get(0).expect("This always exists"); + let text = reply. + value. + text[0..full_match.start()] + .to_owned() + + + &reply + .value + .text[full_match.end()..]; + let text: &str = text.trim().trim_matches('\u{200b}'); + + let replyee = replyee_match.get(1).expect("This should exist").as_str(); + + + if let Some(parent) = output_replies + .iter_mut() + // .rev() + .flat_map(|com| &mut com.replies) + .flat_map(|com| &mut com.replies) + .flat_map(|com| &mut com.replies) + .filter(|com| com.value.author == replyee) + .last() + { + parent.replies.push(CommentExt::from(Comment { + text: text.to_owned(), + ..reply.value + })) + } else if let Some(parent) = output_replies + .iter_mut() + // .rev() + .flat_map(|com| &mut com.replies) + .flat_map(|com| &mut com.replies) + .filter(|com| com.value.author == replyee) + .last() + { + parent.replies.push(CommentExt::from(Comment { + text: text.to_owned(), + ..reply.value + })) + } else if let Some(parent) = output_replies + .iter_mut() + // .rev() + .flat_map(|com| &mut com.replies) + .filter(|com| com.value.author == replyee) + .last() + { + parent.replies.push(CommentExt::from(Comment { + text: text.to_owned(), + ..reply.value + })) + } else if let Some(parent) = output_replies.iter_mut() + // .rev() + .filter(|com| com.value.author == replyee) + .last() + { + parent.replies.push(CommentExt::from(Comment { + text: text.to_owned(), + ..reply.value + })) + } else { + eprintln!( + "Failed to find a parent for ('{}') both directly and via replies! The reply text was:\n'{}'\n", + replyee, + reply.value.text + ); + output_replies.push(reply); + } + } else { + output_replies.push(reply); + } + } + comment.replies = output_replies; + }); + + let mut less = Command::new("less") + .args(["--raw-control-chars"]) + .stdin(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .context("Failed to run less")?; + + let mut child = Command::new("fmt") + .args(["--uniform-spacing", "--split-only", "--width=90"]) + .stdin(Stdio::piped()) + .stderr(Stdio::inherit()) + .stdout(less.stdin.take().expect("Should be open")) + .spawn() + .context("Failed to run fmt")?; + + let mut stdin = child.stdin.take().context("Failed to open stdin")?; + std::thread::spawn(move || { + stdin + .write_all(comments.to_string().as_bytes()) + .expect("Should be able to write to stdin of fmt"); + }); + + let _ = less.wait().context("Failed to await less")?; + + Ok(()) +} + +#[cfg(test)] +mod test { + #[test] + fn test_string_replacement() { + let s = "A \n\nB\n\nC".to_owned(); + assert_eq!("A \n \n B\n \n C", s.replace('\n', "\n ")) + } +} diff --git a/pkgs/sources/comments/update.sh b/pkgs/sources/comments/update.sh new file mode 100755 index 00000000..e500bb23 --- /dev/null +++ b/pkgs/sources/comments/update.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +nix flake update + +[ "$1" = "upgrade" ] && cargo upgrade +cargo update + +# vim: ft=sh diff --git a/pkgs/sources/default.nix b/pkgs/sources/default.nix new file mode 100644 index 00000000..4668d735 --- /dev/null +++ b/pkgs/sources/default.nix @@ -0,0 +1,27 @@ +{ + homeConfig, + nixosConfig, + sysLib, +}: let + comments = import ./comments; + generate_firefox_extensions = import ./generate_moz_extension; + lf_make_map = import ./lf-make-map; + nvim_plugs = import ./plgs-pkgs; + scripts = import ./scripts {inherit sysLib homeConfig nixosConfig;}; + snap-sync-forked = (import ./snap-sync-forked) {inherit sysLib;}; + update_vim_plugins = import ./update_vim_plugins; + yt = import ./yt; + yts-grammar = import ./tree-sitter-yts; + + overlays = + comments + ++ generate_firefox_extensions + ++ lf_make_map + ++ nvim_plugs + ++ scripts + ++ snap-sync-forked + ++ update_vim_plugins + ++ yt + ++ yts-grammar; +in + overlays diff --git a/pkgs/sources/generate_moz_extension/.envrc b/pkgs/sources/generate_moz_extension/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/pkgs/sources/generate_moz_extension/.envrc @@ -0,0 +1 @@ +use flake diff --git a/pkgs/sources/generate_moz_extension/.gitignore b/pkgs/sources/generate_moz_extension/.gitignore new file mode 100644 index 00000000..f717ddd7 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/.gitignore @@ -0,0 +1,3 @@ +/target +/result +.direnv diff --git a/pkgs/sources/generate_moz_extension/Cargo.lock b/pkgs/sources/generate_moz_extension/Cargo.lock new file mode 100644 index 00000000..c0a83aa8 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/Cargo.lock @@ -0,0 +1,1275 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generate_extensions" +version = "0.1.0" +dependencies = [ + "anyhow", + "futures", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[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.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] diff --git a/pkgs/sources/generate_moz_extension/Cargo.toml b/pkgs/sources/generate_moz_extension/Cargo.toml new file mode 100644 index 00000000..e7d44db4 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "generate_extensions" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.83" +futures = "0.3.30" +reqwest = "0.12.4" +serde = { version = "1.0.201", features = ["derive"] } +serde_json = "1.0.117" +tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] } diff --git a/pkgs/sources/generate_moz_extension/default.nix b/pkgs/sources/generate_moz_extension/default.nix new file mode 100644 index 00000000..be734eee --- /dev/null +++ b/pkgs/sources/generate_moz_extension/default.nix @@ -0,0 +1,16 @@ +[ + ( + final: prev: { + generate_firefox_extensions = import ./generate_firefox_extensions.nix { + inherit + (prev) + rustPlatform + # Dependencies + + openssl + pkg-config + ; + }; + } + ) +] diff --git a/pkgs/sources/generate_moz_extension/examples/generate_extensions.sh b/pkgs/sources/generate_moz_extension/examples/generate_extensions.sh new file mode 100755 index 00000000..96802992 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/examples/generate_extensions.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +tmp=$(mktemp) +cat <<EOF | awk '!/^\s*#/' >"$tmp" + darkreader:navbar + keepassxc-browser:navbar + vhack-libredirect:navbar + # torproject-snowflake:navbar + tridactyl-vim:menupanel + ublock-origin:menupanel +EOF + +# The cat execution should be unquoted; +# shellcheck disable=SC2046 +cargo run -- $(cat "$tmp") + +rm "$tmp" diff --git a/pkgs/sources/generate_moz_extension/flake.lock b/pkgs/sources/generate_moz_extension/flake.lock new file mode 100644 index 00000000..741a8ad1 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/flake.lock @@ -0,0 +1,106 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1714864355, + "narHash": "sha256-uXNW6bapWFfkYIkK1EagydSrFMqycOYEDSq75GmUpjk=", + "owner": "ipetkov", + "repo": "crane", + "rev": "442a7a6152f49b907e73206dc8e1f46a61e8e873", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1715037484, + "narHash": "sha256-OUt8xQFmBU96Hmm4T9tOWTu4oCswCzoVl+pxSq/kiFc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ad7efee13e0d216bf29992311536fce1d3eefbef", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1715221036, + "narHash": "sha256-81EKOdlmT/4hZpImRlvMVPgmCcJYZjwlWbJese/XqUw=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "5c4bc8a0a70093a31a12509c5653c147f2310bd2", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/generate_moz_extension/flake.nix b/pkgs/sources/generate_moz_extension/flake.nix new file mode 100644 index 00000000..5575f90b --- /dev/null +++ b/pkgs/sources/generate_moz_extension/flake.nix @@ -0,0 +1,75 @@ +{ + description = "A simple way to query the mozialla api for extension data"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + }; + + outputs = { + self, + nixpkgs, + crane, + flake-utils, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + + rust-stable = pkgs.rust-bin.stable.latest.default; + rust-minimal = pkgs.rust-bin.stable.latest.minimal; + + craneLib = (crane.mkLib pkgs).overrideToolchain rust-minimal; + + buildInputs = [ + pkgs.openssl # needed for openssl + ]; + nativeBuildInputs = [ + pkgs.pkg-config # needed for openssl + ]; + + craneBuild = craneLib.buildPackage { + src = craneLib.cleanCargoSource ./.; + inherit buildInputs nativeBuildInputs; + + doCheck = true; + }; + in { + packages.default = craneBuild; + app.default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/generate_extensions"; + }; + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + cocogitto + + rust-stable + cargo-edit + ]; + inherit buildInputs nativeBuildInputs; + }; + }); +} +# vim: ts=2 + diff --git a/pkgs/sources/generate_moz_extension/generate_firefox_extensions.nix b/pkgs/sources/generate_moz_extension/generate_firefox_extensions.nix new file mode 100644 index 00000000..abd95c77 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/generate_firefox_extensions.nix @@ -0,0 +1,20 @@ +{ + rustPlatform, + openssl, + pkg-config, +}: +rustPlatform.buildRustPackage { + pname = "generate_firefox_extensions"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + buildInputs = [ + openssl # needed for openssl-sys crate + ]; + nativeBuildInputs = [ + pkg-config # needed for openssl dependency + ]; +} diff --git a/pkgs/sources/generate_moz_extension/res/generate_extensions.py b/pkgs/sources/generate_moz_extension/res/generate_extensions.py new file mode 100644 index 00000000..ee8cc966 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/res/generate_extensions.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# source: https://github.com/etu/nixconfig/blob/ba47d577c8bfb4a1c06927c34ece34118f4a0460/modules/graphical/firefox/generate.py + +from concurrent.futures import ThreadPoolExecutor +import json +import requests + +EXTENSIONS = sorted( + [ + "darkreader", + "firenvim", + "keepassxc-browser", + "simple-tab-groups", + ] +) + + +def index_ext(ext: str): + # print(f"Indexing {ext}...") + + resp = requests.get(f"https://addons.mozilla.org/api/v5/addons/addon/{ext}/").json() + rel = resp["current_version"] + + if not rel["file"]["hash"].startswith("sha256:"): + raise ValueError("Unhandled hash type") + + return { + "pname": ext, + "version": rel["version"], + "addonId": resp["guid"], + "url": rel["file"]["url"], + "sha256": rel["file"]["hash"], + } + + +if __name__ == "__main__": + # outfile = os.path.dirname(os.path.realpath(__file__)) + "/extensions.json" + + with ThreadPoolExecutor() as e: + extensions = {ext: e.submit(index_ext, ext) for ext in EXTENSIONS} + extensions = {k: v.result() for k, v in extensions.items()} + + # with open(outfile, "w") as f: + print(json.dumps(extensions, indent=2)) diff --git a/pkgs/sources/generate_moz_extension/res/reference.json b/pkgs/sources/generate_moz_extension/res/reference.json new file mode 100644 index 00000000..f46ea8ec --- /dev/null +++ b/pkgs/sources/generate_moz_extension/res/reference.json @@ -0,0 +1,30 @@ +{ + "darkreader": { + "pname": "darkreader", + "version": "4.9.62", + "addonId": "addon@darkreader.org", + "url": "https://addons.mozilla.org/firefox/downloads/file/4053589/darkreader-4.9.62.xpi", + "sha256": "sha256:e537a2cee45ed7c26f79ecd3ed362620e3f00d24c158532a58e163a63a3d60cc" + }, + "firenvim": { + "pname": "firenvim", + "version": "0.2.14", + "addonId": "firenvim@lacamb.re", + "url": "https://addons.mozilla.org/firefox/downloads/file/4026386/firenvim-0.2.14.xpi", + "sha256": "sha256:a8c495a59e30eaabbb3fcd188db9b5e28b40bffefe41a3f0fa22ecc58c80c2b6" + }, + "keepassxc-browser": { + "pname": "keepassxc-browser", + "version": "1.8.4", + "addonId": "keepassxc-browser@keepassxc.org", + "url": "https://addons.mozilla.org/firefox/downloads/file/4045866/keepassxc_browser-1.8.4.xpi", + "sha256": "sha256:cc39aa058cb8915cfc88424e2e1cebe3ccfc3f95d7bddb2abd0c4905d2b17719" + }, + "simple-tab-groups": { + "pname": "simple-tab-groups", + "version": "4.7.2.1", + "addonId": "simple-tab-groups@drive4ik", + "url": "https://addons.mozilla.org/firefox/downloads/file/3873608/simple_tab_groups-4.7.2.1.xpi", + "sha256": "sha256:75077589098ca62c00b86cf9554c6120bf8dc04c5f916fe26f84915f5147b2a4" + } +} diff --git a/pkgs/sources/generate_moz_extension/res/test.json b/pkgs/sources/generate_moz_extension/res/test.json new file mode 100644 index 00000000..daa1d19a --- /dev/null +++ b/pkgs/sources/generate_moz_extension/res/test.json @@ -0,0 +1,30 @@ +{ + "darkreader": { + "addon_id": "addon@darkreader.org", + "pname": "darkreader", + "sha256": "sha256:e537a2cee45ed7c26f79ecd3ed362620e3f00d24c158532a58e163a63a3d60cc", + "url": "https://addons.mozilla.org/firefox/downloads/file/4053589/darkreader-4.9.62.xpi", + "version": "4.9.62" + }, + "firenvim": { + "addon_id": "firenvim@lacamb.re", + "pname": "firenvim", + "sha256": "sha256:a8c495a59e30eaabbb3fcd188db9b5e28b40bffefe41a3f0fa22ecc58c80c2b6", + "url": "https://addons.mozilla.org/firefox/downloads/file/4026386/firenvim-0.2.14.xpi", + "version": "0.2.14" + }, + "keepassxc-browser": { + "addon_id": "keepassxc-browser@keepassxc.org", + "pname": "keepassxc-browser", + "sha256": "sha256:cc39aa058cb8915cfc88424e2e1cebe3ccfc3f95d7bddb2abd0c4905d2b17719", + "url": "https://addons.mozilla.org/firefox/downloads/file/4045866/keepassxc_browser-1.8.4.xpi", + "version": "1.8.4" + }, + "simple-tab-groups": { + "addon_id": "simple-tab-groups@drive4ik", + "pname": "simple-tab-groups", + "sha256": "sha256:75077589098ca62c00b86cf9554c6120bf8dc04c5f916fe26f84915f5147b2a4", + "url": "https://addons.mozilla.org/firefox/downloads/file/3873608/simple_tab_groups-4.7.2.1.xpi", + "version": "4.7.2.1" + } +} diff --git a/pkgs/sources/generate_moz_extension/src/main.rs b/pkgs/sources/generate_moz_extension/src/main.rs new file mode 100644 index 00000000..bde986a3 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/src/main.rs @@ -0,0 +1,138 @@ +use std::env::args; + +use anyhow::{bail, Context}; +use futures::StreamExt; +use reqwest::Client; +use serde_json::{json, Map, Value}; + +pub mod types; + +macro_rules! get_json_value { + ($key:expr, $json_value:ident, $type:ident, $get:ident) => { + match $json_value.get($key) { + Some(resp) => { + let resp = resp.to_owned(); + if resp.$type() { + resp.$get().expect( + "The should have been checked in the if guard, so unpacking here is fine", + ).to_owned() + } else { + bail!( + "Value {} => \n{}\n is not of type: {}", + $key, + resp, + stringify!($type) + ); + } + } + None => { + bail!( + "There seems to be no '{}' in your json data (json value: '{}')\n Has the api changend?", + $key, serde_json::to_string_pretty(&$json_value).expect("Will always work") + ); + } + } + }; +} + +use futures::stream::futures_unordered::FuturesUnordered; +use types::{Extension, InputExtension}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let mut extensions: Vec<InputExtension> = vec![]; + for input_extension in args() + .skip(1) + .map(|str| InputExtension::try_from(str)) + .collect::<Vec<anyhow::Result<InputExtension>>>() + { + extensions.push(input_extension?); + } + + let resulting_extensions = process_extensions(extensions).await?; + + let mut output = Map::new(); + for extension in resulting_extensions { + output.insert(extension.pname.clone(), json!(extension)); + } + + println!( + "{}", + serde_json::to_string_pretty(&serde_json::Value::Object(output)).expect( + "This is constructed from json, it should also be possible to serialize it again" + ) + ); + Ok(()) +} + +async fn process_extensions(extensions: Vec<InputExtension>) -> anyhow::Result<Vec<Extension>> { + let mut output = Vec::with_capacity(extensions.len()); + + let client = Client::new(); + for extension in extensions + .iter() + .map(|ext| { + let local_client = &client; + index_extension(ext, local_client) + }) + .collect::<FuturesUnordered<_>>() + .collect::<Vec<_>>() + .await + { + output.push(extension?); + } + Ok(output) +} + +async fn index_extension(extension: &InputExtension, client: &Client) -> anyhow::Result<Extension> { + let response = client + .get(format!( + "https://addons.mozilla.org/api/v5/addons/addon/{}", + extension, + )) + .send() + .await + .context("Accessing the mozzila extenios api failed with error: {e}")?; + + eprintln!("Indexing {} ({})...", extension, response.status()); + let response: Value = serde_json::from_str( + &response + .text() + .await + .context("Turning the response to text fail with error: {e}")?, + ) + .context("Deserializing the response failed! Error: {e}")?; + + if let Some(detail) = response.get("detail") { + if detail == "Not found." { + bail!("Your extension ('{}') was not found!", extension); + } + }; + + let release = { get_json_value!("current_version", response, is_object, as_object) }; + + #[allow(non_snake_case)] + let addonId = { get_json_value!("guid", response, is_string, as_str) }; + + let version = { get_json_value!("version", release, is_string, as_str) }; + let file = { get_json_value!("file", release, is_object, as_object) }; + + let url = { get_json_value!("url", file, is_string, as_str) }; + let sha256 = { + let hash = get_json_value!("hash", file, is_string, as_str); + if hash.starts_with("sha256:") { + hash + } else { + bail!("This hash type is unhandled: {}", hash); + } + }; + + Ok(Extension { + pname: extension.moz_name.clone(), + default_area: extension.default_area, + version, + addonId, + url, + sha256, + }) +} diff --git a/pkgs/sources/generate_moz_extension/src/types.rs b/pkgs/sources/generate_moz_extension/src/types.rs new file mode 100644 index 00000000..b830fe0d --- /dev/null +++ b/pkgs/sources/generate_moz_extension/src/types.rs @@ -0,0 +1,71 @@ +use std::fmt::Display; + +use anyhow::anyhow; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[allow(non_snake_case)] +pub struct Extension { + pub pname: String, + pub default_area: DefaultArea, + pub version: String, + pub addonId: String, + pub url: String, + pub sha256: String, +} + +#[derive(Debug, Clone)] +pub struct InputExtension { + pub moz_name: String, + pub default_area: DefaultArea, +} +#[derive(Debug, Clone, Copy, Deserialize, Serialize)] +#[allow(non_camel_case_types)] +pub enum DefaultArea { + navbar, + menupanel, +} + +impl Display for DefaultArea { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DefaultArea::navbar => f.write_str("navbar"), + DefaultArea::menupanel => f.write_str("menupanel"), + } + } +} + +impl TryFrom<&str> for DefaultArea { + type Error = anyhow::Error; + + fn try_from(value: &str) -> Result<Self, Self::Error> { + match value { + "navbar" => Ok(Self::navbar), + "menupanel" => Ok(Self::menupanel), + _ => Err(anyhow!( + "Your <default_area> needs to be one of 'navbar' or 'menupanel', but is: '{}'", + value + )), + } + } +} + +impl Display for InputExtension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.moz_name) + } +} +impl TryFrom<String> for InputExtension { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result<Self, Self::Error> { + if let Some((moz_name, default_area)) = value.split_once(':') { + Ok(Self { + moz_name: moz_name.to_owned(), + default_area: default_area.try_into()?, + }) + } else { + Err(anyhow!("Can't parse the input string as a InputExtension!\n Needs to be: '<moz_name>:<default_area>'")) + } + } +} diff --git a/pkgs/sources/generate_moz_extension/update.sh b/pkgs/sources/generate_moz_extension/update.sh new file mode 100755 index 00000000..e500bb23 --- /dev/null +++ b/pkgs/sources/generate_moz_extension/update.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +nix flake update + +[ "$1" = "upgrade" ] && cargo upgrade +cargo update + +# vim: ft=sh diff --git a/pkgs/sources/lf-make-map/.envrc b/pkgs/sources/lf-make-map/.envrc new file mode 100644 index 00000000..c8c56659 --- /dev/null +++ b/pkgs/sources/lf-make-map/.envrc @@ -0,0 +1,11 @@ +use flake || use nix +watch_file flake.nix + +PATH_add ./target/debug +PATH_add ./target/release +PATH_add ./scripts + +if on_git_branch; then + echo && git status --short --branch && + echo && git fetch --verbose +fi diff --git a/pkgs/sources/lf-make-map/.gitignore b/pkgs/sources/lf-make-map/.gitignore new file mode 100644 index 00000000..cb87f36f --- /dev/null +++ b/pkgs/sources/lf-make-map/.gitignore @@ -0,0 +1,6 @@ +# build +/target +/result + +# dev env +.direnv diff --git a/pkgs/sources/lf-make-map/Cargo.lock b/pkgs/sources/lf-make-map/Cargo.lock new file mode 100644 index 00000000..16af6e03 --- /dev/null +++ b/pkgs/sources/lf-make-map/Cargo.lock @@ -0,0 +1,505 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[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.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lf-make-map" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "log", + "stderrlog", + "walkdir", +] + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[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 = "stderrlog" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c910772f992ab17d32d6760e167d2353f4130ed50e796752689556af07dc6b" +dependencies = [ + "chrono", + "is-terminal", + "log", + "termcolor", + "thread_local", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/pkgs/sources/lf-make-map/Cargo.toml b/pkgs/sources/lf-make-map/Cargo.toml new file mode 100644 index 00000000..da9881fd --- /dev/null +++ b/pkgs/sources/lf-make-map/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "lf-make-map" +description = "An automatic lf cd mapping generator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.83" +clap = { version = "4.5.4", features = ["derive", "env"] } +log = "0.4.21" +stderrlog = "0.6.0" +walkdir = "2.5.0" diff --git a/pkgs/sources/lf-make-map/README.md b/pkgs/sources/lf-make-map/README.md new file mode 100644 index 00000000..0c57cede --- /dev/null +++ b/pkgs/sources/lf-make-map/README.md @@ -0,0 +1,12 @@ +# Lf make map + +> An automatic lf cd mapping generator + +Some text about the project. + +## Licence + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. diff --git a/pkgs/sources/lf-make-map/default.nix b/pkgs/sources/lf-make-map/default.nix new file mode 100644 index 00000000..8ff4c624 --- /dev/null +++ b/pkgs/sources/lf-make-map/default.nix @@ -0,0 +1,12 @@ +[ + ( + final: prev: { + lf-make-map = import ./lf_make_map.nix { + inherit + (prev) + rustPlatform + ; + }; + } + ) +] diff --git a/pkgs/sources/lf-make-map/flake.lock b/pkgs/sources/lf-make-map/flake.lock new file mode 100644 index 00000000..611392df --- /dev/null +++ b/pkgs/sources/lf-make-map/flake.lock @@ -0,0 +1,147 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1714864355, + "narHash": "sha256-uXNW6bapWFfkYIkK1EagydSrFMqycOYEDSq75GmUpjk=", + "owner": "ipetkov", + "repo": "crane", + "rev": "442a7a6152f49b907e73206dc8e1f46a61e8e873", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": [ + "systems" + ] + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1715037484, + "narHash": "sha256-OUt8xQFmBU96Hmm4T9tOWTu4oCswCzoVl+pxSq/kiFc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "ad7efee13e0d216bf29992311536fce1d3eefbef", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay", + "systems": "systems", + "treefmt-nix": "treefmt-nix" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1715221036, + "narHash": "sha256-81EKOdlmT/4hZpImRlvMVPgmCcJYZjwlWbJese/XqUw=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "5c4bc8a0a70093a31a12509c5653c147f2310bd2", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1680978846, + "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=", + "owner": "nix-systems", + "repo": "x86_64-linux", + "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "x86_64-linux", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1714058656, + "narHash": "sha256-Qv4RBm4LKuO4fNOfx9wl40W2rBbv5u5m+whxRYUMiaA=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "c6aaf729f34a36c445618580a9f95a48f5e4e03f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/lf-make-map/flake.nix b/pkgs/sources/lf-make-map/flake.nix new file mode 100644 index 00000000..dc8c24cc --- /dev/null +++ b/pkgs/sources/lf-make-map/flake.nix @@ -0,0 +1,125 @@ +{ + description = "An automatic lf cd mapping generator"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + + crane = { + url = "github:ipetkov/crane"; + inputs = { + nixpkgs.follows = "nixpkgs"; + }; + }; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs = { + nixpkgs.follows = "nixpkgs"; + flake-utils.follows = "flake-utils"; + }; + }; + + # inputs for following + systems = { + url = "github:nix-systems/x86_64-linux"; # only evaluate for this system + }; + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + flake-utils = { + url = "github:numtide/flake-utils"; + inputs = { + systems.follows = "systems"; + }; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + treefmt-nix, + crane, + rust-overlay, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [(import rust-overlay)]; + }; + + nightly = false; + rust_minimal = + if nightly + then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal) + else pkgs.rust-bin.stable.latest.minimal; + rust_default = + if nightly + then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default) + else pkgs.rust-bin.stable.latest.default; + + cargo_toml = craneLib.cleanCargoToml {cargoToml = ./Cargo.toml;}; + pname = cargo_toml.package.name; + + craneLib = (crane.mkLib pkgs).overrideToolchain rust_minimal; + craneBuild = craneLib.buildPackage { + src = craneLib.cleanCargoSource ./.; + + doCheck = true; + }; + + manual = pkgs.stdenv.mkDerivation { + name = "${pname}-manual"; + inherit (cargo_toml.package) version; + + src = ./docs; + nativeBuildInputs = with pkgs; [pandoc]; + + buildPhase = '' + mkdir --parents $out/docs; + + pandoc "./${pname}.1.md" -s -t man > $out/docs/${pname}.1 + ''; + + installPhase = '' + install -D $out/docs/${pname}.1 $out/share/man/man1/${pname}; + ''; + }; + + treefmtEval = import ./treefmt.nix {inherit treefmt-nix pkgs;}; + in { + packages.default = pkgs.symlinkJoin { + inherit (cargo_toml.package) name; + + paths = [manual craneBuild]; + }; + + checks = { + inherit craneBuild; + formatting = treefmtEval.config.build.check self; + }; + + formatter = treefmtEval.config.build.wrapper; + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + cocogitto + + rust_default + cargo-edit + + licensure + ]; + }; + }); +} +# vim: ts=2 + diff --git a/pkgs/sources/lf-make-map/lf_make_map.nix b/pkgs/sources/lf-make-map/lf_make_map.nix new file mode 100644 index 00000000..afb067b8 --- /dev/null +++ b/pkgs/sources/lf-make-map/lf_make_map.nix @@ -0,0 +1,10 @@ +{rustPlatform}: +rustPlatform.buildRustPackage { + pname = "lf-make-map"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; +} diff --git a/pkgs/sources/lf-make-map/src/cli.rs b/pkgs/sources/lf-make-map/src/cli.rs new file mode 100644 index 00000000..a398e451 --- /dev/null +++ b/pkgs/sources/lf-make-map/src/cli.rs @@ -0,0 +1,49 @@ +use std::path::PathBuf; + +use clap::{ArgAction, Parser, Subcommand}; + +/// An automatic lf cd mapping generator +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +#[command(next_line_help = true)] +pub struct Args { + /// The directory to treat as home + #[arg(long, short = 'n', env = "HOME")] + pub home_name: PathBuf, + + /// The number of directories to generate mappings for, starting from each `relevant_directory` + #[arg(long, short, default_value = "2")] + pub depth: usize, + + /// Increase message verbosity + #[arg(long="verbose", short = 'v', action = ArgAction::Count)] + pub verbosity: u8, + + /// Silence all output + #[arg(long, short = 'q')] + pub quiet: bool, + + #[command(subcommand)] + pub command: Command, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + /// Visualize the generated mappings in a tree + Visualize { + #[command(flatten)] + options: CommandOptions, + }, + + /// Output the generated mappings in a format suitable for the lf config file + Generate { + #[command(flatten)] + options: CommandOptions, + }, +} + +#[derive(Debug, Parser)] +pub struct CommandOptions { + /// The directories to generate mappings for + pub relevant_directories: Vec<PathBuf>, +} diff --git a/pkgs/sources/lf-make-map/src/main.rs b/pkgs/sources/lf-make-map/src/main.rs new file mode 100644 index 00000000..aaf79b20 --- /dev/null +++ b/pkgs/sources/lf-make-map/src/main.rs @@ -0,0 +1,229 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; +use clap::Parser; +use cli::{Args, Command}; +use log::trace; +use mapping::map_tree::MappingTree; +use walkdir::{DirEntry, WalkDir}; + +use crate::mapping::MapKey; + +mod cli; +mod mapping; + +fn main() -> anyhow::Result<()> { + let args = Args::parse(); + + stderrlog::new() + .module(module_path!()) + .quiet(args.quiet) + .show_module_names(false) + .color(stderrlog::ColorChoice::Auto) + .verbosity(args.verbosity as usize) + .timestamp(stderrlog::Timestamp::Off) + .init()?; + + let mut mappings = MappingTree::new(); + + let relevant_directories = match &args.command { + Command::Visualize { options } => &options.relevant_directories, + Command::Generate { options } => &options.relevant_directories, + }; + + for dir in relevant_directories { + trace!("Processing '{}'..", dir.display()); + let path = strip_path(&dir, &args.home_name)?; + + mappings + .include(path_to_str(path)?) + .with_context(|| format!("Failed to include path: '{}'", path.display()))?; + } + + let home = path_to_str(&args.home_name)?.to_owned(); + + let mut current_depth = 1; + while current_depth != args.depth { + for (key, value) in mappings.iter(false) { + trace!( + "Adding to child ('{}' -> '{}')", + MapKey::display(&key), + value + ); + + let mut local_mappings = MappingTree::new(); + for dir in WalkDir::new(extend(&home, &value)?) + .min_depth(1) + .max_depth(1) + .into_iter() + .filter_entry(|e| is_dir(e) && !is_hidden(e)) + { + let directory = dir + .with_context(|| format!("Failed to read dir ('{}')", home.clone() + &value))?; + let path_to_strip = &PathBuf::from(extend(&home, &value)?); + let path = strip_path(&directory.path(), &path_to_strip)?; + trace!( + "Including: '{}' (after stripping '{}' from '{}' -> '{}' + '/' + '{}')", + path.display(), + directory.path().display(), + path_to_strip.display(), + home, + value + ); + + let gen_key = MapKey::new_ones_from_path(path_to_str(path)?, 1); + local_mappings + .insert( + &gen_key, + path_to_str(strip_path(&directory.path(), &PathBuf::from(&home))?)?, + ) + .with_context(|| format!("Failed to include path: '{}'", path.display()))?; + } + + trace!("{}", local_mappings); + + trace!( + "'{}' -> '{:#?}'", + MapKey::display(&key), + local_mappings.root_node() + ); + mappings.interleave(&key, local_mappings.root_node().to_owned())?; + } + current_depth += 1; + } + + match args.command { + Command::Visualize { .. } => println!("{}", mappings), + Command::Generate { .. } => println!("{}", mappings.to_lf_mappings(args.home_name)), + } + + Ok(()) +} + +fn extend(base: &str, value: &str) -> Result<String> { + let base_path = PathBuf::from(base); + let value_path = PathBuf::from(value); + + Ok(path_to_str(&base_path.join(&value_path))?.to_owned()) +} + +fn is_hidden(entry: &DirEntry) -> bool { + entry + .file_name() + .to_str() + .map(|s| s.starts_with(".")) + .unwrap_or(false) +} + +fn is_dir(entry: &DirEntry) -> bool { + entry.file_type().is_dir() +} + +fn strip_path<'a>(path: &'a Path, to_strip: &Path) -> Result<&'a Path> { + path.strip_prefix(&to_strip).with_context(|| { + format!( + "'{}' is not under the specified home path ('{}')!", + path.display(), + to_strip.display() + ) + }) +} + +fn path_to_str(path: &Path) -> Result<&str> { + path.to_str().with_context(|| { + format!( + "\ +Can't derive a keymapping from path: '{}' \ +because it can't be turned to a string +", + path.display() + ) + }) +} + +// fn gen_lf_mappings(home_name: PathBuf, char_num: usize, rel_dirs: Vec<PathBuf>) { +// let mut mappings_vec = vec![]; +// let mut index_counter = 0; +// rel_dirs.iter().for_each(|rel_dir| { +// mappings_vec.push(vec![Mapping::new( +// &gen_hot_key(rel_dir, rel_dir, char_num), +// rel_dir, +// rel_dir, +// None, +// )]); +// get_dir(rel_dir.to_owned()).iter().for_each(|path| { +// mappings_vec[index_counter].push(Mapping::new( +// &gen_hot_key( +// path, +// path.parent().expect("All paths here should have parents"), +// char_num, +// ), +// path, +// &path +// .parent() +// .expect("All paths here should have parents") +// .to_owned(), +// None, +// )); +// }); +// index_counter += 1; +// }); +// print_mappings(&mappings_vec, home_name); +// mappings_vec +// .into_iter() +// .for_each(|rel_dir_mapping: Vec<Mapping>| { +// let mut hash_map = sort_mapping_by_hot_key(rel_dir_mapping.clone()); +// //dbg!(hash_map); +// hash_map.insert("gsi".to_owned(), vec![rel_dir_mapping[0].clone()]); +// }); +// } +// +// fn sort_mapping_by_hot_key(mut mappings: Vec<Mapping>) -> HashMap<String, Vec<Mapping>> { +// mappings.sort_by_key(|mapping| mapping.hot_key.clone()); +// +// let mut filtered_mappings: HashMap<String, Vec<Mapping>> = HashMap::new(); +// mappings.iter().for_each(|mapping| { +// filtered_mappings.insert(mapping.hot_key.clone(), vec![]); +// }); +// //dbg!(&mappings); +// +// let mut index_counter = 1; +// mappings.iter().for_each(|mapping| { +// if mappings.len() > index_counter { +// let next_mapping = &mappings[index_counter]; +// let vec = filtered_mappings +// .get_mut(&mapping.hot_key) +// .expect("This existst as it has been initialized"); +// +// if &next_mapping.hot_key == &mapping.hot_key { +// vec.push(mapping.clone()); +// vec.push(next_mapping.clone()); +// } else { +// vec.push(mapping.clone()); +// } +// +// let new_vec = vec.to_owned(); +// filtered_mappings.insert(mapping.hot_key.to_owned(), new_vec); +// } +// +// index_counter += 1; +// }); +// filtered_mappings +// } +// +// fn print_mappings(mappings: &Vec<Vec<Mapping>>, home_name: PathBuf) { +// for mapping in mappings { +// mapping.iter().for_each(|map| { +// println!( +// "{} = \"cd {}\";", +// map.hot_key, +// map.path +// .display() +// .to_string() +// .replace(home_name.to_str().expect("This should be UTF-8"), "~") +// ); +// }); +// +// println!("# -------------"); +// } +// } diff --git a/pkgs/sources/lf-make-map/src/mapping/map_tree/display.rs b/pkgs/sources/lf-make-map/src/mapping/map_tree/display.rs new file mode 100644 index 00000000..65302e1e --- /dev/null +++ b/pkgs/sources/lf-make-map/src/mapping/map_tree/display.rs @@ -0,0 +1,91 @@ +use std::fmt::Display; + +use crate::mapping::{ + map_tree::{Node, NodeValue}, + MapKey, +}; + +use super::MappingTree; + +impl Display for MappingTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn write_node( + f: &mut std::fmt::Formatter<'_>, + node: &Node, + indention: String, + location: Vec<MapKey>, + is_last: bool, + is_root: bool, + ) -> std::fmt::Result { + let node_value = match &node.value { + NodeValue::Parent { children: _ } => "<Parent>".to_owned(), + NodeValue::Child { path, extandable } => { + path.to_owned() + if *extandable { " [exten.]" } else { " [stop]" } + } + }; + + let new_idention = indention.clone() + + if is_root { + "" + } else { + match is_last { + true => " ", + false => "│ ", + } + }; + + let bullet = match is_last { + true => String::from("└── "), + false => String::from("├── "), + }; + + if is_root { + write!(f, ": {}\n", node_value)?; + } else { + write!( + f, + "{}{}\x1b[1;33m{}\x1b[0m: {}\n", + indention, + bullet, + MapKey::display(&location), + node_value, + )?; + }; + + match &node.value { + NodeValue::Parent { children } => { + let mut children_vec: Vec<(&MapKey, &Node)> = children.iter().collect(); + children_vec.sort_by(|(a, _), (b, _)| a.key.cmp(&b.key)); + + let mut counter = 1; + for (key, child) in &children_vec { + let mut new_location = location.clone(); + new_location.push((*key).to_owned()); + + write_node( + f, + child, + new_idention.clone(), + new_location.clone(), + counter == children_vec.len(), + false, + )?; + counter += 1; + } + } + NodeValue::Child { + path: _, + extandable: _, + } => { + // Do nothing and stop the recursion + } + } + + Ok(()) + } + + write_node(f, &self.root, String::new(), vec![], false, true)?; + + Ok(()) + } +} diff --git a/pkgs/sources/lf-make-map/src/mapping/map_tree/iterator.rs b/pkgs/sources/lf-make-map/src/mapping/map_tree/iterator.rs new file mode 100644 index 00000000..4364bb2b --- /dev/null +++ b/pkgs/sources/lf-make-map/src/mapping/map_tree/iterator.rs @@ -0,0 +1,53 @@ +use crate::mapping::MapKey; + +use super::{MappingTree, Node, NodeValue}; + +pub struct MappingTreeIterator { + children: Vec<(Vec<MapKey>, String)>, +} + +impl MappingTreeIterator { + pub fn new(tree: &MappingTree, ignore_extendable: bool) -> Self { + let children = extract_child(vec![], &tree.root, ignore_extendable); + + Self { children } + } +} + +fn extract_child( + current_key: Vec<MapKey>, + node: &Node, + ignore_extendable: bool, +) -> Vec<(Vec<MapKey>, String)> { + match &node.value { + NodeValue::Parent { children } => children + .iter() + .map(|(key, value)| { + let mut new_key = current_key.clone(); + new_key.push(key.to_owned()); + + extract_child(new_key, value, ignore_extendable) + }) + .flatten() + .collect(), + NodeValue::Child { path, extandable } => { + if ignore_extendable { + vec![(current_key, path.to_string())] + } else { + if *extandable { + vec![(current_key, path.to_string())] + } else { + vec![] + } + } + } + } +} + +impl Iterator for MappingTreeIterator { + type Item = (Vec<MapKey>, String); + + fn next(&mut self) -> Option<Self::Item> { + self.children.pop() + } +} diff --git a/pkgs/sources/lf-make-map/src/mapping/map_tree/lf_mapping.rs b/pkgs/sources/lf-make-map/src/mapping/map_tree/lf_mapping.rs new file mode 100644 index 00000000..6d9c7a0d --- /dev/null +++ b/pkgs/sources/lf-make-map/src/mapping/map_tree/lf_mapping.rs @@ -0,0 +1,19 @@ +use std::path::PathBuf; + +use crate::mapping::MapKey; + +use super::MappingTree; + +impl MappingTree { + pub fn to_lf_mappings(self, home_path: PathBuf) -> String { + self.iter(true) + .map(|(key, value)| { + format!( + "map g{} cd \"{}\"\n", + MapKey::display(&key), + home_path.join(&value).display() + ) + }) + .collect() + } +} diff --git a/pkgs/sources/lf-make-map/src/mapping/map_tree/mod.rs b/pkgs/sources/lf-make-map/src/mapping/map_tree/mod.rs new file mode 100644 index 00000000..35e6d91d --- /dev/null +++ b/pkgs/sources/lf-make-map/src/mapping/map_tree/mod.rs @@ -0,0 +1,402 @@ +use std::{collections::HashMap, mem}; + +use anyhow::{bail, Result}; +use log::debug; + +use self::iterator::MappingTreeIterator; + +use super::MapKey; + +pub mod display; +pub mod iterator; +pub mod lf_mapping; + +/// A prefix tree +#[derive(Debug)] +pub struct MappingTree { + root: Node, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum NodeValue { + Parent { children: HashMap<MapKey, Node> }, + Child { path: String, extandable: bool }, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Node { + value: NodeValue, +} + +impl MappingTree { + pub fn new() -> Self { + Self { + root: Node::new_parent(), + } + } + + pub fn root_node(&self) -> &Node { + &self.root + } + + pub fn iter(&self, ignore_extendable: bool) -> MappingTreeIterator { + MappingTreeIterator::new(&self, ignore_extendable) + } + + /// Returns the node at the key, otherwise None. The node can be changed + pub fn get_mut(&mut self, key: &[MapKey]) -> Option<&mut Node> { + let mut current_node = &mut self.root; + for ch in key.iter() { + if let NodeValue::Parent { children } = &mut current_node.value { + current_node = children.get_mut(&ch)? + } else { + return None; + } + } + + Some(current_node) + } + + /// Returns the node at the key, otherwise the last node that matched. + pub fn try_get(&self, key: &[MapKey]) -> (&Node, Vec<MapKey>) { + let mut current_node = &self.root; + let mut current_key = vec![]; + + for ch in key.iter() { + if let NodeValue::Parent { children } = ¤t_node.value { + current_node = if let Some(node) = children.get(&ch) { + let (key, _value) = children + .get_key_value(&ch) + .expect("This exists, we checked"); + current_key.push(key.clone()); + + node + } else { + return (current_node, current_key); + }; + } else { + return (current_node, current_key); + } + } + + (current_node, current_key) + } + + pub fn include(&mut self, path: &str) -> Result<()> { + let associated_key = MapKey::new_ones_from_path(path, 1); + self.insert(&associated_key, path) + } + + pub fn insert(&mut self, key: &[MapKey], path: &str) -> Result<()> { + self.insert_node(key, Node::new_child(path.to_owned())) + } + + pub fn interleave(&mut self, key: &[MapKey], node: Node) -> Result<()> { + let want_to_be_parent = self.get_mut(&key).expect("This value exists"); + let (parent_value, _parent_children) = if let NodeValue::Parent { children } = node.value { + ( + NodeValue::Parent { + children: children.clone(), + }, + children, + ) + } else { + unreachable!("This value will be a parent") + }; + + let child_value = mem::replace(&mut want_to_be_parent.value, parent_value); + assert!(matches!( + child_value, + NodeValue::Child { + path: _, + extandable: _ + } + )); + + let child_value = if let NodeValue::Child { + path, + extandable: _, + } = child_value + { + NodeValue::Child { + path, + extandable: false, + } + } else { + unreachable!("This is only a child value") + }; + + let child = Node { value: child_value }; + + let mut new_key = key.to_vec(); + new_key.push(MapKey { + key: '.', + part_path: ".".to_owned(), + resolution: 1, + }); + self.insert_node(&new_key, child)?; + Ok(()) + } + + pub fn insert_node(&mut self, key: &[MapKey], node: Node) -> Result<()> { + let (_node, found_key) = self.try_get(key).clone(); + + if found_key != key { + let needed_nodes_key = key + .strip_prefix(&found_key[..]) + .expect("The node's location is a prefix"); + + let needed_nodes_length = needed_nodes_key.iter().count(); + + let mut current_node = self + .get_mut(&found_key[..]) + .expect("This should always exists"); + let mut current_location = found_key.clone(); + let mut counter = 1; + + for ch in needed_nodes_key.iter() { + current_location.push(ch.to_owned()); + + let next_node = if counter == needed_nodes_length { + node.clone() + } else { + Node::new_parent() + }; + + current_node = match ¤t_node.value { + NodeValue::Parent { children } => { + assert_eq!(children.get(&ch), None); + + let children = + if let NodeValue::Parent { children } = &mut current_node.value { + children + } else { + unreachable!("This is a parent, we cheched") + }; + + children.insert(ch.to_owned(), next_node); + children.get_mut(&ch).expect("Was just inserted") + } + NodeValue::Child { + path, + extandable: _, + } => { + // A node that should be a parent was classified + // as child before: + // + // 1. Remove the child node and replace it with a parent one. + // 2. Add the child node to the parent node as child, but with a '.' as MapKey. + // 3. Add the original node also as child to the parent node. + + let mut children = HashMap::new(); + let move_child_node = Node::new_child(path.to_owned()); + + children.insert( + MapKey { + key: '.', + part_path: ".".to_owned(), + resolution: 1, + }, + move_child_node, + ); + children.insert(ch.to_owned(), next_node); + + current_node.value = NodeValue::Parent { children }; + + let children = + if let NodeValue::Parent { children } = &mut current_node.value { + children + } else { + unreachable!("We just inserted the parent value.") + }; + + children.get_mut(&ch).expect("Was just inserted") + } + }; + + counter += 1; + } + } else { + fn reduce_string(a: &str) -> Option<char> { + let first_char = a.chars().take(1).last().expect("Should contain one char"); + + if a.chars().all(|ch| ch == first_char) { + return Some(first_char); + } else { + return None; + } + } + fn check_subset(a: &str, b: &str) -> bool { + if a.len() > b.len() { + let a_prefix: String = a.chars().take(b.len()).collect(); + let a_suffix: String = a.chars().skip(b.len()).collect(); + + if a_prefix == b { + let clean_suffix = reduce_string(&a_suffix); + if let Some(ch) = clean_suffix { + ch == b.chars().last().expect("Will match") + } else { + false + } + } else { + false + } + } else if b.len() > a.len() { + let b_prefix: String = b.chars().take(a.len()).collect(); + let b_suffix: String = b.chars().skip(a.len()).collect(); + + if b_prefix == a { + let clean_suffix = reduce_string(&b_suffix); + if let Some(ch) = clean_suffix { + ch == a.chars().last().expect("Will match") + } else { + false + } + } else { + false + } + } else { + a == b + } + } + + // Another node was already inserted with the same key! + // So we simple increase the resolution of the other node and this node, until their + // keys are not the same anymore. + // This only includes the last segment of the `MapKey` + // + // 1. Change both keys, until they are not equal any more + // 2. Move the wrongly placed node to the new place. + // 3. Insert our node. + let mut foreign_key = vec![found_key.last().expect("This will exist").clone()]; + let mut our_key = vec![key.last().expect("This will exist").clone()]; + + debug!( + "'{}' ('{}') and '{}' ('{}') are the same, try to find a better combination!", + MapKey::display(&our_key), + our_key[0].part_path, + MapKey::display(&foreign_key), + foreign_key[0].part_path, + ); + + // The 'a' and 'b' stuff is here, to ensure that both returning None will not match + // this condition. + if reduce_string(&foreign_key[0].part_path).unwrap_or('a') + == reduce_string(&our_key[0].part_path).unwrap_or('b') + { + bail!( + "\ +The foreign_key ('{}', path_part: '{}' -> '{}') and our_key ('{}', path_part: '{}' -> '{}') \ +have an identical path_part (when duplicated chars are removed)! +I cannot extended them via incrementation. +Please rename the paths to fix this. + ", + MapKey::display(&foreign_key), + &foreign_key[0].part_path, + reduce_string(&foreign_key[0].part_path).expect("Is some here"), + MapKey::display(&our_key), + &our_key[0].part_path, + reduce_string(&our_key[0].part_path).expect("Is some here"), + ); + } + + if check_subset(&foreign_key[0].part_path, &our_key[0].part_path) { + bail!( + "\ +The foreign_key ('{}', path_part: '{}') and our_key ('{}', path_part: '{}') \ +are subsets of one another! +A discrimination through incrementation will not work! +Please rename the paths to fix this. + ", + MapKey::display(&foreign_key), + &foreign_key[0].part_path, + MapKey::display(&our_key), + &our_key[0].part_path, + ); + } + + while our_key == foreign_key { + our_key = our_key[0].increment(our_key[our_key.len() - 1].resolution + 1); + foreign_key = + foreign_key[0].increment(foreign_key[foreign_key.len() - 1].resolution + 1); + debug!( + "Now its: '{}' ('{}') and '{}' ('{}')", + MapKey::display(&our_key), + our_key[0].part_path, + MapKey::display(&foreign_key), + foreign_key[0].part_path, + ); + } + + debug!( + "Found a better one: '{}' ('{}') and '{}' ('{}')", + MapKey::display(&our_key), + our_key[0].part_path, + MapKey::display(&foreign_key), + foreign_key[0].part_path, + ); + + let parent = self + .get_mut(&found_key[..&found_key.len() - 1]) + .expect("This will exist"); + + if let NodeValue::Parent { children } = &mut parent.value { + if let NodeValue::Child { + path: _, + extandable: _, + } = children + .get(found_key.last().expect("Exists")) + .expect("This node also exists") + .value + { + let old = children + .remove(found_key.last().expect("This will exist")) + .expect("This will be there"); + + let full_foreign_key: Vec<_> = found_key + .clone() + .into_iter() + .rev() + .skip(1) + .rev() + .chain(foreign_key.clone().into_iter()) + .collect(); + self.insert_node(&full_foreign_key, old.clone())?; + } + + let full_our_key: Vec<_> = key + .to_vec() + .into_iter() + .rev() + .skip(1) + .rev() + .chain(our_key.clone().into_iter()) + .collect(); + + self.insert_node(&full_our_key, node.clone())?; + } else { + unreachable!("This node will be a parent"); + } + } + + Ok(()) + } +} + +impl Node { + pub fn new_child(path: String) -> Self { + Self { + value: NodeValue::Child { + path, + extandable: true, + }, + } + } + pub fn new_parent() -> Self { + Self { + value: NodeValue::Parent { + children: HashMap::new(), + }, + } + } +} diff --git a/pkgs/sources/lf-make-map/src/mapping/mod.rs b/pkgs/sources/lf-make-map/src/mapping/mod.rs new file mode 100644 index 00000000..114fdca0 --- /dev/null +++ b/pkgs/sources/lf-make-map/src/mapping/mod.rs @@ -0,0 +1,156 @@ +use std::{ + fmt::{Display, Write}, + hash::Hash, +}; + +use log::debug; + +pub mod map_tree; + +#[derive(Clone, Debug, Eq)] +pub struct MapKey { + pub key: char, + + resolution: usize, + + /// Part of the path, used to derive the key + part_path: String, +} + +impl Hash for MapKey { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.key.hash(state) + } +} + +impl PartialEq for MapKey { + fn eq(&self, other: &Self) -> bool { + self.key == other.key + } +} + +impl MapKey { + pub fn new_from_part_path(part_path: &str, resolution: usize) -> Vec<Self> { + let key = Self::part_path_to_key(&part_path, resolution); + + key.chars() + .map(|ch| Self { + key: ch, + resolution, + part_path: part_path.to_owned(), + }) + .collect() + } + + pub fn new_ones_from_path(path: &str, number_of_chars: usize) -> Vec<Self> { + let key: Vec<MapKey> = path + .split('/') + .map(|part| Self::new_from_part_path(part, number_of_chars)) + .flatten() + .collect(); + + debug!( + "Generated full MapKeys: '{}' -> '{}'", + path, + MapKey::display(&key) + ); + key + } + + pub fn increment(&self, target_resolution: usize) -> Vec<Self> { + let new_resolution = target_resolution; + + // debug!("Incrementing: '{}' ('{}')", &self, &self.part_path); + + let added_chars = if new_resolution < self.part_path.len() { + MapKey::part_path_to_key(&self.part_path, new_resolution) + } else { + let mut generated_chars = + MapKey::part_path_to_key(&self.part_path, self.part_path.len()); + + generated_chars.extend( + (0..(new_resolution - self.part_path.len())) + .into_iter() + .map(|_| self.part_path.chars().last().expect("This will exists")), + ); + + generated_chars + }; + + let part_path = self.part_path.clone(); + let output: Vec<Self> = added_chars + .chars() + .enumerate() + .map(|(res, ch)| MapKey { + key: ch, + resolution: res + 1, + part_path: part_path.clone(), + }) + .collect(); + + // debug!("Finished increment: '{}' ('{}')", MapKey::display(&output), output[0].part_path); + output + } + + pub fn display(values: &[Self]) -> String { + values.iter().map(|value| value.key.clone()).collect() + } + fn part_path_to_key(part: &str, number_of_chars: usize) -> String { + fn make(pat: char, part: &str, number_of_chars: usize) -> String { + let mut acc = String::new(); + + if !part.split(pat).all(|part| part.len() > 0) { + panic!( + "\ +Can't turn this path '{}' to a mapping. +This should not happen, please report the bug!", + part + ) + } + + let mut last_working = None; + for i in 0..number_of_chars { + for str in part.split(pat) { + if acc.len() != number_of_chars { + acc.push(match str.chars().nth(i) { + Some(ch) => ch, + None => { + if let Some(last) = last_working { + str.chars().nth(last).expect("This should always exist") + } else { + last_working = Some(i - 1); + str.chars().nth(i - 1).expect("This should always exist") + } + } + }) + } + } + } + + acc + } + + let value = if part.contains('_') && !part.starts_with('_') && !part.ends_with('_') { + make('_', part, number_of_chars) + } else if part.contains('-') && !part.starts_with('-') && !part.ends_with('-') { + make('-', part, number_of_chars) + } else { + part.chars().take(number_of_chars).collect::<String>() + }; + + assert_eq!( + value.len(), + number_of_chars, + "'{}' does not have expected length of: {}", + value, + number_of_chars + ); + value + } +} + +impl Display for MapKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_char(self.key) + } +} diff --git a/pkgs/sources/lf-make-map/update.sh b/pkgs/sources/lf-make-map/update.sh new file mode 100755 index 00000000..a0a029f4 --- /dev/null +++ b/pkgs/sources/lf-make-map/update.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +nix flake update + +[ "$1" = "upgrade" ] && cargo upgrade +cargo update diff --git a/pkgs/sources/plgs-pkgs/README.md b/pkgs/sources/plgs-pkgs/README.md new file mode 100644 index 00000000..e8169951 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/README.md @@ -0,0 +1,92 @@ +# Fork + +All files in this repository where forked form [here](https://github.com/NixNeovim/NixNeovimPlugins) on commit `5010b91eb03696574c3c293f072a090618227e87`. +Below the original README. They were licensed under the MIT license. + +# All vim plugins, ready to go + +This repo auto generates nix packages for vim/neovim plugins. +Packages are automatically updated twice per week using a GitHub Actions. +Plugins are fetched from the `manifest.txt` and [awesome-neovim][0] repo. + +This is a fork of [this repo](https://github.com/m15a/nixpkgs-vim-extra-plugins); however, we fetch all additions from the original repo, so we will never have less plugins. +Further, the original deletes plugins that are available in the nixpkgs. We, instead, try to assemble a list of all available plugins. +Therefore, to access plugins you will never have to search in two places. + +This repo can be used as a stand-alone, by adding it to your inputs. +However, we recommend to use [NixNeovim](https://github.com/NixNeovim/NixNeovim) modules instead, and use this only when you need a plugins, which does not have a module, yet. + +## Available plugins + +The [plugins.md](plugins.md) contains an auto-generated list of all available plugins. + +## Usage + +- We recommend using [NixNeovim](https://github.com/NixNeovim/NixNeovim), and only access the plugins directly when they do not have a module in NixNeovim. + +However, you can also use this repo without NixNeovim: +To access the plugins, you need to add the overlay. +The overlay adds extra Vim plugins to `pkgs.vimExtraPlugins`. +First, add this repo to your inputs: + +``` +inputs.nixneovimplugins.url = github:jooooscha/nixpkgs-vim-extra-plugins +``` + +Next, apply the provided overlay: + +``` +nixpkgs.overlays = [ + inputs.nixneovimplugins.overlays.default +]; +``` + +Finally, you can add the packages to your vim/neovim config. For example you can use [NixNeovim](https://github.com/NixNeovim/Nixneovim) or you can add the plugins directly: + +``` + programs.neovim = { + plugins = [ + pkgs.vimExtraPlugins.nvim-colorizer-lua + ]; + } +``` + +More info on using neovim with nix can be found here: [NixOS Neovim](https://nixos.wiki/wiki/Neovim) + +## Contribution + +### How to add a new plugin + +#### 1. Add the plugin to manifest.txt: + +``` +# Examples + +haringsrob/nvim_context_vt +sourcehut:henriquehbr/ataraxis.lua +gitlab:yorickpeterse/nvim-pqf +williamboman/mason.nvim:45b9a4da776d9fb017960b3ac7241161fb7bc578 +foo/bar::baz --> renamed to baz +foo/bar:dev --> using dev branch +``` + +Supported are Github (default), SourceHut, and GitLab. + +#### 2. Create a Pull Request + +- Create a pull request with the changed manifest.txt (and blacklist.txt if neccessary). +- A GitHub action will check your contribution and generate all neccessary nix code for your new plugin. It will also take care of sorting and cleaning the manifest.txt +- After all checks have passed, I will merge your change. + +I am happy for any contribution. :) + +### How to remove a new plugin + +Copy the entry from manifest.txt to blacklist.txt and create a PR. +The GitHub Actions will do the rest, including removing the entry from manifest.txt + +## Credits + +This is originally based on work by [m15a](https://github.com/m15a/nixpkgs-vim-extra-plugins) + +[0]: https://github.com/rockerBOO/awesome-neovim diff --git a/pkgs/sources/plgs-pkgs/check.nix b/pkgs/sources/plgs-pkgs/check.nix new file mode 100644 index 00000000..ad23e2c7 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/check.nix @@ -0,0 +1,37 @@ +{ + pkgs, + lib, + ... +}: let + # checks if a plugin has a license + hasLicense = _: pkg: let + warn = x: lib.warn x x; + + msg = + if builtins.hasAttr "license" pkg.meta + then "${pkg.name} has license" + else warn "${pkg.name} has no license"; + + msg' = lib.replaceStrings [" "] ["-"] msg; + in + pkgs.runCommandNoCC msg' {} "echo : > $out "; + + # function to check License for all packages + check-missing-licenses = let + buildInputs = + lib.mapAttrsToList + hasLicense + pkgs.vimExtraPlugins; + in + pkgs.runCommandNoCC + "check-missing-licenses" + {inherit buildInputs;} + "echo : > $out"; +in { + checks = + pkgs.vimExtraPlugins + // { + inherit check-missing-licenses; + inherit (pkgs) update-vim-plugins; + }; +} diff --git a/pkgs/sources/plgs-pkgs/default.nix b/pkgs/sources/plgs-pkgs/default.nix new file mode 100644 index 00000000..0f7cd485 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/default.nix @@ -0,0 +1,15 @@ +[ + (final: prev: + prev.lib.composeManyExtensions [ + (self: super: let + origin = import ./plugins { + inherit (super.vimUtils) buildVimPlugin; + inherit (super) lib fetchurl fetchgit; + }; + in { + vimExtraPlugins = super.lib.makeExtensible (_: super.lib.recurseIntoAttrs origin); + }) + ] + final + prev) +] diff --git a/pkgs/sources/plgs-pkgs/overrides.nix b/pkgs/sources/plgs-pkgs/overrides.nix new file mode 100644 index 00000000..e03a78b1 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/overrides.nix @@ -0,0 +1,34 @@ +final: prev: let + inherit (final) lib; + + /* + * Mark broken packages here. + */ + markBrokenPackages = self: super: + lib.mapAttrs (attrName: broken: + super.${attrName}.overrideAttrs (old: { + meta = old.meta // {inherit broken;}; + })) + { + # <name> = true; + }; + + /* + * Add licenses if missing or incorrect in generated ./pkgs/vim-plugins.nix. + */ + fixLicenses = self: super: + lib.mapAttrs (attrName: license: + super.${attrName}.overrideAttrs (old: { + meta = old.meta // {inherit license;}; + })) (with lib.licenses; { + /* + * Example: + * plugin-name = [<licenses>] + */ + }); +in { + vimExtraPlugins = prev.vimExtraPlugins.extend (lib.composeManyExtensions [ + markBrokenPackages + fixLicenses + ]); +} diff --git a/pkgs/sources/plgs-pkgs/plugins/.plugins.json b/pkgs/sources/plgs-pkgs/plugins/.plugins.json new file mode 100644 index 00000000..9331bc8f --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/.plugins.json @@ -0,0 +1,7 @@ +{ + "ThePrimeagen/harpoon:master": "{\"description\": \"\", \"homepage\": \"https://github.com/ThePrimeagen/harpoon\", \"license\": {\"py/reduce\": [{\"py/type\": \"update_vim_plugins.nix.License\"}, {\"py/tuple\": [\"mit\"]}]}, \"name\": \"harpoon\", \"owner\": \"ThePrimeagen\", \"py/object\": \"update_vim_plugins.plugin.GitHubPlugin\", \"source\": {\"py/object\": \"update_vim_plugins.nix.UrlSource\", \"sha256\": \"1w4hi9hbdjwdhb4vwa0x08a25vbcxqg1d5cskm2qvjy5fdlqils0\", \"url\": \"https://github.com/ThePrimeagen/harpoon/archive/ccae1b9bec717ae284906b0bf83d720e59d12b91.tar.gz\"}, \"source_line\": \"ThePrimeagen/harpoon:master\", \"version\": {\"__reduce__\": [{\"py/type\": \"datetime.date\"}, [\"B+cMGg==\"]], \"py/object\": \"datetime.date\"}}", + "akinsho/toggleterm.nvim": "{\"description\": \"A neovim lua plugin to help easily manage multiple terminal windows\", \"homepage\": \"https://github.com/akinsho/toggleterm.nvim\", \"license\": {\"py/reduce\": [{\"py/type\": \"update_vim_plugins.nix.License\"}, {\"py/tuple\": [\"gpl3Only\"]}]}, \"name\": \"toggleterm-nvim\", \"owner\": \"akinsho\", \"py/object\": \"update_vim_plugins.plugin.GitHubPlugin\", \"source\": {\"py/object\": \"update_vim_plugins.nix.UrlSource\", \"sha256\": \"0nx69q9597vy7lzvvh58fnjyin23ns6apmyp532sgf547bw7mld6\", \"url\": \"https://github.com/akinsho/toggleterm.nvim/archive/cbd041d91b90cd3c02df03fe6133208888f8e008.tar.gz\"}, \"source_line\": \"akinsho/toggleterm.nvim\", \"version\": {\"__reduce__\": [{\"py/type\": \"datetime.date\"}, [\"B+cMBg==\"]], \"py/object\": \"datetime.date\"}}", + "andrewferrier/debugprint.nvim": "{\"description\": \"Debugging in NeoVim the print() way!\", \"homepage\": \"https://github.com/andrewferrier/debugprint.nvim\", \"license\": {\"py/reduce\": [{\"py/type\": \"update_vim_plugins.nix.License\"}, {\"py/tuple\": [\"mit\"]}]}, \"name\": \"debugprint-nvim\", \"owner\": \"andrewferrier\", \"py/object\": \"update_vim_plugins.plugin.GitHubPlugin\", \"source\": {\"py/object\": \"update_vim_plugins.nix.UrlSource\", \"sha256\": \"06r1jhx7jd15q8wvnw0xqwk3bkx39pm4pbv70hf9ggd6zsnmsrmn\", \"url\": \"https://github.com/andrewferrier/debugprint.nvim/archive/54297dd0a4f318b279a1cb954e7714f3942df123.tar.gz\"}, \"source_line\": \"andrewferrier/debugprint.nvim\", \"version\": {\"__reduce__\": [{\"py/type\": \"datetime.date\"}, [\"B+gDHQ==\"]], \"py/object\": \"datetime.date\"}}", + "lmburns/lf.nvim": "{\"description\": \"Lf file manager for Neovim (in Lua)\", \"homepage\": \"https://github.com/lmburns/lf.nvim\", \"license\": {\"py/reduce\": [{\"py/type\": \"update_vim_plugins.nix.License\"}, {\"py/tuple\": [\"mit\"]}]}, \"name\": \"lf-nvim\", \"owner\": \"lmburns\", \"py/object\": \"update_vim_plugins.plugin.GitHubPlugin\", \"source\": {\"py/object\": \"update_vim_plugins.nix.UrlSource\", \"sha256\": \"1nwf90bnzqhlgs007gg6xpx0vf4r1d19586nld78ipi1ch7nz4px\", \"url\": \"https://github.com/lmburns/lf.nvim/archive/69ab1efcffee6928bf68ac9bd0c016464d9b2c8b.tar.gz\"}, \"source_line\": \"lmburns/lf.nvim\", \"version\": {\"__reduce__\": [{\"py/type\": \"datetime.date\"}, [\"B+cKAw==\"]], \"py/object\": \"datetime.date\"}}", + "nvim-telescope/telescope-bibtex.nvim": "{\"description\": \"A telescope.nvim extension to search and paste bibtex entries into your TeX files.\", \"homepage\": \"https://github.com/nvim-telescope/telescope-bibtex.nvim\", \"license\": {\"py/reduce\": [{\"py/type\": \"update_vim_plugins.nix.License\"}, {\"py/tuple\": [\"mit\"]}]}, \"name\": \"telescope-bibtex-nvim\", \"owner\": \"nvim-telescope\", \"py/object\": \"update_vim_plugins.plugin.GitHubPlugin\", \"source\": {\"py/object\": \"update_vim_plugins.nix.UrlSource\", \"sha256\": \"1sd6p8cvv3dckgrhc7grlyfcibjxhxbfyh0w7p5m4mdcazhy1kqs\", \"url\": \"https://github.com/nvim-telescope/telescope-bibtex.nvim/archive/289a6f86ebec06e8ae1590533b732b9981d84900.tar.gz\"}, \"source_line\": \"nvim-telescope/telescope-bibtex.nvim\", \"version\": {\"__reduce__\": [{\"py/type\": \"datetime.date\"}, [\"B+gDHA==\"]], \"py/object\": \"datetime.date\"}}" +} \ No newline at end of file diff --git a/pkgs/sources/plgs-pkgs/plugins/blacklist.txt b/pkgs/sources/plgs-pkgs/plugins/blacklist.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/blacklist.txt @@ -0,0 +1 @@ + diff --git a/pkgs/sources/plgs-pkgs/plugins/default.nix b/pkgs/sources/plgs-pkgs/plugins/default.nix new file mode 100644 index 00000000..df09e446 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/default.nix @@ -0,0 +1,55 @@ +{ + lib, + buildVimPlugin, + fetchurl, + fetchgit, +}: { + /* + Generated from: ThePrimeagen/harpoon:master + */ + harpoon = buildVimPlugin { + pname = "harpoon"; + version = "2023-12-26"; + src = fetchurl { + url = "https://github.com/ThePrimeagen/harpoon/archive/ccae1b9bec717ae284906b0bf83d720e59d12b91.tar.gz"; + sha256 = "1w4hi9hbdjwdhb4vwa0x08a25vbcxqg1d5cskm2qvjy5fdlqils0"; + }; + meta = with lib; { + description = ""; + homepage = "https://github.com/ThePrimeagen/harpoon"; + license = with licenses; [mit]; + }; + }; + /* + Generated from: lmburns/lf.nvim + */ + lf-nvim = buildVimPlugin { + pname = "lf-nvim"; + version = "2023-10-03"; + src = fetchurl { + url = "https://github.com/lmburns/lf.nvim/archive/69ab1efcffee6928bf68ac9bd0c016464d9b2c8b.tar.gz"; + sha256 = "1nwf90bnzqhlgs007gg6xpx0vf4r1d19586nld78ipi1ch7nz4px"; + }; + meta = with lib; { + description = "Lf file manager for Neovim (in Lua)"; + homepage = "https://github.com/lmburns/lf.nvim"; + license = with licenses; [mit]; + }; + }; + /* + Generated from: nvim-telescope/telescope-bibtex.nvim + */ + telescope-bibtex-nvim = buildVimPlugin { + pname = "telescope-bibtex-nvim"; + version = "2024-03-28"; + src = fetchurl { + url = "https://github.com/nvim-telescope/telescope-bibtex.nvim/archive/289a6f86ebec06e8ae1590533b732b9981d84900.tar.gz"; + sha256 = "1sd6p8cvv3dckgrhc7grlyfcibjxhxbfyh0w7p5m4mdcazhy1kqs"; + }; + meta = with lib; { + description = "A telescope.nvim extension to search and paste bibtex entries into your TeX files."; + homepage = "https://github.com/nvim-telescope/telescope-bibtex.nvim"; + license = with licenses; [mit]; + }; + }; +} diff --git a/pkgs/sources/plgs-pkgs/plugins/manifest.txt b/pkgs/sources/plgs-pkgs/plugins/manifest.txt new file mode 100644 index 00000000..615083c8 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/manifest.txt @@ -0,0 +1,3 @@ +lmburns/lf.nvim +nvim-telescope/telescope-bibtex.nvim +ThePrimeagen/harpoon:master diff --git a/pkgs/sources/plgs-pkgs/plugins/plugins.md b/pkgs/sources/plgs-pkgs/plugins/plugins.md new file mode 100644 index 00000000..4f73f811 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/plugins.md @@ -0,0 +1,7 @@ +- Plugin count: 3 + +| Repo | Last Update | Nix package name | Last checked | +|:---|:---|:---|:---| +| [ThePrimeagen/harpoon:master](https://github.com/ThePrimeagen/harpoon) | 2023-12-26 | `harpoon` | 2024-05-09 | +| [lmburns/lf.nvim](https://github.com/lmburns/lf.nvim) | 2023-10-03 | `lf-nvim` | 2024-05-09 | +| [nvim-telescope/telescope-bibtex.nvim](https://github.com/nvim-telescope/telescope-bibtex.nvim) | 2024-03-28 | `telescope-bibtex-nvim` | 2024-05-09 | diff --git a/pkgs/sources/plgs-pkgs/plugins/whitelist.txt b/pkgs/sources/plgs-pkgs/plugins/whitelist.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pkgs/sources/plgs-pkgs/plugins/whitelist.txt diff --git a/pkgs/sources/plgs-pkgs/update.sh b/pkgs/sources/plgs-pkgs/update.sh new file mode 100755 index 00000000..6a0d3452 --- /dev/null +++ b/pkgs/sources/plgs-pkgs/update.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env sh + +BASE_DIR="$(readlink -f "$(dirname "$0")/plugins")" + +# Fetch plugins +cd "$BASE_DIR" || (echo "BUG: No '$BASE_DIR'" && exit 1) + +# Cleanup manifest +sort -o "$BASE_DIR/manifest.txt" "$BASE_DIR/manifest.txt" +sort -o "$BASE_DIR/blacklist.txt" "$BASE_DIR/blacklist.txt" +## Remove all plugins, which are on the blacklist +# The same file is read and written to +# shellcheck disable=SC2005 +echo "$(comm -23 "$BASE_DIR/manifest.txt" "$BASE_DIR/blacklist.txt")" >"$BASE_DIR/manifest.txt" + +# Backup vim-plugins.nix +mv "$BASE_DIR/default.nix" "$BASE_DIR/default.nix.bak" +echo "{...} : {}" >"$BASE_DIR/default.nix" + +# Generate derivations for new plugins (this binary is provided by the dev-environment) +update-vim-plugins cleanup "$BASE_DIR" + +# Restore vim-plugins.nix +mv "$BASE_DIR/default.nix.bak" "$BASE_DIR/default.nix" + +# Update new plugins +update-vim-plugins update "$BASE_DIR" --all diff --git a/pkgs/sources/scripts/default.nix b/pkgs/sources/scripts/default.nix new file mode 100644 index 00000000..09c8d411 --- /dev/null +++ b/pkgs/sources/scripts/default.nix @@ -0,0 +1,412 @@ +{ + sysLib, + homeConfig, + nixosConfig, +}: [ + ( + final: prev: let + inherit (prev) lib; + + write_shell = { + name, + path, + dependencies ? [], + keepPath ? false, + completions ? false, + }: + sysLib.writeShellScript { + inherit name keepPath; + src = ./source/${path}/${name}.sh; + dependencies = dependencies ++ [prev.dash]; + generateCompletions = completions; + }; + write_python = { + name, + path, + dependencies_system ? [], + dependencies_python ? _: [], + keepPath ? false, + }: let + src = ./source/${path}/${name}.py; + dependencies = + [(prev.python3.withPackages dependencies_python)] + ++ dependencies_system; + path_setting = + if keepPath + then "--prefix PATH :" + else "--set PATH"; + in + prev.runCommandLocal name { + nativeBuildInputs = [prev.makeWrapper] ++ dependencies; + } + '' + install -m755 ${src} -D "$out/bin/${name}" + patchShebangs "$out/bin/${name}" + wrapProgram "$out/bin/${name}" ${path_setting} ${prev.lib.makeBinPath dependencies}; + ''; + + ## Begin of shell scripts + aumo-scr = write_shell { + name = "aumo"; + path = "apps"; + dependencies = builtins.attrValues { + inherit + (prev) + udisks + findutils + rofi + ; + }; + }; + + battery-scr = write_shell { + name = "battery"; + path = "wrappers"; + dependencies = []; + }; + + brightness-scr = lib.mkIf nixosConfig.soispha.laptop.enable (write_shell { + name = "brightness"; + path = "small_functions"; + generateCompletions = true; + dependencies = []; + replacementStrings = {BACKLIGHT_NAME = nixosConfig.soispha.laptop.backlight;}; + }); + + con2pdf-scr = sysLib.writeShellScript { + name = "con2pdf"; + src = ./source/apps/con2pdf.sh; + dependencies = builtins.attrValues {inherit (prev) sane-backends imagemagick coreutils fd;}; + generateCompletions = true; + replacementStrings = { + DEVICE_FUNCTION = + # This is here, because escaping the whole function, to use it in the shell script + # directly just isn't possible + prev.writeText "DEVICE_FUNCTION" + /* + bash + */ + '' + scanimage -L | awk 'BEGIN { FS = "`" } { gsub(/'.*/, "", $2); print $2 }' + ''; + }; + }; + + description-scr = write_shell { + name = "description"; + path = "specific/ytcc"; + dependencies = builtins.attrValues { + inherit (prev) jq fmt less locale; + }; + }; + + fupdate-scr = write_shell { + name = "fupdate"; + path = "apps"; + keepPath = true; + completions = true; + dependencies = builtins.attrValues { + inherit + (prev) + dash + nix + gnugrep + fd + coreutils + bat # used by batgrep + gnused # required by batgrep + git # needed to fetch through git + ; + inherit (prev.bat-extras) batgrep; + }; + }; + + git-edit-index-scr = write_shell { + name = "git-edit-index"; + path = "apps"; + completions = true; + # This starts neovim, wich might want to shell out + keepPath = true; + dependencies = builtins.attrValues { + inherit + (prev) + git + gnused + # $EDITOR + + ; + }; + }; + + hibernate-scr = write_shell { + name = "hibernate"; + path = "wrappers"; + dependencies = builtins.attrValues { + inherit + (prev) + systemd + taskwarrior + ; + }; + }; + + ll-scr = sysLib.writeShellScript { + name = "ll"; + src = ./source/wrappers/ll.sh; + wrap = false; + }; + + # TODO: this need to be replaced with a wayland alternative + # llp-scr = write_shell { + # name = "llp"; + # path = "wrappers"; + # dependencies = builtins.attrValues {inherit (prev) lf ueberzug;}; + # }; + + lock-scr = write_shell { + name = "lock"; + path = "wrappers"; + dependencies = builtins.attrValues { + inherit + (prev) + taskwarrior + swaylock + ; + }; + }; + + lyrics-scr = write_shell { + name = "lyrics"; + path = "wrappers"; + dependencies = builtins.attrValues { + inherit + (prev) + exiftool + mpc-cli + jq + less + locale # dependency of less + ; + }; + }; + + mpc-fav-scr = write_shell { + name = "mpc-fav"; + path = "wrappers"; + dependencies = builtins.attrValues { + inherit + (prev) + mpc-cli + ; + }; + }; + + mpc-rm-scr = write_shell { + name = "mpc-rm"; + path = "wrappers"; + dependencies = builtins.attrValues { + inherit + (prev) + mpc-cli + trash-cli + ; + }; + }; + + mpc-scr = write_shell { + name = "mpc"; + path = "wrappers"; + dependencies = [ + mpc-fav-scr + mpc-rm-scr + prev.mpc-cli + ]; + }; + + nato-scr = write_python { + name = "nato"; + path = "small_functions"; + dependencies_python = ps: []; + }; + + neorg-scr = sysLib.writeShellScriptMultiPart { + name = "neorg"; + keepPath = true; + src = ./source/specific/neorg/sh; + baseName = "main.sh"; + cmdPrefix = "functions"; + cmdNames = [ + "add.sh" + "context.sh" + "dmenu.sh" + "f_start.sh" + "f_stop.sh" + "list.sh" + "project.sh" + "review.sh" + "utils.sh" + "workspace.sh" + ]; + dependencies = with prev; [ + cocogitto + rofi + libnotify + ]; + generateCompletions = true; + replacementStrings = { + DEFAULT_NEORG_PROJECT_DIR = + homeConfig.programs.nixvim.plugins.neorg.modules."core.dirman".config.workspaces.projects; + HOME_TASKRC = "${homeConfig.xdg.configHome}/task/home-manager-taskrc"; + NEORG_REVIEW_PATH = "${homeConfig.xdg.dataHome}/neorg/review"; + ALL_PROJECTS_NEWLINE = "${homeConfig.soispha.taskwarrior.projects.projects_newline}"; + ALL_PROJECTS_COMMA = "${homeConfig.soispha.taskwarrior.projects.projects_comma}"; + ALL_PROJECTS_PIPE = "${homeConfig.soispha.taskwarrior.projects.projects_pipe}"; + ALL_WORKSPACES = "${lib.strings.concatStringsSep "|" (builtins.attrNames homeConfig.programs.nixvim.plugins.neorg.modules."core.dirman".config.workspaces)}"; + ID_GENERATION_FUNCTION = "${sysLib.writeShellScript { + name = "neorg_id_function"; + src = ./source/specific/neorg/neorg_id_function.sh; + dependencies = with prev; [ + taskwarrior + gawk + findutils # xargs + ]; + }}/bin/neorg_id_function"; + + # TODO: Replace the hard-coded path here with some reference <2023-10-20> + TASK_PROJECT_FILE = "/home/soispha/repos/nix/nixos-config/hm/soispha/conf/taskwarrior/projects/default.nix"; + }; + }; + + screenshot_persistent-scr = write_shell { + name = "screenshot_persistent"; + path = "small_functions"; + keepPath = true; + dependencies = builtins.attrValues { + inherit + (prev) + grim + slurp + alacritty + rofi + libnotify + lf # TODO: add llp + ; + }; + }; + + screenshot_temporary-scr = write_shell { + name = "screenshot_temporary"; + path = "small_functions"; + dependencies = builtins.attrValues {inherit (prev) grim slurp wl-clipboard;}; + }; + + show-scr = write_shell { + name = "show"; + path = "wrappers"; + keepPath = true; # I might want to use nvim in less (and shell escapes) + dependencies = builtins.attrValues {inherit (prev) less locale;}; + }; + + sort_song-scr = write_shell { + name = "sort_song"; + path = "wrappers"; + dependencies = builtins.attrValues {inherit (prev) mediainfo jq gawk;}; + }; + + spodi-scr = sysLib.writeShellScriptMultiPart { + name = "spodi"; + keepPath = false; + src = ./source/specific/spodi; + baseName = "spodi.sh"; + cmdPrefix = "sh"; + cmdNames = [ + "download.sh" + "update.sh" + ]; + dependencies = with prev; [ + gawk + expect + spotdl + fd + coreutils + ]; + generateCompletions = true; + replacementStrings = { + XDG_CACHE_HOME = homeConfig.xdg.cacheHome; + XDG_MUSIC_DIR = homeConfig.xdg.userDirs.music; + }; + }; + + update-sys-scr = write_shell { + name = "update-sys"; + path = "small_functions"; + completions = true; + dependencies = builtins.attrValues { + inherit + (prev) + git + nixos-rebuild + sudo + openssh + coreutils + mktemp + gnugrep + gnused + systemd + ; + }; + }; + + virsh-del-scr = write_shell { + name = "virsh-del"; + path = "wrappers"; + dependencies = builtins.attrValues {inherit (prev) libvirt;}; + }; + + yti-scr = write_shell { + name = "yti"; + path = "wrappers"; + dependencies = builtins.attrValues {inherit (prev) gawk expect yt-dlp;}; + }; + in { + scripts = { + # llp = llp-scr; # TODO: see above + aumo = aumo-scr; + battery = battery-scr; + brightness = brightness-scr; + con2pdf = con2pdf-scr; + description = description-scr; + fupdate = fupdate-scr; + git-edit-index = git-edit-index-scr; + hibernate = hibernate-scr; + ll = ll-scr; + lock = lock-scr; + lyrics = lyrics-scr; + mpc = mpc-scr; + mpc-fav = mpc-fav-scr; + mpc-rm = mpc-rm-scr; + nato = nato-scr; + neorg = neorg-scr; + screenshot_persistent = screenshot_persistent-scr; + screenshot_temporary = screenshot_temporary-scr; + show = show-scr; + sort_song = sort_song-scr; + spodi = spodi-scr; + update-sys = update-sys-scr; + virsh-del = virsh-del-scr; + yti = yti-scr; + }; + } + ) +] + + + pkgs = import nixpkgs (import ./sys/nixpkgs { + inherit (nixpkgs) lib; + inherit system sysLib; + + # FIXME: Don't unconditionally use tiamat here <2024-02-24> + homeConfig = self.nixosConfigurations.tiamat.config.home-manager.users.soispha; + nixosConfig = self.nixosConfigurations.tiamat.config; + overlays = []; + }); diff --git a/pkgs/sources/scripts/source/apps/aumo.sh b/pkgs/sources/scripts/source/apps/aumo.sh new file mode 100755 index 00000000..84d39deb --- /dev/null +++ b/pkgs/sources/scripts/source/apps/aumo.sh @@ -0,0 +1,28 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +unmounting() { + disk_name="$(find /dev/disk/by-label -type l -printf "%P|" | rofi -sep "|" -dmenu -p "Select disk to mount")" + + udisksctl unmount --block-device "/dev/disk/by-label/$disk_name" +} + +mounting() { + disk_name="$(find /dev/disk/by-label -type l -printf "%P|" | rofi -sep "|" -dmenu -p "Select disk to mount")" + + udisksctl mount --block-device "/dev/disk/by-label/$disk_name" +} + +case "$1" in +"mount") + mounting + ;; +"unmount" | "umount") + unmounting + ;; +*) + die "Usage: $NAME mount|unmount" + ;; +esac diff --git a/pkgs/sources/scripts/source/apps/con2pdf.sh b/pkgs/sources/scripts/source/apps/con2pdf.sh new file mode 100755 index 00000000..08bf8998 --- /dev/null +++ b/pkgs/sources/scripts/source/apps/con2pdf.sh @@ -0,0 +1,234 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# needed for help() and version +# shellcheck disable=2034 +AUTHORS="Soispha" +# shellcheck disable=2034 +YEARS="2023" +# shellcheck disable=2034 +VERSION="1.0.0" + +# NAME is from the wrapper +# shellcheck disable=SC2269 +NAME="$NAME" +help() { + cat <<EOF +Scan images and turn them into a pdf. + +Usage: + $NAME [OPTIONS] --name --device + +OPTIONS: + --out-dir | -o [FILE] + Path to place the generated pdf files (default: ./pdf). + + --name | -n NAME + Name for the pdf files (e.g. <NAME>_1.pdf). + + --num-pages | -p NUM + Number of pages to merge into one pdf (default: 1). + + --device | -d DEVICE + Device used for scanning. + + --method | -m METHOD + Method to use for scanning (default: ADF). + + --help | -h + Display this help and exit. + + --version | -v + Display version and copyright information and exit. +ARGUMENTS: + FILE := [[fd . --max-depth 3]] + A name of a file to store, default is: ./pdf + + NAME | * := [[fd . --max-depth 3]] + The basename of the generated files + + NUM | *([0-9]) := 0 | 1 | 2 | 3 | 4 + Possible numbers of pages, can be more than 4 + + DEVICE := [[$(cat %DEVICE_FUNCTION)]] + Possible scanner names + + METHOD := ADF | Flatbed + The scanning method to use, not all scanners support both of + these. The default is ADF +EOF +} + +scan_adf() { + device="$1" + sides_per_page="$2" + method="ADF" + for i in $(seq "$sides_per_page"); do + do_until_success \ + "scanimage --format=tiff --progress --source='$method' --device='$device' --batch=%d.tif --batch-increment='$sides_per_page' --batch-start='$i'" \ + "warn 'Retrying scan, as we assume a network error!'" + + if [ "$sides_per_page" -ne 1 ]; then + msg "Finished turn, please change side!" + readp "Press enter to continue" noop + fi + done +} +process_images_adf() { + tiff_temp_path="$1" + output_directory="$2" + name="$3" + + counter=0 + pdf_counter=0 + image_cache="$(mktmp)" + while read -r scanned_image; do + dbg "$scanned_image (scanned_image) at $counter (counter)" + echo "$scanned_image" >>"$image_cache" + : $((counter += 1)) + if [ "$counter" = "$number_of_pages" ]; then + dbg "$counter == $number_of_pages" + counter=0 + convert_images "$image_cache" "${name}_$pdf_counter" "$output_directory" + : $((pdf_counter += 1)) + printf "" >"$image_cache" + fi + done <"$(tmp_pipe fd . "$tiff_temp_path" "|" sort -V)" +} + +scan_flatbed() { + device="$1" + number_of_pages"$2" + method="Flatbed" + for i in $(seq "$number_of_pages"); do + do_until_success \ + "scanimage --format=tiff --progress --source='$method' --device='$device' --output-file=$i.tiff" \ + "warn 'Retrying scan, as we assume a network error!'" + if [ "$number_of_pages" -ne 1 ]; then + msg "Finished turn, please change side!" + readp "Press enter to continue" noop + fi + done +} +process_images_flatbed() { + tiff_temp_path="$1" + output_directory="$2" + name="$3" + + counter=0 + image_cache="$(mktmp)" + while read -r scanned_image; do + echo "$scanned_image" >>"$image_cache" + : $((counter += 1)) + if [ "$counter" = "$number_of_pages" ]; then + counter=0 + convert_images "$image_cache" "$name" "$output_directory" + printf "" >"$image_cache" + fi + done <"$(tmp_pipe fd . "$tiff_temp_path" "|" sort -V)" +} +convert_images() { + image_cache="$1" + pdf_name="$2" + output_dir="$3" + + set -- + while read -r image; do + dbg "setting image: $image" + set -- "$@" "$image" + done <"$image_cache" + + while [ -e "$output_dir/${pdf_name}.pdf" ]; do + pdf_name="${pdf_name}_$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 25)" + done + dbg "using pdf_name: $pdf_name" + convert "$@" -compress jpeg -quality 100 "$output_dir/${pdf_name}.pdf" +} + +scan() { + number_of_pages="$1" + device="$2" + output_directory="$(readlink -f "$3")" + name="$4" + method="$5" + + [ -z "$number_of_pages" ] && die "Parameter 'number_of_pages' is not set!" + [ -z "$device" ] && die "Parameter 'device' is not set!" + [ -z "$output_directory" ] && die "Parameter 'output_directory' is not set!" + [ -z "$name" ] && die "Parameter 'name' is not set!" + [ -z "$method" ] && die "Parameter 'method' is not set!" + + tiff_temp_path="$(mktmp -d)" + cd "$tiff_temp_path" || die "Bug" + + msg "Started scanning..." + if [ "$method" = "Flatbed" ]; then + scan_flatbed "$device" "$number_of_pages" + else + scan_adf "$device" "$number_of_pages" + fi + + msg "Creating output directory..." + mkdir "$output_directory" + cd "$output_directory" || die "Bug" + + msg "Converting images to pdfs..." + if [ "$method" = "Flatbed" ]; then + process_images_flatbed "$tiff_temp_path" "$output_directory" "$name" + else + process_images_adf "$tiff_temp_path" "$output_directory" "$name" + fi +} + +for input in "$@"; do + case "$input" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + esac +done + +number_of_pages="1" +unset device +output_directory="$(pwd)/pdf" +unset name +method="ADF" + +while [ "$#" -ne 0 ]; do + case "$1" in + "--help" | "-h") ;; + "--version" | "-v") ;; + "--out-dir" | "-o") + shift 1 + output_directory="$1" + ;; + "--name" | "-n") + shift 1 + name="$1" + ;; + "--num-pages" | "-p") + shift 1 + number_of_pages="$1" + ;; + "--device" | "-d") + shift 1 + device="$1" + ;; + "--method" | "-m") + shift 1 + method="$1" + ;; + *) + die "Command line arg $1 does not exist. See --help for a list." + ;; + esac + shift 1 +done +scan "$number_of_pages" "$device" "$output_directory" "$name" "$method" diff --git a/pkgs/sources/scripts/source/apps/fupdate.1.md b/pkgs/sources/scripts/source/apps/fupdate.1.md new file mode 100644 index 00000000..710e8fb7 --- /dev/null +++ b/pkgs/sources/scripts/source/apps/fupdate.1.md @@ -0,0 +1,70 @@ +% FUPDATE(1) fupdate 1.0.0 +% Soispha +% May 2023 + +# NAME + +fupdate - updates your flake, while checking for common mistakes + +# SYNOPSIS + +**fupdate** list of \[*flake*|*\<some word>*|*--help*|*-h*\] + +# DESCRIPTION + +Argument can be stacked, this makes it possible to specify multiple targets to be updated in succession. See the Examples section for further details. + +No argument or *flake* +: **fupdate**, when executed without arguments or with *flake*, will update your *flake.lock*, check for duplicate flake inputs, i.e., an input has an input declared, which you have also declared as input, and will run a script called *update.sh*, if you allow it. +The allowance for the script is asked, when you run **fupdate** and the found script is not yet allowed. Furthermore, the allowance is based on the concrete sha256 hash of the script, so any changes will require another allowance. + +**\<some word>** as argument +: If the executable **update-\<some word>** is reachable thought the PATH variable, than this is run. Otherwise, the program will exit. + +# OPTIONS + +**--help**, **-h** +: Displays a help message and exit. + +**--version**, **-v** +: Displays the software version and exit. + +# EXAMPLES + +**fupdate** or **fupdate flake** +: Updates your *flake.lock*. See the Description section for further details. + +**fupdate sys** +: Run the executable **update-sys**, if it exists. See the Description section for further details. + +**fupdate flake sys docs** +: First updates your flake, then, if the command succeeded, runs **update-sys**, afterweich **update-docs** is run. + +# FILES + +*update.sh* +: This is supposed to be a shell script located in your flake base directory, i.e., the directory which contains both a *flake.nix* and a *flake.lock* file. + +*~/.local/share/flake-update/* +: **fupdate** will store the hashes to the allowed *update.sh* files here. + +# BUGS + +Report bugs to <https://codeberg.org/soispha/flake_update/issues>. + +# COPYRIGHT + +Copyright (C) 2023 Soispha + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. diff --git a/pkgs/sources/scripts/source/apps/fupdate.sh b/pkgs/sources/scripts/source/apps/fupdate.sh new file mode 100755 index 00000000..4322610a --- /dev/null +++ b/pkgs/sources/scripts/source/apps/fupdate.sh @@ -0,0 +1,197 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +UPDATE_SCRIPT_NAME="update.sh" +CONFIG_DIRECTORY_PATH="$HOME/.local/share/flake-update" + +# Both are used in version() +# shellcheck disable=SC2034 +AUTHORS="Soispha" +# shellcheck disable=SC2034 +YEARS="2023" + +UPDATE_SCRIPT_NOT_WANTED=false + +# Searches upward for a `UPDATE_SCRIPT_NAME` script +# Returns a path to the script if it exists, otherwise nothing is returned +check_for_update_script() { + dirname="$(search_upward_files "$UPDATE_SCRIPT_NAME")" + if [ "$dirname" ]; then + printf "%s/%s" "$dirname" "$UPDATE_SCRIPT_NAME" + fi +} + +# Checks if a given path to the update script is allowed. +# Takes the path as input +# Return 0, if allowed, 1 if not. +check_for_allowed_update_script() { + update_script="$1" + config_path="${CONFIG_DIRECTORY_PATH}${update_script}" + update_script_hash="$(sha256sum "$update_script")" + if [ -f "$config_path" ]; then + if [ "$(cat "$config_path")" = "$update_script_hash" ]; then + dbg "Recorded hash matches" + return 0 + else + dbg "Recorded hash \'$(cat "$config_path")\' does not match real hash \'$update_script_hash\', assuming not allowed" + return 1 + fi + else + dbg "Path \'$config_path\' does not exist, assuming not allowed" + return 1 + fi +} + +# Asks the user if they want to allow a given script. +# Takes the path as input +ask_to_allow_update_script() { + update_script="$1" + config_path="${CONFIG_DIRECTORY_PATH}${update_script}" + update_script_hash="$(sha256sum "$update_script")" + println "\033[2J" # clear the screen + cat "$update_script" + readp "Do you want to allow this script?[N/y]: " allow + # shellcheck disable=SC2154 + dbg "allow is: $allow" + case "$allow" in + [yY]) + dbg "allowed script" + dbg "storing contents in: $config_path" + mkdir --parents "$(dirname "$config_path")" + print "$update_script_hash" >"$config_path" + ;; + *) + UPDATE_SCRIPT_NOT_ALLOWED=true + ;; + esac +} + +# Runs the provided script and continues to update the nix flake +# Takes the path to the script and the directory to the flake as arguments +# If the path to the update script is empty, it will be ignored +update() { + update_script="$1" + flake_base_dir="$2" + shift 2 + dbg "Provided following args to update script: '$*'" + + cd "$flake_base_dir" || die "Provided dir \'$flake_base_dir\' can not be accessed" + dbg "changed directory to: $flake_base_dir" + + nix flake update + + if ! [ "$update_script" = "" ] && ! [ "$UPDATE_SCRIPT_NOT_WANTED" = "true" ]; then + "$update_script" "$@" + fi + + if grep '[^0-9]_[0-9]' flake.lock >/dev/null; then + batgrep '[^0-9]_[0-9]' flake.lock + die "Your flake.nix contains duplicate inputs!" + fi +} + +help() { + cat <<EOF +This is a Nix flake update manager. + +USAGE: + $NAME [--help | --version] [flake [--no-script] | <some other command>] + +OPTIONS: + --help | -h + Display this help and exit. + + --version | -v + Display version and copyright information and exit. + + --no-script + Avoid running the 'update.sh' script +COMMANDS: + flake + update the flake project + + <some other command> + runs a executable called "update-<some other command>", if it exists +EOF +} + +main() { + if ! [ "$UPDATE_SCRIPT_NOT_ALLOWED" = true ]; then + update_script="$(check_for_update_script)" + flake_base_dir="$(search_flake_base_dir)" # Assume, that the update script is in the base dir + dbg "update_script is: $update_script" + dbg "flake_base_dir is: $flake_base_dir" + + if [ "$update_script" = "" ]; then + update "" "$flake_base_dir" "$@" + elif check_for_allowed_update_script "$update_script" && ! [ "$update_script" = "" ]; then + update "$update_script" "$flake_base_dir" "$@" + else + ask_to_allow_update_script "$update_script" + main "$@" + fi + fi +} + +if [ "$#" -eq 0 ]; then + main + exit 0 +fi + +for input in "$@"; do + case "$input" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + "--no-script" | "-n") + UPDATE_SCRIPT_NOT_WANTED=true + ;; + "--") + end_of_cli_options=true + + # Stop processing args after that marker. + break + ;; + esac + [ "$end_of_cli_options" = "true" ] && break +done + +case "$1" in +"flake") + shift 1 + + # Filter out fupdate specific flags + while [ "$1" != "--" ]; do + # FIXME: This check allows to add a flag multiple times, but this should probably + # not be allowed <2024-03-29> + case "$1" in + "--no-script" | "-n") + shift 1 + ;; + *) + break + ;; + esac + done + + [ "$1" = "--" ] && shift 1 + main "$@" + ;; +*) + command="$1" + shift 1 + [ "$1" = "--" ] && shift 1 + if which update-"$command" >/dev/null 2>&1; then + update-"$command" "$@" + else + die "command \"update-$command\" is not executable, or does not exist" + fi + ;; +esac diff --git a/pkgs/sources/scripts/source/apps/git-edit-index.sh b/pkgs/sources/scripts/source/apps/git-edit-index.sh new file mode 100755 index 00000000..e73dc53c --- /dev/null +++ b/pkgs/sources/scripts/source/apps/git-edit-index.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# needed for help() and version +# shellcheck disable=2034 +AUTHORS="Soispha" +# shellcheck disable=2034 +YEARS="2024" +# shellcheck disable=2034 +VERSION="1.0.0" + +# NAME is from the wrapper +# shellcheck disable=SC2269 +NAME="$NAME" + +help() { + cat <<EOF +Edit a file from the index. This script does not touch the unstaged variant of the file. + +USAGE: + $NAME [OPTIONS] [--] FILES.. + +OPTIONS: + -- + Stop parsing options and interpret everything as an file. + + --help | -h + Display this help and exit. + + --version | -v + Display version and copyright information and exit. +ARGUMENTS: + FILES := [[ git diff --name-only --cached --diff-filter=AM ]] + The files to edit. + +EOF +} + +GIT_DIR="$(git rev-parse --show-toplevel)" +materialize_file() { + git diff --cached "$1" >"$GIT_DIR/.git/EDIT_INDEX_PATCH" + + git add "$1" + git restore --staged "$1" + cat "$1" >"$GIT_DIR/.git/EDIT_INDEX_FILE" + git restore "$1" + + git apply "$GIT_DIR/.git/EDIT_INDEX_PATCH" + "$EDITOR" "$1" + + git add "$1" + mv "$GIT_DIR/.git/EDIT_INDEX_FILE" "$1" +} + +edit() { + files_to_add="$(mktmp)" + realpath --relative-to=. "$@" >"$files_to_add" + + index_files="$(mktmp)" + git diff --name-only --cached --diff-filter=AM >"$index_files" + + while read -r file; do + if grep -q "$file" "$files_to_add"; then + sed -i "s|$file||" "$files_to_add" + materialize_file "$file" + fi + done <"$index_files" + + files_to_check="$(mktmp)" + clean "$files_to_add" >"$files_to_check" + if [ "$(wc -l <"$files_to_check")" -gt 0 ]; then + warn "Could not edit every file:" + cat "$files_to_add" + fi +} + +for arg in "$@"; do + case "$arg" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + "--") + end_of_cli_options=true + ;; + esac + [ "$end_of_cli_options" = "true" ] && break +done + +edit "$@" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/small_functions/brightness.sh b/pkgs/sources/scripts/source/small_functions/brightness.sh new file mode 100755 index 00000000..a7272279 --- /dev/null +++ b/pkgs/sources/scripts/source/small_functions/brightness.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +help() { + cat <<EOF +This is a system brightness manager + +USAGE: + $NAME up [VALUE] | down [VALUE] + +OPTIONS: + --help | -h + Output this help and exit. + + --version | -v + Output the version and exit. + +COMMANDS: + up [VALUE] + Increase the brightness by VALUE or 5%. + + down [VALUE] + Decrease the brightness by VALUE or 5%. + +ARGUMENTS: + VALUE := [[seq 0 100]] + The amount to increase/decrease the brightness. In percentage +EOF +} + +BACKLIGHT="/sys/class/%BACKLIGHT_NAME" + +brightness() { + offset="$1" + + max="$(cat $BACKLIGHT/max_brightness)" + cur="$(cat $BACKLIGHT/brightness)" + percentage="$(echo | awk --assign=cur="$cur" --assign=max="$max" '{printf cur / max}')" + + new="$(echo | awk --assign=per="$percentage" --assign=offset="$offset" '{printf per + (offset / 10)}')" + + output="$(echo | awk --assign=new="$new" --assign=max="$max" '{printf max * new}')" + + msg "echo \"$output\" > $BACKLIGHT/brightness" +} + +for arg in "$@"; do + case "$arg" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + esac +done + +case "$1" in +"up") + shift 1 + value="5" + [ -n "$1" ] && value="$1" + brightness "+$value" + ;; +"down") + shift 1 + value="-5" + [ -n "$1" ] && value="$1" + brightness "-$value" + ;; +*) + die "The command '$1' does not exist! See '--help' for a list" + ;; +esac + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/small_functions/nato.py b/pkgs/sources/scripts/source/small_functions/nato.py new file mode 100755 index 00000000..e9d15f56 --- /dev/null +++ b/pkgs/sources/scripts/source/small_functions/nato.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# originally from here: https://cgit.pacien.net/desktop-utilities/ + +import sys + +alphabet = { + "nato": { + "A": "Alfa", # No idea why this is not just 'Alpha' .. + "B": "Bravo", + "C": "Charlie", + "D": "Delta", + "E": "Echo", + "F": "Foxtrot", + "G": "Golf", + "H": "Hotel", + "I": "India", + "J": "Juliett", + "K": "Kilo", + "L": "Lima", + "M": "Mike", + "N": "November", + "O": "Oscar", + "P": "Papa", + "Q": "Quebec", + "R": "Romeo", + "S": "Sierra", + "T": "Tango", + "U": "Uniform", + "V": "Victor", + "W": "Whiskey", + "X": "X-ray", + "Y": "Yankee", + "Z": "Zulu", + "0": "Nadazero", + "1": "Unaone", + "2": "Bissotwo", + "3": "Terrathree", + "4": "Kartefour", + "5": "Pantafive", + "6": "Soxisix", + "7": "Setteseven", + "8": "Oktoeight", + "9": "Novenine", + ",": "Comma", + "/": "Forward slash", + ".": "Stop/Decimal", + }, + "german": { + "A": "Aachen", + "Ä": "Umlaut Aachen", + "B": "Berlin", + "C": "Chemnitz", + "D": "Düsseldorf", + "E": "Essen", + "F": "Frankfurt", + "G": "Goslar", + "H": "Hamburg", + "I": "Ingelheim", + "J": "Jena", + "K": "Köln", + "L": "Leipzig", + "M": "München", + "N": "Nürnberg", + "O": "Offenbach", + "Ö": "Umlaut Offenbach", + "P": "Potsdam", + "Q": "Quickborn", + "R": "Rostock", + "S": "Salzwedel", + "ẞ": "Eszett", + "T": "Tübingen", + "U": "Unna", + "Ü": "Umlaut Unna", + "V": "Völklingen", + "W": "Wuppertal", + "X": "Xanten", + "Y": "Ypsilon", + "Z": "Zwickau", + }, +} + + +def str_to_telephony(phrase, language): + language_alphabet = alphabet[language] + + return [ + language_alphabet[c] if c in language_alphabet else c for c in phrase.upper() + ] + + +language = sys.argv[1] +if language not in ["nato", "german"]: + print( + f"Langugae '{language}' is not a valid language, only 'nato' and 'german' are!", + file=sys.stderr, + ) + exit(1) + +print( + "\n".join( + str_to_telephony( + " ".join(sys.argv[2:]), + language, + ) + ) +) diff --git a/pkgs/sources/scripts/source/small_functions/screenshot_persistent.sh b/pkgs/sources/scripts/source/small_functions/screenshot_persistent.sh new file mode 100755 index 00000000..4308b8d2 --- /dev/null +++ b/pkgs/sources/scripts/source/small_functions/screenshot_persistent.sh @@ -0,0 +1,22 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# only generate a path (this could lead to a time-of-check/time-of-use bug) +tmp="$(mktmp --dry-run)" + +if grim -g "$(slurp)" "$tmp"; then + name="$(rofi -dmenu -p "Name of screenshot: " -l 0)" + screen_shot_path="$HOME/media/pictures/screenshots/$name.png" + while [ -f "$screen_shot_path" ]; do + notify-send "Warning" 'Screenshot name already in use!' + name="$(rofi -dmenu -p "New name of screenshot: " -l 0)" + screen_shot_path="$HOME/media/pictures/screenshots/$name.png" + done + + mv "$tmp" "$screen_shot_path" + alacritty -e lf -command ":{{ set sortby atime; set reverse!; }}" +fi + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/small_functions/screenshot_temporary.sh b/pkgs/sources/scripts/source/small_functions/screenshot_temporary.sh new file mode 100755 index 00000000..8968ca79 --- /dev/null +++ b/pkgs/sources/scripts/source/small_functions/screenshot_temporary.sh @@ -0,0 +1,8 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +grim -g "$(slurp)" | wl-copy + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/small_functions/update-sys.sh b/pkgs/sources/scripts/source/small_functions/update-sys.sh new file mode 100755 index 00000000..d28247f6 --- /dev/null +++ b/pkgs/sources/scripts/source/small_functions/update-sys.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +help() { + cat <<EOF +This is a NixOS System flake update manager. + +USAGE: + $NAME [--branch <branchname>] [--help] + +OPTIONS: + --branch | -b BRANCHNAME + select a branch to update from. + + --mode | -m MODE + select a mode to update with + + --help | -h + output this help. +ARGUMENTS: + BRANCHNAME := [[ git branch --list --format '%(refname:short)' ]] + The name of the branch to deploy the config from + + MODE := switch|boot|test|build|dry-build|dry-activate|edit|repl|build-vm|build-vm-with-bootloader + See the 'nixos-rebuild' manpage for more information about these modes. +EOF + exit "$1" +} +default_branch=$(mktmp) +BRANCH="" + +while [ "$#" -gt 0 ]; do + case "$1" in + "--help" | "-h") + help 0 + ;; + "--branch" | "-b") + if [ -n "$2" ]; then + BRANCH="$2" + else + error "$1 requires an argument" + help 1 + fi + shift 2 + ;; + "--mode" | "-m") + if [ -n "$2" ]; then + MODE="$2" + else + error "$1 requires an argument" + help 1 + fi + shift 2 + ;; + *) + error "the option $1 does not exist!" + help 1 + ;; + esac +done + +cd /etc/nixos || die "No /etc/nixos" +msg "Starting system update..." +git remote update origin --prune >/dev/null 2>&1 +if ! [ "$BRANCH" = "" ]; then + git switch "$BRANCH" >/dev/null 2>&1 && msg2 "Switched to branch '$BRANCH'" +fi +msg2 "Updating git repository..." +git pull --rebase + +git remote show origin | grep 'HEAD' | cut -d':' -f2 | sed -e 's/^ *//g' -e 's/ *$//g' >"$default_branch" & + +msg2 "Updating system..." +if [ -n "$MODE" ]; then + nixos-rebuild "$MODE" +else + nixos-rebuild switch +fi + +git switch "$(cat "$default_branch")" >/dev/null 2>&1 && msg2 "Switched to branch '$(cat "$default_branch")'" +msg "Finished Update!" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/specific/neorg/neorg_id_function.sh b/pkgs/sources/scripts/source/specific/neorg/neorg_id_function.sh new file mode 100755 index 00000000..865ecacf --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/neorg_id_function.sh @@ -0,0 +1,16 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +context="$(task _get rc.context)" +if [ "$context" ]; then + filter="project:$context" +else + filter="0-10000" +fi +tasks="$(task "$filter" _ids)" + +if [ "$tasks" ]; then + echo "$tasks" | xargs task _zshids | awk -F: -v q="'" '{gsub(/'\''/, q "\\" q q ); print $1 ":" q $2 q}' +fi diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/add.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/add.sh new file mode 100755 index 00000000..5a830a10 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/add.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env dash + +add0open_taskwarrior_project_file() { + task_project_file="%TASK_PROJECT_FILE" + + cd "$(dirname $task_project_file)" || die "BUG: task_project_file ('$task_project_file') can't be accessed" + + git_dir="$(search_flake_base_dir)" + [ "$git_dir" ] || die "(BUG): No git directory?" + cd "$git_dir" || die "Unreachable, this MUST exists" + + nvim "$task_project_file" + git add "$task_project_file" + + base_task_project_file_path="$(awk "{ gsub(\"$git_dir/\", \"\", \$0); print }" "$(ptmp "$task_project_file")")" + git add $task_project_file + + # Check that only the project file has been added (and that our file is actually + # modified) + if git status --porcelain=v2 | awk -v path="$base_task_project_file_path" 'BEGIN { hit = 0 } { if ($2 ~ /A./ || $2 ~ /M./) { if ($NF ~ path) { hit = 1 } else { hit = 0; exit 1 } } } END { if (hit == 1) { exit 0 } else { exit 1 } }'; then + git commit --verbose --message="chore($(dirname "$base_task_project_file_path")): Update" + fi +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/context.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/context.sh new file mode 100755 index 00000000..7095847d --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/context.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env dash + +context0open_current_task_context() { + current_context="$(utils0get_current_context)" + + if [ "$current_context" ]; then + context_path="$(utils0get_current_context_path "$current_context")" + + extended_neorg_project_dir="$(utils0get_neorg_project_dir)" + cd "$extended_neorg_project_dir" || die "(BUG?): Can not access the project dir: $extended_neorg_project_dir" + + nvim "$extended_neorg_project_dir/$context_path" + + git add . + git commit --message="chore($(dirname "$context_path")): Update" --no-gpg-sign + else + warn "No context active" + fi +} + +context0open_current_task_context_at_task_id() { + task_id="$1" + current_context="$(utils0get_current_context)" + + if [ "$current_context" ]; then + context_path="$(utils0get_current_context_path "$current_context")" + extended_neorg_project_dir="$(utils0get_neorg_project_dir)" + task_uuid="$(task "$task_id" uuids)" + + cd "$extended_neorg_project_dir" || die "(BUG?): Can not access the project dir: $extended_neorg_project_dir" + + if ! grep -q "% $task_uuid" "$extended_neorg_project_dir/$context_path"; then + echo "* TITLE (% $task_uuid)" >>"$extended_neorg_project_dir/$context_path" + fi + + nvim "$extended_neorg_project_dir/$context_path" -c "/% $task_uuid" + + git add . + git commit --message="chore($(dirname "$context_path")): Update" --no-gpg-sign + else + warn "No context active" + fi +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/dmenu.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/dmenu.sh new file mode 100755 index 00000000..5a138982 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/dmenu.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env dash + +dmenu0open_context_in_browser() { + project="$(echo "%ALL_PROJECTS_PIPE" | rofi -sep "|" -dmenu)" + + if [ "$project" ]; then + [ -d "%NEORG_REVIEW_PATH" ] || mkdir --parents "%NEORG_REVIEW_PATH" + [ -f "%NEORG_REVIEW_PATH/$project.lock" ] || touch "%NEORG_REVIEW_PATH/$project.lock" + project0open_project_in_browser "$project" + else + notify-send "(neorg/dmenu) No project selected" + exit 1 + fi +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_start.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_start.sh new file mode 100755 index 00000000..2423dd44 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_start.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env dash + +fstart0start_new_task() { + task_id="$1" + fstop0stop_current_task + task start "$task_id" +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_stop.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_stop.sh new file mode 100755 index 00000000..e4ff0b94 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/f_stop.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env dash + +fstop0stop_current_task() { + # we ensured that only one task may be active + active="$(task +ACTIVE _ids)" + [ "$active" ] && task stop "$active" +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/list.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/list.sh new file mode 100755 index 00000000..10659457 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/list.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env dash + +list0list_all_contexts_newline() { + print "%ALL_PROJECTS_NEWLINE" +} +list0list_all_contexts_comma() { + print "%ALL_PROJECTS_COMMA" +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/project.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/project.sh new file mode 100755 index 00000000..64591850 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/project.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env dash + +project0open_current_context_in_browser() { + current_context="$(utils0get_current_context)" + [ "$current_context" ] || die "No current context to use" + project0open_context_in_browser "$(utils0context2project "$current_context")" +} + +project0open_project_in_browser() { + project="$1" + [ "$project" ] || die "BUG: No context supplied to project0open_context_in_browser" + + old_context="$(utils0get_current_context)" + # We have ensured that only one task may be active + old_started_task="$(task +ACTIVE _ids)" + + tracking="$(mktmp)" + task "project:$project" _ids | xargs --no-run-if-empty task _zshids >"$tracking" + task context "$(utils0project2context "$project")" + + while read -r description; do + desc="$(echo "$description" | awk -F: '{print $2}')" + if [ "$desc" = "tracking" ]; then + task_id="$(echo "$description" | awk -F: '{print $1}')" + notify-send "(Neorg)" "Starting task $project -> $desc" + task start "$task_id" + break + fi + done <"$tracking" + + firefox -P "$project" + + task stop "$task_id" + [ "$old_started_task" ] && task start "$old_started_task" + + if [ "$old_context" ]; then + task context "$old_context" + else + task context none + fi +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/review.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/review.sh new file mode 100755 index 00000000..a0a9ab8d --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/review.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env dash + +review0start() { + for project in $(list0list_all_contexts_newline); do + if [ -f "%NEORG_REVIEW_PATH/$project.lock" ]; then + msg "Reviewing '$project'" + notify-send "Neorg" "Reviewing '$project'" + firefox -P "$project" + rm "%NEORG_REVIEW_PATH/$project.lock" + fi + done +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/utils.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/utils.sh new file mode 100755 index 00000000..c3843e8e --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/utils.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env dash + +# Runs it's first argument and then the second, regardless if the first failed or +# succeeded +utils0chain() { + eval "$1" + eval "$2" +} + +utils0get_current_context() { + current_context="$(task _get rc.context)" + printf "%s\n" "$current_context" +} + +utils0get_current_context_path() { + current_context="$1" + context_path="$(task _get rc.context."$current_context".rc.neorg_path 2>/dev/null)" + if ! [ "$context_path" ]; then + context_path="$(grep "context.$current_context.rc.neorg_path" "%HOME_TASKRC" | awk 'BEGIN {FS="="} {print $2}')" + [ "$context_path" ] || die "All contexts should have a 'neorg_path' set!" + fi + printf "%s\n" "$context_path" +} + +utils0get_neorg_project_dir() { + # Perform shell expansion of Tilde + neorg_project_dir="$(sed "s|^~|$HOME|" "$(ptmp "%DEFAULT_NEORG_PROJECT_DIR")")" + printf "%s\n" "$neorg_project_dir" +} + +utils0project2context() { + project="$1" + context="$(sed 's|\.|_|g' "$(ptmp "$project")")" + printf "%s\n" "$context" +} +utils0context2project() { + context="$1" + project="$(sed 's|_|\.|g' "$(ptmp "$context")")" + printf "%s\n" "$project" +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/functions/workspace.sh b/pkgs/sources/scripts/source/specific/neorg/sh/functions/workspace.sh new file mode 100755 index 00000000..d5eb2fca --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/functions/workspace.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env dash + +workspace0open_neorg_workspace() { + workspace="$1" + nvim -c "NeorgStart" -s "$(ptmp ":Neorg workspace $workspace\n")" +} +workspace0open_neorg_workspace_prompt() { + nvim -c "NeorgStart" -s "$(ptmp ":Neorg workspace ")" +} diff --git a/pkgs/sources/scripts/source/specific/neorg/sh/main.sh b/pkgs/sources/scripts/source/specific/neorg/sh/main.sh new file mode 100755 index 00000000..559351b9 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/neorg/sh/main.sh @@ -0,0 +1,164 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# load dependencies +. ./functions/add.sh +. ./functions/context.sh +. ./functions/dmenu.sh +. ./functions/f_start.sh +. ./functions/f_stop.sh +. ./functions/list.sh +. ./functions/project.sh +. ./functions/utils.sh +. ./functions/workspace.sh +. ./functions/review.sh + +# these are used in version() +# shellcheck disable=2034 +AUTHORS="Soispha" +# shellcheck disable=2034 +YEARS="2023" + +NAME="neorg" + +help() { + cat <<EOF +This is the core interface to the system-integrated task management + +USAGE: + $NAME [OPTIONS] [COMMAND] + +OPTIONS: + --help | -h + Display this help and exit. + + --version | -v + Display version and copyright information and exit. +COMMANDS: + task [ID] + Open the neorg context associated with the current context and + the uuid of the task with id ID. Without ID, it'll open the + current context's norg file. + If no context is set, drops you to the selection prompt + + dmenu + Select a project in dmenu mode. This will give you all projects + and exectute the selected one as in 'neorg projects <selected>' + + workspace [WS] + The neorg workspace (WS) to open at startup, an empty value drops + you at a prompt to enter the workspace yourself. + + project [P] + Opens the webbrowser with either the context (P) or + the current active context as argument if no context is supplied + + list + Lists all available contexts + + add + Allows you to quickly add projects + + fstart ID + Starts the task (ID) but only after it stooped + the previous active task, if it existed. + + fstop + Stops the current active task + + review + Review all firefox tabs +ARGUMENTS: + ID | *([0-9]) := [[%ID_GENERATION_FUNCTION]] + The function displays all possible IDs of the eligable tasks. + + WS := %ALL_WORKSPACES + All possible workspaces + + P := %ALL_PROJECTS_PIPE + The possible project + +EOF +} + +for arg in "$@"; do + case "$arg" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + esac +done + +while [ "$#" -ne 0 ]; do + case "$1" in + "t"*) # task + shift 1 + task_id="$1" + [ "$task_id" ] || utils0chain context0open_current_task_context "exit 0" + context0open_current_task_context_at_task_id "$task_id" + exit 0 + ;; + "w"*) # workspace + shift 1 + workspace_to_open="$1" + # TODO: Exit with 1 on error, instead of the 0 <2023-10-20> + [ "$workspace_to_open" ] || utils0chain workspace0open_neorg_workspace_prompt "exit 0" + workspace0open_neorg_workspace "$workspace_to_open" + exit 0 + ;; + "p"*) # project + shift 1 + project_to_open="$1" + # TODO: Exit with 1 on error, instead of the 0 <2023-10-20> + [ "$project_to_open" ] || utils0chain project0open_current_context_in_browser "exit 0" + if ! grep -q "$project_to_open" "$(ptmp "%ALL_PROJECTS_NEWLINE")"; then + die "Your project ('$project_to_open') is not in the list of available projects: +%ALL_PROJECTS_COMMA" + fi + project0open_project_in_browser "$project_to_open" + exit 0 + ;; + "l"*) # list + list0list_all_contexts_newline + exit 0 + ;; + "a"*) # add-project + add0open_taskwarrior_project_file + exit 0 + ;; + "d"*) # dmenu + dmenu0open_context_in_browser + exit 0 + ;; + "fsta"*) # fstart + shift 1 + task_id="$1" + [ "$task_id" ] || die "No task id provided to fstart" + fstart0start_new_task "$task_id" + exit 0 + ;; + "fsto"*) # fstop + fstop0stop_current_task + exit 0 + ;; + "r"*) # review + shift 1 + review0start + exit 0 + ;; + *) + die "Command '$1' does not exist! Please look at:\n $NAME --help" + exit 0 + ;; + esac +done + +context0open_current_task_context +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/specific/spodi/sh/download.sh b/pkgs/sources/scripts/source/specific/spodi/sh/download.sh new file mode 100755 index 00000000..fe9746c8 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/spodi/sh/download.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env dash + +download_to_down() { + DOWNLOAD_DIRECTORY="%XDG_MUSIC_DIR/down/spotify" + + already_downloaded_files="$(mktmp)" + fd . "$DOWNLOAD_DIRECTORY" --exclude spotdl.log --exclude spotdl-errors.log >"$already_downloaded_files" + if [ -z "$NO_CHECK" ] && [ "$(wc -l <"$already_downloaded_files")" -ne 0 ]; then + die "something is already downloaded" + fi + # [ -e "$DOWNLOAD_DIRECTORY/spotdl.log" ] && rm "$DOWNLOAD_DIRECTORY/spotdl.log" + + download "$1" "$DOWNLOAD_DIRECTORY" +} + +download() { + download_url="$1" + output_path="$2" + + config="$(mktmp)" + cat <<EOF | clean >"$config" +# Main options +--audio slider-kz bandcamp youtube-music piped youtube soundcloud +--lyrics genius musixmatch azlyrics synced + +# FFmpeg options +--ffmpeg ffmpeg +--threads 16 +--bitrate 256k + +# Spotify options +--cache-path %XDG_CACHE_HOME/spotdl/.spotipy + +# Output options +--preload +--format opus +--output {artists}_-_{title} +--print-errors +--save-errors $output_path/spotdl-errors.log +# TODO: Reactive whence spotdl support for these has improved <2023-12-19> +# --generate-lrc +--overwrite skip + +# Misc options +--log-level INFO +EOF + + cd "$output_path" || die "BUG: no $output_path" + touch "$output_path/spotdl-errors.log" + + # The sub shell needs to be unquoted, as the arguments may not be treated as one. + # shellcheck disable=2046 + unbuffer spotdl $(cat "$config") download "$download_url" | tee "$output_path/spotdl.log" + + [ -d ~/.spotdl ] && rm -r ~/.spotdl +} + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/specific/spodi/sh/update.sh b/pkgs/sources/scripts/source/specific/spodi/sh/update.sh new file mode 100755 index 00000000..a289cf58 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/spodi/sh/update.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env dash + +update() { + UPDATE_DIRECTORY="%XDG_MUSIC_DIR/artists" + UPDATE_CONFIG_FILE="%XDG_MUSIC_DIR/artists/update.conf" + + if ! [ -f "$UPDATE_CONFIG_FILE" ]; then + error="$( + cat <<EOF +Please provide an update config file at: '$UPDATE_CONFIG_FILE'. + +The 'update.conf' file should follow this pattern: +<path_to_artist>/<artist_name>|<spotify_url> + +All comments and empty lines are ignored +EOF + )" + die "$error" + fi + + config_file="$(mktmp)" + clean "$UPDATE_CONFIG_FILE" >"$config_file" + + while IFS="|" read -r artist url; do + full_artist="$UPDATE_DIRECTORY/$artist" + [ -d "$full_artist" ] || mkdir --parents "$full_artist" + [ -d "$full_artist/update" ] || mkdir --parents "$full_artist/update" + [ -d "$full_artist/all" ] || mkdir --parents "$full_artist/all" + [ -d "$full_artist/filtered" ] || mkdir --parents "$full_artist/filtered" + + while read -r file; do + ln --symbolic --relative "$file" "$full_artist/update/$(basename "$file")" + done <"$(tmp fd --type file --extension opus . "$full_artist/all")" + + msg2 "Updating $artist with url: '$url'" + download "$url" "$full_artist/update" + + while read -r file; do + mv "$file" "$full_artist/all" + ln --symbolic --relative "$full_artist/all/$(basename "$file")" "$full_artist/filtered/$(basename "$file")" + done <"$(tmp fd --type file --extension opus . "$full_artist/update")" + + while read -r file; do + rm "$file" + done <"$(tmp fd --type symlink --extension opus . "$full_artist/update")" + + cp "$full_artist/update/spotdl.log" "$full_artist/all/spotdl.$(date +%Y_%m_%d).log" + cp "$full_artist/update/spotdl-errors.log" "$full_artist/all/spotdl-errors.$(date +%Y_%m_%d).log" + done <"$config_file" +} + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/specific/spodi/spodi.sh b/pkgs/sources/scripts/source/specific/spodi/spodi.sh new file mode 100755 index 00000000..475fd48a --- /dev/null +++ b/pkgs/sources/scripts/source/specific/spodi/spodi.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# these are used in version() +# shellcheck disable=2034 +AUTHORS="Soispha" +# shellcheck disable=2034 +YEARS="2023" + +# load dependencies +. ./sh/update.sh +. ./sh/download.sh + +help() { + cat <<EOF +This is a small wrapper around downloading things from spotify + +USAGE: + $NAME [OPTIONS] COMMAND + +OPTIONS: + --help | -h + Display this help and exit. + + --version | -v + Display version and copyright information and exit. +COMMANDS: + update + Read the artist.conf file and download all newly released things + + download URL + Download a specific url to the DOWNLOAD_DIRECTORY +EOF +} + +for arg in "$@"; do + case "$arg" in + "--help" | "-h") + help + exit 0 + ;; + "--version" | "-v") + version + exit 0 + ;; + esac +done + +case "$1" in +"update") + shift 1 + update + exit 0 + ;; +"download") + shift 1 + download_url="$1" + [ -z "$download_url" ] && die "You need to provide a download url" + download_to_down "$download_url" + exit 0 + ;; +*) + die "Command '$1' is not know" + help + exit 1 + ;; +esac + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/specific/ytcc/description.sh b/pkgs/sources/scripts/source/specific/ytcc/description.sh new file mode 100755 index 00000000..ae9107b9 --- /dev/null +++ b/pkgs/sources/scripts/source/specific/ytcc/description.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +jq --raw-output '.description' "$XDG_RUNTIME_DIR/ytcc/running" | fmt -u -s | less + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/battery.sh b/pkgs/sources/scripts/source/wrappers/battery.sh new file mode 100755 index 00000000..e650ba5d --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/battery.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +capacity="$(cat /sys/class/power_supply/BAT0/capacity)" +status="$(cat /sys/class/power_supply/BAT0/status)" + +printf "%s%% (%s)\n" "$capacity" "$status" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/hibernate.sh b/pkgs/sources/scripts/source/wrappers/hibernate.sh new file mode 100755 index 00000000..30868fd1 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/hibernate.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +context="$(task _get rc.context)" +[ "$context" ] && task context none + +# We have ensured that only one task is active +active="$(task +ACTIVE _ids)" +[ "$active" ] && task stop "$active" + +systemctl hibernate "$@" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/ll.sh b/pkgs/sources/scripts/source/wrappers/ll.sh new file mode 100755 index 00000000..f689ba44 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/ll.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +last_directory="$(mktemp)" + +command lf -last-dir-path="$last_directory" "$@" + +dir="$(cat "$last_directory")" +cd "$dir" || die "$dir does not exist!" +rm "$last_directory" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/lock.sh b/pkgs/sources/scripts/source/wrappers/lock.sh new file mode 100755 index 00000000..3101ef9a --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/lock.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +context="$(task _get rc.context)" +[ "$context" ] && task context none + +# We have ensured that only one task is active +active="$(task +ACTIVE _ids)" +[ "$active" ] && task stop "$active" + +swaylock + +[ "$active" ] && task start "$active" + +[ "$context" ] && task context "$context" +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/lyrics.sh b/pkgs/sources/scripts/source/wrappers/lyrics.sh new file mode 100755 index 00000000..02a147c8 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/lyrics.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +( + cd "$XDG_MUSIC_DIR" || die "No music dir!" + exiftool "$(mpc --format '%file%' current)" -json | jq '.[0].Lyrics' -r | less +) + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/mpc-fav.sh b/pkgs/sources/scripts/source/wrappers/mpc-fav.sh new file mode 100755 index 00000000..795a4875 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/mpc-fav.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +FAV_DIR="$XDG_MUSIC_DIR/playlists/favourites" + +cd "$XDG_MUSIC_DIR" || die "No music dir!" + +[ -d "$FAV_DIR" ] || mkdir --parents "$FAV_DIR" + +ln -sr "$(mpc --format '%file%' current)" "$FAV_DIR/" || die "Link failed!" + +mpc update + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/mpc-rm.sh b/pkgs/sources/scripts/source/wrappers/mpc-rm.sh new file mode 100755 index 00000000..94e0634b --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/mpc-rm.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +cd "$XDG_MUSIC_DIR" || die "No music dir!" +trash-put "$(mpc --format '%file%' current)" +mpc del 0 + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/mpc.sh b/pkgs/sources/scripts/source/wrappers/mpc.sh new file mode 100755 index 00000000..5aae5cdb --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/mpc.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +case "$1" in +"rm") + shift 1 + mpc-rm "$@" + ;; +"fav") + shift 1 + mpc-fav "$@" + ;; +*) + mpc "$@" + ;; +esac + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/show.sh b/pkgs/sources/scripts/source/wrappers/show.sh new file mode 100755 index 00000000..ae2bdb13 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/show.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# Maybe add `--quit-if-one-screen` +less --redraw-on-quit "$@" + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/sort_song.sh b/pkgs/sources/scripts/source/wrappers/sort_song.sh new file mode 100755 index 00000000..e2978507 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/sort_song.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +case "$("$1" | tr '[:upper:]' '[:lower:]')" in +"lyrics") + filter="LYRICS" + directory="lyrics" + ;; +"instrumental") + filter="INSTRUMENTAL" + directory="instrumental" + ;; +*) + die "Expected 'instrumental|lyrics' but got '$1'" + ;; +esac + +process() { + mediainfo --Output=JSON "$1" | jq '.media.track | map(.Lyrics) | join("")' +} + +mkdir "../$directory" + +fd . --extension=opus | while read -r file; do + if [ "$(process "$file")" = '""' ] || [ "$(process "$file")" = '"Instrumental"' ] || [ "$(process "$file")" = '"instrumental"' ]; then + echo "INSTRUMENTAL::$file" + else + echo "LYRICS::$file" + fi +done | grep "$filter" | awk 'BEGIN {FS="::"}{print $2}' | while read -r file; do ln -s "../all/$file" "../$directory/$file"; done + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/virsh-del.sh b/pkgs/sources/scripts/source/wrappers/virsh-del.sh new file mode 100755 index 00000000..c3de5484 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/virsh-del.sh @@ -0,0 +1,10 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +virsh destroy "$1" +virsh undefine "$1" --nvram +virsh vol-delete --pool default "$1".qcow2 + +# vim: ft=sh diff --git a/pkgs/sources/scripts/source/wrappers/yti.sh b/pkgs/sources/scripts/source/wrappers/yti.sh new file mode 100755 index 00000000..a69ffa74 --- /dev/null +++ b/pkgs/sources/scripts/source/wrappers/yti.sh @@ -0,0 +1,33 @@ +#! /usr/bin/env dash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +DOWN_DIR=/home/soispha/media/music/down/youtube + +tmp=$(mktmp) +config=$(mktmp) + +for e in "$DOWN_DIR"/*.opus; do echo "$e" >>"$tmp"; done +[ "$(wc -l "$tmp" | awk '{print $1}')" -gt 2 ] && die "something is already downloaded" + +cat <<EO >"$config" +--paths home:"$DOWN_DIR" +#--output %(fulltitle) +--restrict-filenames +--no-overwrites +--no-write-info-json +--clean-info-json +--prefer-free-formats +#--format mp3 +--extract-audio +--audio-quality 0 +--audio-format best +EO + +rm "$DOWN_DIR/yt-dlp.log" +cd "$DOWN_DIR" || die "BUG: no $DOWN_DIR" + +unbuffer yt-dlp --config-location "$config" "$1" | tee "$DOWN_DIR/yt-dlp.log" + +# vim: ft=sh diff --git a/pkgs/sources/snap-sync-forked/default.nix b/pkgs/sources/snap-sync-forked/default.nix new file mode 100644 index 00000000..5b086a5a --- /dev/null +++ b/pkgs/sources/snap-sync-forked/default.nix @@ -0,0 +1,24 @@ +{sysLib}: [ + (final: prev: { + snap-sync-forked = sysLib.writeShellScript { + name = "snap-sync-forked"; + src = ./snap-sync-forked.sh; + dependencies = with prev; [ + bash + btrfs-progs + coreutils + gawk + gnugrep + snapper + util-linux + + # optional: + libnotify + openssh + pv + rsync + sudo + ]; + }; + }) +] diff --git a/pkgs/sources/snap-sync-forked/snap-sync-forked.sh b/pkgs/sources/snap-sync-forked/snap-sync-forked.sh new file mode 100755 index 00000000..3d9c1ac9 --- /dev/null +++ b/pkgs/sources/snap-sync-forked/snap-sync-forked.sh @@ -0,0 +1,534 @@ +#!/usr/bin/env bash + +# shellcheck source=/dev/null +SHELL_LIBRARY_VERSION="2.1.2" . %SHELL_LIBRARY_PATH + +# +# snap-sync +# https://github.com/wesbarnett/snap-sync +# Copyright (C) 2016-2021 Wes Barnett + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., + +# ------------------------------------------------------------------------- + +# Takes snapshots of each snapper configuration. It then sends the snapshot to +# a location on an external drive. After the initial transfer, it does +# incremental snapshots on later calls. It's important not to delete the +# snapshot created on your system since that will be used to determine the +# difference for the next incremental snapshot. + +set -o errtrace + +version="0.7" +name="snap-sync" + +printf "\nsnap-sync version %s, Copyright (C) 2016-2021 Wes Barnett\n" "$version" +printf "snap-sync comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. See the license for more information. \n\n" + +# The following line is modified by the Makefile or +# find_snapper_config script +SNAPPER_CONFIG=/etc/sysconfig/snapper + +donotify=0 +if ! command -v notify-send &>/dev/null; then + donotify=1 +fi + +doprogress=0 +if ! command -v pv &>/dev/null; then + doprogress=1 +fi + +error() { + printf "==> ERROR: %s\n" "$@" + notify_error 'Error' 'Check journal for more information.' +} >&2 + +die() { + error "$@" + exit 1 +} + +traperror() { + printf "Exited due to error on line %s.\n" "$1" + printf "exit status: %s\n" "$2" + printf "command: %s\n" "$3" + printf "bash line: %s\n" "$4" + printf "function name: %s\n" "$5" + exit 1 +} + +trapkill() { + die "Exited due to user intervention." +} + +trap 'traperror ${LINENO} $? "$BASH_COMMAND" $BASH_LINENO "${FUNCNAME[@]}"' ERR +trap trapkill SIGTERM SIGINT + +usage() { + cat <<EOF +$name $version +Usage: $name [options] + +Options: + -c, --config <config> snapper configuration to backup + -d, --description <desc> snapper description + -h, --help print this message + -n, --noconfirm do not ask for confirmation + -k, --keepold keep old incremental snapshots instead of deleting them + after backup is performed + -p, --port <port> remote port; used with '--remote'. + -q, --quiet do not send notifications; instead print them. + -r, --remote <address> ip address of a remote machine to backup to + --sudo use sudo on the remote machine + -s, --subvolid <subvlid> subvolume id of the mounted BTRFS subvolume to back up to + -u, --UUID <UUID> UUID of the mounted BTRFS subvolume to back up to + +See 'man snap-sync' for more details. +EOF +} + +ssh="" +sudo=0 +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -d | --description) + description="$2" + shift 2 + ;; + -c | --config) + selected_configs="$2" + shift 2 + ;; + -u | --UUID) + uuid_cmdline="$2" + shift 2 + ;; + -s | --subvolid) + subvolid_cmdline="$2" + shift 2 + ;; + -k | --keepold) + keep="yes" + shift + ;; + -n | --noconfirm) + noconfirm="yes" + shift + ;; + -h | --help) + usage + exit 1 + ;; + -q | --quiet) + donotify=1 + shift + ;; + -r | --remote) + remote=$2 + shift 2 + ;; + -p | --port) + port=$2 + shift 2 + ;; + --sudo) + sudo=1 + shift + ;; + *) + die "Unknown option: '$key'. Run '$name -h' for valid options." + ;; + esac +done + +notify() { + for u in $(users | tr ' ' '\n' | sort -u); do + sudo -u "$u" DISPLAY=:0 \ + DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(sudo -u "$u" id -u)/bus" \ + notify-send -a $name "$1" "$2" --icon="dialog-$3" + done +} + +notify_info() { + if [[ $donotify -eq 0 ]]; then + notify "$1" "$2" "information" + else + printf '%s\n' "$1: $2" + fi +} + +notify_error() { + if [[ $donotify -eq 0 ]]; then + notify "$1" "$2" "error" + else + printf '%s\n' "$1: $2" + fi +} + +[[ $EUID -ne 0 ]] && die "Script must be run as root. See '$name -h' for a description of options" +! [[ -f $SNAPPER_CONFIG ]] && die "$SNAPPER_CONFIG does not exist." + +description=${description:-"latest incremental backup"} +uuid_cmdline=${uuid_cmdline:-"none"} +subvolid_cmdline=${subvolid_cmdline:-"5"} +noconfirm=${noconfirm:-"no"} + +if [[ -z $remote ]]; then + if ! command -v rsync &>/dev/null; then + die "--remote specified but rsync command not found" + fi +fi + +if [[ $uuid_cmdline != "none" ]]; then + if [[ -z $remote ]]; then + notify_info "Backup started" "Starting backups to $uuid_cmdline subvolid=$subvolid_cmdline..." + else + notify_info "Backup started" "Starting backups to $uuid_cmdline subvolid $subvolid_cmdline at $remote..." + fi +else + if [[ -z $remote ]]; then + notify_info "Backup started" "Starting backups. Use command line menu to select disk." + else + notify_info "Backup started" "Starting backups. Use command line menu to select disk on $remote." + fi +fi + +if [[ -n $remote ]]; then + ssh="ssh $remote" + if [[ -n $port ]]; then + ssh="$ssh -p $port" + fi + if [[ $sudo -eq 1 ]]; then + ssh="$ssh sudo" + fi +fi + +if [[ "$($ssh findmnt -n -v --target / -o FSTYPE)" == "btrfs" ]]; then + EXCLUDE_UUID=$($ssh findmnt -n -v -t btrfs --target / -o UUID) + TARGETS=$($ssh findmnt -n -v -t btrfs -o UUID,TARGET --list | grep -v "$EXCLUDE_UUID" | awk '{print $2}') + UUIDS=$($ssh findmnt -n -v -t btrfs -o UUID,TARGET --list | grep -v "$EXCLUDE_UUID" | awk '{print $1}') +else + TARGETS=$($ssh findmnt -n -v -t btrfs -o TARGET --list) + UUIDS=$($ssh findmnt -n -v -t btrfs -o UUID --list) +fi + +declare -a TARGETS_ARRAY +declare -a UUIDS_ARRAY +declare -a SUBVOLIDS_ARRAY + +i=0 +for x in $TARGETS; do + SUBVOLIDS_ARRAY[i]=$($ssh btrfs subvolume show "$x" | awk '/Subvolume ID:/ { print $3 }') + TARGETS_ARRAY[i]=$x + i=$((i + 1)) +done + +i=0 +disk=-1 +disk_count=0 +for x in $UUIDS; do + UUIDS_ARRAY[i]=$x + if [[ $x == "$uuid_cmdline" && ${SUBVOLIDS_ARRAY[$((i))]} == "$subvolid_cmdline" ]]; then + disk=$i + disk_count=$((disk_count + 1)) + fi + i=$((i + 1)) +done + +if [[ ${#UUIDS_ARRAY[$@]} -eq 0 ]]; then + die "No external btrfs subvolumes found to backup to. Run '$name -h' for more options." +fi + +if [[ $disk_count -gt 1 ]]; then + printf "Multiple mount points were found with UUID %s and subvolid %s.\n" "$uuid_cmdline" "$subvolid_cmdline" + disk="-1" +fi + +if [[ $disk == -1 ]]; then + if [[ $disk_count == 0 && $uuid_cmdline != "none" ]]; then + error "A device with UUID $uuid_cmdline and subvolid $subvolid_cmdline was not found to be mounted, or it is not a BTRFS device." + fi + if [[ -z $ssh ]]; then + printf "Select a mounted BTRFS device on your local machine to backup to.\nFor more options, exit and run '%s -h'.\n" "$name" + else + printf "Select a mounted BTRFS device on %s to backup to.\nFor more options, exit and run '%s -h'.\n" "$remote" "$name" + fi + while [[ $disk -lt 0 || $disk -gt $i ]]; do + for x in "${!TARGETS_ARRAY[@]}"; do + printf "%4s) %s (uuid=%s, subvolid=%s)\n" "$((x + 1))" "${TARGETS_ARRAY[$x]}" "${UUIDS_ARRAY[$x]}" "${SUBVOLIDS_ARRAY[$x]}" + done + printf "%4s) Exit\n" "0" + read -e -r -p "Enter a number: " disk + if ! [[ $disk == ?(-)+([0-9]) ]] || [[ $disk -lt 0 || $disk -gt $i ]]; then + printf "\nNo disk selected. Select a disk to continue.\n" + disk=-1 + fi + done + if [[ $disk == 0 ]]; then + exit 0 + fi + disk=$((disk - 1)) +fi + +selected_subvolid="${SUBVOLIDS_ARRAY[$((disk))]}" +selected_uuid="${UUIDS_ARRAY[$((disk))]}" +selected_mnt="${TARGETS_ARRAY[$((disk))]}" +printf "\nYou selected the disk with uuid=%s, subvolid=%s.\n" "$selected_uuid" "$selected_subvolid" +if [[ -z $ssh ]]; then + printf "The disk is mounted at '%s'.\n" "$selected_mnt" +else + printf "The disk is mounted at '%s:%s'.\n" "$remote" "$selected_mnt" +fi + +# shellcheck source=/dev/null +source "$SNAPPER_CONFIG" + +if [[ -z $selected_configs ]]; then + printf "\nInteractively cycling through all snapper configurations...\n" +fi +selected_configs=${selected_configs:-$SNAPPER_CONFIGS} + +declare -a BACKUPDIRS_ARRAY +declare -a MYBACKUPDIR_ARRAY +declare -a OLD_NUM_ARRAY +declare -a OLD_SNAP_ARRAY +declare -a NEW_NUM_ARRAY +declare -a NEW_SNAP_ARRAY +declare -a NEW_INFO_ARRAY +declare -a BACKUPLOC_ARRAY +declare -a CONT_BACKUP_ARRAY + +# Initial configuration of where backup directories are +i=0 +for x in $selected_configs; do + + if [[ "$(snapper -c "$x" list --disable-used-space -t single | awk '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {cnt++} END {print cnt}')" -gt 1 ]]; then + error "More than one snapper entry found with UUID $selected_uuid subvolid $selected_subvolid for configuration $x. Skipping configuration $x." + continue + fi + + if [[ "$(snapper -c "$x" list --disable-used-space -t single | awk '/'$name' backup in progress/ {cnt++} END {print cnt}')" -gt 0 ]]; then + printf "\nNOTE: Previous failed %s backup snapshots found for '%s'.\n" "$name" "$x" + if [[ $noconfirm == "yes" ]]; then + printf "'noconfirm' option passed. Failed backups will not be deleted.\n" + else + read -e -r -p "Delete failed backup snapshot(s)? (These local snapshots from failed backups are not used.) [y/N]? " delete_failed + while [[ -n $delete_failed && $delete_failed != [Yy]"es" && + $delete_failed != [Yy] && $delete_failed != [Nn]"o" && + $delete_failed != [Nn] ]]; do + read -e -r -p "Delete failed backup snapshot(s)? (These local snapshots from failed backups are not used.) [y/N] " delete_failed + if [[ -n $delete_failed && $delete_failed != [Yy]"es" && + $delete_failed != [Yy] && $delete_failed != [Nn]"o" && + $delete_failed != [Nn] ]]; then + printf "Select 'y' or 'N'.\n" + fi + done + if [[ $delete_failed == [Yy]"es" || $delete_failed == [Yy] ]]; then + # explicit split list of snapshots (on whitespace) into multiple arguments + # shellcheck disable=SC2046 + snapper -c "$x" delete $(snapper -c "$x" list --disable-used-space | awk '/'$name' backup in progress/ {print $1}') + fi + fi + fi + + SNAP_SYNC_EXCLUDE=no + + if [[ -f "/etc/snapper/configs/$x" ]]; then + # shellcheck source=/dev/null + source "/etc/snapper/configs/$x" + else + die "Selected snapper configuration $x does not exist." + fi + + if [[ $SNAP_SYNC_EXCLUDE == "yes" ]]; then + continue + fi + + printf "\n" + + old_num=$(snapper -c "$x" list --disable-used-space -t single | awk '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {print $1}') + old_snap=$SUBVOLUME/.snapshots/$old_num/snapshot + + OLD_NUM_ARRAY[i]=$old_num + OLD_SNAP_ARRAY[i]=$old_snap + + if [[ -z $old_num ]]; then + printf "No backups have been performed for '%s' on this disk.\n" "$x" + read -e -r -p "Enter name of subvolume to store backups, relative to $selected_mnt (to be created if not existing): " mybackupdir + printf "This will be the initial backup for snapper configuration '%s' to this disk. This could take awhile.\n" "$x" + BACKUPDIR="$selected_mnt/$mybackupdir" + $ssh test -d "$BACKUPDIR" || $ssh btrfs subvolume create "$BACKUPDIR" + else + mybackupdir=$(snapper -c "$x" list --disable-used-space -t single | awk -F"|" '/'"subvolid=$selected_subvolid, uuid=$selected_uuid"'/ {print $5}' | awk -F "," '/backupdir/ {print $1}' | awk -F"=" '{print $2}') + BACKUPDIR="$selected_mnt/$mybackupdir" + $ssh test -d "$BACKUPDIR" || die "%s is not a directory on %s.\n" "$BACKUPDIR" "$selected_uuid" + fi + BACKUPDIRS_ARRAY[i]="$BACKUPDIR" + MYBACKUPDIR_ARRAY[i]="$mybackupdir" + + printf "Creating new local snapshot for '%s' configuration...\n" "$x" + new_num=$(snapper -c "$x" create --print-number -d "$name backup in progress") + new_snap=$SUBVOLUME/.snapshots/$new_num/snapshot + new_info=$SUBVOLUME/.snapshots/$new_num/info.xml + sync + backup_location=$BACKUPDIR/$x/$new_num/ + if [[ -z $ssh ]]; then + printf "Will backup %s to %s\n" "$new_snap" "$backup_location/snapshot" + else + printf "Will backup %s to %s\n" "$new_snap" "$remote":"$backup_location/snapshot" + fi + + if ($ssh test -d "$backup_location/snapshot"); then + printf "WARNING: Backup directory '%s' already exists. This configuration will be skipped!\n" "$backup_location/snapshot" + printf "Move or delete destination directory and try backup again.\n" + fi + + NEW_NUM_ARRAY[i]="$new_num" + NEW_SNAP_ARRAY[i]="$new_snap" + NEW_INFO_ARRAY[i]="$new_info" + BACKUPLOC_ARRAY[i]="$backup_location" + + cont_backup="K" + CONT_BACKUP_ARRAY[i]="yes" + if [[ $noconfirm == "yes" ]]; then + cont_backup="yes" + else + while [[ -n $cont_backup && $cont_backup != [Yy]"es" && + $cont_backup != [Yy] && $cont_backup != [Nn]"o" && + $cont_backup != [Nn] ]]; do + read -e -r -p "Proceed with backup of '$x' configuration [Y/n]? " cont_backup + if [[ -n $cont_backup && $cont_backup != [Yy]"es" && + $cont_backup != [Yy] && $cont_backup != [Nn]"o" && + $cont_backup != [Nn] ]]; then + printf "Select 'Y' or 'n'.\n" + fi + done + fi + + if [[ $cont_backup != [Yy]"es" && $cont_backup != [Yy] && -n $cont_backup ]]; then + CONT_BACKUP_ARRAY[i]="no" + printf "Not backing up '%s' configuration.\n" "$x" + snapper -c "$x" delete "$new_num" + fi + + i=$((i + 1)) + +done + +# Actual backing up +printf "\nPerforming backups...\n" +i=-1 +for x in $selected_configs; do + + i=$((i + 1)) + + SNAP_SYNC_EXCLUDE=no + + if [[ -f "/etc/snapper/configs/$x" ]]; then + # shellcheck source=/dev/null + source "/etc/snapper/configs/$x" + else + die "Selected snapper configuration $x does not exist." + fi + + cont_backup=${CONT_BACKUP_ARRAY[$i]} + if [[ $cont_backup == "no" || $SNAP_SYNC_EXCLUDE == "yes" ]]; then + notify_info "Backup in progress" "NOTE: Skipping $x configuration." + continue + fi + + notify_info "Backup in progress" "Backing up $x configuration." + + printf "\n" + + old_num="${OLD_NUM_ARRAY[$i]}" + old_snap="${OLD_SNAP_ARRAY[$i]}" + BACKUPDIR="${BACKUPDIRS_ARRAY[$i]}" + mybackupdir="${MYBACKUPDIR_ARRAY[$i]}" + new_num="${NEW_NUM_ARRAY[$i]}" + new_snap="${NEW_SNAP_ARRAY[$i]}" + new_info="${NEW_INFO_ARRAY[$i]}" + backup_location="${BACKUPLOC_ARRAY[$i]}" + + if ($ssh test -d "$backup_location/snapshot"); then + printf "ERROR: Backup directory '%s' already exists. Skipping backup of this configuration!\n" "$backup_location/snapshot" + continue + fi + + $ssh mkdir -p "$backup_location" + + if [[ -z $old_num ]]; then + printf "Sending first snapshot for '%s' configuration...\n" "$x" + if [[ $doprogress -eq 0 ]]; then + btrfs send "$new_snap" | pv | $ssh btrfs receive "$backup_location" &>/dev/null + else + btrfs send "$new_snap" | $ssh btrfs receive "$backup_location" &>/dev/null + fi + else + + printf "Sending incremental snapshot for '%s' configuration...\n" "$x" + # Sends the difference between the new snapshot and old snapshot to the + # backup location. Using the -c flag instead of -p tells it that there + # is an identical subvolume to the old snapshot at the receiving + # location where it can get its data. This helps speed up the transfer. + + if [[ $doprogress -eq 0 ]]; then + btrfs send -c "$old_snap" "$new_snap" | pv | $ssh btrfs receive "$backup_location" + else + btrfs send -c "$old_snap" "$new_snap" | $ssh btrfs receive "$backup_location" + fi + + if [[ $keep == "yes" ]]; then + printf "Modifying data for old local snapshot for '%s' configuration...\n" "$x" + snapper -v -c "$x" modify -d "old snap-sync snapshot (you may remove)" -u "backupdir=,subvolid=,uuid=" -c "number" "$old_num" + else + printf "Deleting old snapshot for %s...\n" "$x" + snapper -c "$x" delete "$old_num" + fi + + fi + + if [[ -z $remote ]]; then + cp "$new_info" "$backup_location" + else + if [[ -z $port ]]; then + rsync -avzq "$new_info" "$remote":"$backup_location" + else + rsync -avzqe "ssh -p $port" "$new_info" "$remote":"$backup_location" + fi + fi + + # It's important not to change this userdata in the snapshots, since that's how + # we find the previous one. + + userdata="backupdir=$mybackupdir, subvolid=$selected_subvolid, uuid=$selected_uuid" + + # Tag new snapshot as the latest + printf "Tagging local snapshot as latest backup for '%s' configuration...\n" "$x" + snapper -v -c "$x" modify -d "$description" -u "$userdata" "$new_num" + + printf "Backup complete for '%s' configuration.\n" "$x" + +done + +printf "\nDone!\n" + +if [[ $uuid_cmdline != "none" ]]; then + notify_info "Finished" "Backups to $uuid_cmdline complete!" +else + notify_info "Finished" "Backups complete!" +fi diff --git a/pkgs/sources/tree-sitter-yts/.editorconfig b/pkgs/sources/tree-sitter-yts/.editorconfig new file mode 100644 index 00000000..919c78fa --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = false + +# for testing purposes, the corpus may have trailing whitespace +# and may have mixed EOL. +# Still want a final newline though, as that makes no semantic difference. +[corpus/*] +trim_trailing_whitespace = false +end_of_line = unset + +[**.{js,json,cc,css}] +indent_style = space +indent_size = 2 + +# tree-sitter generate emits json with no trailing newline +[src/node-types.json] +insert_final_newline = false diff --git a/pkgs/sources/tree-sitter-yts/.envrc b/pkgs/sources/tree-sitter-yts/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/.envrc @@ -0,0 +1 @@ +use flake diff --git a/pkgs/sources/tree-sitter-yts/.gitignore b/pkgs/sources/tree-sitter-yts/.gitignore new file mode 100644 index 00000000..c4e2e389 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/.gitignore @@ -0,0 +1,3 @@ +/.direnv +/result +/node_modules diff --git a/pkgs/sources/tree-sitter-yts/Cargo.toml b/pkgs/sources/tree-sitter-yts/Cargo.toml new file mode 100644 index 00000000..5287c420 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tree-sitter-yts" +description = "yts grammar for the tree-sitter parsing library" +version = "0.0.1" +keywords = ["incremental", "parsing", "yts"] +categories = ["parsing", "text-editors"] +repository = "https://github.com/tree-sitter/tree-sitter-yts" +edition = "2018" +license = "MIT" + +build = "bindings/rust/build.rs" +include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] + +[lib] +path = "bindings/rust/lib.rs" + +[dependencies] +tree-sitter = "~0.20.10" + +[build-dependencies] +cc = "1.0" diff --git a/pkgs/sources/tree-sitter-yts/binding.gyp b/pkgs/sources/tree-sitter-yts/binding.gyp new file mode 100644 index 00000000..b05038b4 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/binding.gyp @@ -0,0 +1,19 @@ +{ + "targets": [ + { + "target_name": "tree_sitter_yts_binding", + "include_dirs": [ + "<!(node -e \"require('nan')\")", + "src" + ], + "sources": [ + "bindings/node/binding.cc", + "src/parser.c", + # If your language uses an external scanner, add it here. + ], + "cflags_c": [ + "-std=c99", + ] + } + ] +} diff --git a/pkgs/sources/tree-sitter-yts/bindings/node/binding.cc b/pkgs/sources/tree-sitter-yts/bindings/node/binding.cc new file mode 100644 index 00000000..a042be54 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/bindings/node/binding.cc @@ -0,0 +1,33 @@ +#include "nan.h" +#include "tree_sitter/parser.h" +#include <node.h> + +using namespace v8; + +extern "C" TSLanguage *tree_sitter_yts (); + +namespace +{ + +NAN_METHOD (New) {} + +void +Init (Local<Object> exports, Local<Object> module) +{ + Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate> (New); + tpl->SetClassName (Nan::New ("Language").ToLocalChecked ()); + tpl->InstanceTemplate ()->SetInternalFieldCount (1); + + Local<Function> constructor = Nan::GetFunction (tpl).ToLocalChecked (); + Local<Object> instance + = constructor->NewInstance (Nan::GetCurrentContext ()).ToLocalChecked (); + Nan::SetInternalFieldPointer (instance, 0, tree_sitter_yts ()); + + Nan::Set (instance, Nan::New ("name").ToLocalChecked (), + Nan::New ("yts").ToLocalChecked ()); + Nan::Set (module, Nan::New ("exports").ToLocalChecked (), instance); +} + +NODE_MODULE (tree_sitter_yts_binding, Init) + +} // namespace diff --git a/pkgs/sources/tree-sitter-yts/bindings/node/index.js b/pkgs/sources/tree-sitter-yts/bindings/node/index.js new file mode 100644 index 00000000..32179742 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/bindings/node/index.js @@ -0,0 +1,19 @@ +try { + module.exports = require("../../build/Release/tree_sitter_yts_binding"); +} catch (error1) { + if (error1.code !== "MODULE_NOT_FOUND") { + throw error1; + } + try { + module.exports = require("../../build/Debug/tree_sitter_yts_binding"); + } catch (error2) { + if (error2.code !== "MODULE_NOT_FOUND") { + throw error2; + } + throw error1; + } +} + +try { + module.exports.nodeTypeInfo = require("../../src/node-types.json"); +} catch (_) {} diff --git a/pkgs/sources/tree-sitter-yts/bindings/rust/build.rs b/pkgs/sources/tree-sitter-yts/bindings/rust/build.rs new file mode 100644 index 00000000..c6061f09 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/bindings/rust/build.rs @@ -0,0 +1,40 @@ +fn main() { + let src_dir = std::path::Path::new("src"); + + let mut c_config = cc::Build::new(); + c_config.include(&src_dir); + c_config + .flag_if_supported("-Wno-unused-parameter") + .flag_if_supported("-Wno-unused-but-set-variable") + .flag_if_supported("-Wno-trigraphs"); + let parser_path = src_dir.join("parser.c"); + c_config.file(&parser_path); + + // If your language uses an external scanner written in C, + // then include this block of code: + + /* + let scanner_path = src_dir.join("scanner.c"); + c_config.file(&scanner_path); + println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); + */ + + c_config.compile("parser"); + println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); + + // If your language uses an external scanner written in C++, + // then include this block of code: + + /* + let mut cpp_config = cc::Build::new(); + cpp_config.cpp(true); + cpp_config.include(&src_dir); + cpp_config + .flag_if_supported("-Wno-unused-parameter") + .flag_if_supported("-Wno-unused-but-set-variable"); + let scanner_path = src_dir.join("scanner.cc"); + cpp_config.file(&scanner_path); + cpp_config.compile("scanner"); + println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); + */ +} diff --git a/pkgs/sources/tree-sitter-yts/bindings/rust/lib.rs b/pkgs/sources/tree-sitter-yts/bindings/rust/lib.rs new file mode 100644 index 00000000..f1868b2d --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/bindings/rust/lib.rs @@ -0,0 +1,52 @@ +//! This crate provides yts language support for the [tree-sitter][] parsing library. +//! +//! Typically, you will use the [language][language func] function to add this language to a +//! tree-sitter [Parser][], and then use the parser to parse some code: +//! +//! ``` +//! let code = ""; +//! let mut parser = tree_sitter::Parser::new(); +//! parser.set_language(tree_sitter_yts::language()).expect("Error loading yts grammar"); +//! let tree = parser.parse(code, None).unwrap(); +//! ``` +//! +//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html +//! [language func]: fn.language.html +//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html +//! [tree-sitter]: https://tree-sitter.github.io/ + +use tree_sitter::Language; + +extern "C" { + fn tree_sitter_yts() -> Language; +} + +/// Get the tree-sitter [Language][] for this grammar. +/// +/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html +pub fn language() -> Language { + unsafe { tree_sitter_yts() } +} + +/// The content of the [`node-types.json`][] file for this grammar. +/// +/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types +pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json"); + +// Uncomment these to include any queries that this grammar contains + +// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm"); +// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm"); +// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm"); +// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm"); + +#[cfg(test)] +mod tests { + #[test] + fn test_can_load_grammar() { + let mut parser = tree_sitter::Parser::new(); + parser + .set_language(super::language()) + .expect("Error loading yts language"); + } +} diff --git a/pkgs/sources/tree-sitter-yts/corpus/comments.txt b/pkgs/sources/tree-sitter-yts/corpus/comments.txt new file mode 100644 index 00000000..0070baf8 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/corpus/comments.txt @@ -0,0 +1,51 @@ +================================================================================ +Parse multiple lines +================================================================================ + +pick 6221 "Name" "2024-01-17" "A" "[0m 0s]" "url" +pick 6181 "Name2" "2024-01-16" "A2" "[0m 0s]" "url" + +# This is a comment +# it contains information + +-------------------------------------------------------------------------------- + +(source_file + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (comment) + (comment)) diff --git a/pkgs/sources/tree-sitter-yts/corpus/comments_correct.txt b/pkgs/sources/tree-sitter-yts/corpus/comments_correct.txt new file mode 100644 index 00000000..40cdab7d --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/corpus/comments_correct.txt @@ -0,0 +1,27 @@ +================================================================================ +Disregard comments in title +================================================================================ + +pick 6094 "#100 Name" "2024-01-12" "A" "[133m 29s]" "url" + +-------------------------------------------------------------------------------- + +(source_file + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote)))) diff --git a/pkgs/sources/tree-sitter-yts/corpus/duration.txt b/pkgs/sources/tree-sitter-yts/corpus/duration.txt new file mode 100644 index 00000000..59476b98 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/corpus/duration.txt @@ -0,0 +1,84 @@ +================================================================================ +Parse multiple lines with different durations +================================================================================ + +pick 6221 "Name" "2024-01-17" "A" "[1h 0m]" "url" +pick 6181 "Name2" "2024-01-16" "A2" "[20m 02s]" "url2" +pick 6184 "Name3" "2024-01-16" "A3" "[20h 0m]" "url3" +pick 6206 "Name4" "2024-01-16" "A4" "[No Duration]" "url4" + +-------------------------------------------------------------------------------- + +(source_file + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote)))) diff --git a/pkgs/sources/tree-sitter-yts/corpus/url.txt b/pkgs/sources/tree-sitter-yts/corpus/url.txt new file mode 100644 index 00000000..1ae3d106 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/corpus/url.txt @@ -0,0 +1,84 @@ +================================================================================ +Parse multiple lines with url +================================================================================ + +pick 6221 "Name" "2024-01-17" "A" "[0h 0m]" "url" +pick 6181 "Name2" "2024-01-16" "A2" "[0h 0m]" "url2" +pick 6184 "Name3" "2024-01-16" "A3" "[0h 0m]" "url3" +pick 6206 "Name4" "2024-01-16" "A4" "[0h 0m]" "url4" + +-------------------------------------------------------------------------------- + +(source_file + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote))) + (line + (command) + (id) + (title + (quote) + (quote)) + (date + (quote) + (quote)) + (author + (quote) + (quote)) + (duration + (quote) + (quote)) + (url + (quote) + (quote)))) diff --git a/pkgs/sources/tree-sitter-yts/default.nix b/pkgs/sources/tree-sitter-yts/default.nix new file mode 100644 index 00000000..7e15481c --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/default.nix @@ -0,0 +1,11 @@ +[ + ( + final: prev: { + yts-grammar = (prev.callPackage ./package.nix {}) { + language = "yts"; + version = "1.0"; + src = ./.; + }; + } + ) +] diff --git a/pkgs/sources/tree-sitter-yts/flake.lock b/pkgs/sources/tree-sitter-yts/flake.lock new file mode 100644 index 00000000..bff9f1fa --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/flake.lock @@ -0,0 +1,97 @@ +{ + "nodes": { + "crane": { + "inputs": { + "nixpkgs": ["nixpkgs"] + }, + "locked": { + "lastModified": 1704819371, + "narHash": "sha256-oFUfPWrWGQTZaCM3byxwYwrMLwshDxVGOrMH5cVP/X8=", + "owner": "ipetkov", + "repo": "crane", + "rev": "5c234301a1277e4cc759c23a2a7a00a06ddd7111", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1705133751, + "narHash": "sha256-rCIsyE80jgiOU78gCWN3A0wE0tR2GI5nH6MlS+HaaSQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9b19f5e77dd906cb52dade0b7bd280339d2a1f3d", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "npmlock2nix": { + "flake": false, + "locked": { + "lastModified": 1673447413, + "narHash": "sha256-sJM82Sj8yfQYs9axEmGZ9Evzdv/kDcI9sddqJ45frrU=", + "owner": "nix-community", + "repo": "npmlock2nix", + "rev": "9197bbf397d76059a76310523d45df10d2e4ca81", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "npmlock2nix", + "type": "github" + } + }, + "root": { + "inputs": { + "crane": "crane", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "npmlock2nix": "npmlock2nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/tree-sitter-yts/flake.nix b/pkgs/sources/tree-sitter-yts/flake.nix new file mode 100644 index 00000000..1b6f8ab0 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/flake.nix @@ -0,0 +1,82 @@ +{ + description = "tree-sitter-yts"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + flake-utils.url = "github:numtide/flake-utils"; + + npmlock2nix = { + url = "github:nix-community/npmlock2nix"; + flake = false; + }; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + npmlock2nix, + crane, + }: (flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + inherit (pkgs) lib; + + npmlock2nix' = pkgs.callPackage npmlock2nix {}; + craneLib = crane.lib.${system}; + in { + build = self.packages.${system}.tree-sitter-nix; + + rust-bindings = craneLib.buildPackage { + src = self; + }; + + # Requires xcode + node-bindings = npmlock2nix'.v2.build { + src = self; + inherit (self.devShells.${system}.default) nativeBuildInputs; + inherit (pkgs) nodejs; + + buildCommands = [ + "${pkgs.nodePackages.node-gyp}/bin/node-gyp configure" + "npm run build" + ]; + + installPhase = '' + touch $out + ''; + }; + + packages.tree-sitter-yts = (pkgs.callPackage ./grammar.nix {}) { + language = "yts"; + version = "1.0"; + src = self; + }; + + packages.default = self.packages.${system}.tree-sitter-yts; + devShells.default = pkgs.mkShell { + packages = [ + pkgs.nodejs + pkgs.python3 + + pkgs.tree-sitter + pkgs.editorconfig-checker + + pkgs.rustc + pkgs.cargo + + # Formatters + pkgs.treefmt + pkgs.nixpkgs-fmt + pkgs.nodePackages.prettier + pkgs.rustfmt + pkgs.clang-tools + ]; + }; + })); +} diff --git a/pkgs/sources/tree-sitter-yts/grammar.js b/pkgs/sources/tree-sitter-yts/grammar.js new file mode 100644 index 00000000..655f6dea --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/grammar.js @@ -0,0 +1,26 @@ +module.exports = grammar({ + name: "yts", + + rules: { + source_file: ($) => repeat(choice($.line, $.comment)), + line: ($) => + seq($.command, $.id, $.title, $.date, $.author, $.duration, $.url, "\n"), + + command: ($) => choice("pick", "p", "watch", "w", "drop", "d", "url", "u"), + id: ($) => /[0-9]+/, + title: ($) => seq($._q, /[^"]+/, $._q), + date: ($) => seq($._q, /\d{4}-\d{2}-\d{2}/, $._q), + author: ($) => seq($._q, /[^"]+/, $._q), + duration: ($) => + seq( + $._q, + seq("[", choice("No Duration", /\d+m \d+s/, /\d+h \d+m/), "]"), + $._q, + ), + url: ($) => seq($._q, /[^"]+/, $._q), + comment: ($) => /#.*/, + _q: ($) => $.quote, + quote: ($) => /"/, + }, + extras: ($) => [/\s/, /\\\r?\n/], +}); diff --git a/pkgs/sources/tree-sitter-yts/highlight.yts b/pkgs/sources/tree-sitter-yts/highlight.yts new file mode 100644 index 00000000..319ee95c --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/highlight.yts @@ -0,0 +1,4 @@ +pick 6221 "Name" "2024-01-17" "A" "0:00" "url" +pick 6181 "Name2" "2024-01-16" "A2" "0:00" "url2" +pick 6184 "Name3" "2024-01-16" "A3" "0:00" "url3" +pick 6206 "Name4" "2024-01-16" "A4" "299:36" "url4" diff --git a/pkgs/sources/tree-sitter-yts/package.json b/pkgs/sources/tree-sitter-yts/package.json new file mode 100644 index 00000000..2511ccb7 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/package.json @@ -0,0 +1,31 @@ +{ + "name": "tree-sitter-yts", + "version": "0.0.1", + "description": "yts grammar for tree-sitter", + "main": "bindings/node", + "keywords": [ + "parsing", + "incremental" + ], + "dependencies": { + "nan": "^2.12.1" + }, + "devDependencies": { + "tree-sitter-cli": "^0.20.8" + }, + "scripts": { + "test": "tree-sitter test" + }, + "tree-sitter": [ + { + "scope": "source.yts", + "file-types": [ + "yts" + ], + "highlights": [ + "queries/highlights.scm" + ], + "injection-regex": "^(yts)$" + } + ] +} diff --git a/pkgs/sources/tree-sitter-yts/package.nix b/pkgs/sources/tree-sitter-yts/package.nix new file mode 100644 index 00000000..fe9a7326 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/package.nix @@ -0,0 +1,63 @@ +# taken from nixpgks: pkgs/development/tools/parsing/tree-sitter/grammar.nix +{ + stdenv, + nodejs, + tree-sitter, + lib, +}: +# Build a parser grammar and put the resulting shared object in `$out/parser` +{ + # language name + language, + version, + src, + location ? null, + generate ? false, + ... +} @ args: +stdenv.mkDerivation ({ + pname = "${language}-grammar"; + + inherit src version; + + nativeBuildInputs = lib.optionals generate [nodejs tree-sitter]; + + CFLAGS = ["-Isrc" "-O2"]; + CXXFLAGS = ["-Isrc" "-O2"]; + + stripDebugList = ["parser"]; + + configurePhase = + lib.optionalString (location != null) '' + cd ${location} + '' + + lib.optionalString generate '' + tree-sitter generate + ''; + + # When both scanner.{c,cc} exist, we should not link both since they may be the same but in + # different languages. Just randomly prefer C++ if that happens. + buildPhase = '' + runHook preBuild + if [[ -e src/scanner.cc ]]; then + $CXX -fPIC -c src/scanner.cc -o scanner.o $CXXFLAGS + elif [[ -e src/scanner.c ]]; then + $CC -fPIC -c src/scanner.c -o scanner.o $CFLAGS + fi + $CC -fPIC -c src/parser.c -o parser.o $CFLAGS + rm -rf parser + $CXX -shared -o parser *.o + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + mkdir $out + mv parser $out/ + if [[ -d queries ]]; then + cp -r queries $out + fi + runHook postInstall + ''; + } + // removeAttrs args ["language" "location" "generate"]) diff --git a/pkgs/sources/tree-sitter-yts/queries/highlights.scm b/pkgs/sources/tree-sitter-yts/queries/highlights.scm new file mode 100644 index 00000000..674cbf18 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/queries/highlights.scm @@ -0,0 +1,11 @@ +(command) @keyword +(id) @constant +(title) @text.title +(date) @number +(author) @operator +(duration) @property +((url) @conceal (#set! conceal "")) + + +((quote) @conceal (#set! conceal "")) +(comment) @comment @spell diff --git a/pkgs/sources/tree-sitter-yts/src/grammar.json b/pkgs/sources/tree-sitter-yts/src/grammar.json new file mode 100644 index 00000000..a35a5464 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/src/grammar.json @@ -0,0 +1,238 @@ +{ + "name": "yts", + "rules": { + "source_file": { + "type": "REPEAT", + "content": { + "type": "CHOICE", + "members": [ + { + "type": "SYMBOL", + "name": "line" + }, + { + "type": "SYMBOL", + "name": "comment" + } + ] + } + }, + "line": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "command" + }, + { + "type": "SYMBOL", + "name": "id" + }, + { + "type": "SYMBOL", + "name": "title" + }, + { + "type": "SYMBOL", + "name": "date" + }, + { + "type": "SYMBOL", + "name": "author" + }, + { + "type": "SYMBOL", + "name": "duration" + }, + { + "type": "SYMBOL", + "name": "url" + }, + { + "type": "STRING", + "value": "\n" + } + ] + }, + "command": { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": "pick" + }, + { + "type": "STRING", + "value": "p" + }, + { + "type": "STRING", + "value": "watch" + }, + { + "type": "STRING", + "value": "w" + }, + { + "type": "STRING", + "value": "drop" + }, + { + "type": "STRING", + "value": "d" + }, + { + "type": "STRING", + "value": "url" + }, + { + "type": "STRING", + "value": "u" + } + ] + }, + "id": { + "type": "PATTERN", + "value": "[0-9]+" + }, + "title": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_q" + }, + { + "type": "PATTERN", + "value": "[^\"]+" + }, + { + "type": "SYMBOL", + "name": "_q" + } + ] + }, + "date": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_q" + }, + { + "type": "PATTERN", + "value": "\\d{4}-\\d{2}-\\d{2}" + }, + { + "type": "SYMBOL", + "name": "_q" + } + ] + }, + "author": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_q" + }, + { + "type": "PATTERN", + "value": "[^\"]+" + }, + { + "type": "SYMBOL", + "name": "_q" + } + ] + }, + "duration": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_q" + }, + { + "type": "SEQ", + "members": [ + { + "type": "STRING", + "value": "[" + }, + { + "type": "CHOICE", + "members": [ + { + "type": "STRING", + "value": "No Duration" + }, + { + "type": "PATTERN", + "value": "\\d+m \\d+s" + }, + { + "type": "PATTERN", + "value": "\\d+h \\d+m" + } + ] + }, + { + "type": "STRING", + "value": "]" + } + ] + }, + { + "type": "SYMBOL", + "name": "_q" + } + ] + }, + "url": { + "type": "SEQ", + "members": [ + { + "type": "SYMBOL", + "name": "_q" + }, + { + "type": "PATTERN", + "value": "[^\"]+" + }, + { + "type": "SYMBOL", + "name": "_q" + } + ] + }, + "comment": { + "type": "PATTERN", + "value": "#.*" + }, + "_q": { + "type": "SYMBOL", + "name": "quote" + }, + "quote": { + "type": "PATTERN", + "value": "\"" + } + }, + "extras": [ + { + "type": "PATTERN", + "value": "\\s" + }, + { + "type": "PATTERN", + "value": "\\\\\\r?\\n" + } + ], + "conflicts": [], + "precedences": [], + "externals": [], + "inline": [], + "supertypes": [] +} + diff --git a/pkgs/sources/tree-sitter-yts/src/node-types.json b/pkgs/sources/tree-sitter-yts/src/node-types.json new file mode 100644 index 00000000..1a63a552 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/src/node-types.json @@ -0,0 +1,200 @@ +[ + { + "type": "author", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "quote", + "named": true + } + ] + } + }, + { + "type": "command", + "named": true, + "fields": {} + }, + { + "type": "date", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "quote", + "named": true + } + ] + } + }, + { + "type": "duration", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "quote", + "named": true + } + ] + } + }, + { + "type": "line", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "author", + "named": true + }, + { + "type": "command", + "named": true + }, + { + "type": "date", + "named": true + }, + { + "type": "duration", + "named": true + }, + { + "type": "id", + "named": true + }, + { + "type": "title", + "named": true + }, + { + "type": "url", + "named": true + } + ] + } + }, + { + "type": "source_file", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": false, + "types": [ + { + "type": "comment", + "named": true + }, + { + "type": "line", + "named": true + } + ] + } + }, + { + "type": "title", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "quote", + "named": true + } + ] + } + }, + { + "type": "url", + "named": true, + "fields": {}, + "children": { + "multiple": true, + "required": true, + "types": [ + { + "type": "quote", + "named": true + } + ] + } + }, + { + "type": "\n", + "named": false + }, + { + "type": "No Duration", + "named": false + }, + { + "type": "[", + "named": false + }, + { + "type": "]", + "named": false + }, + { + "type": "comment", + "named": true + }, + { + "type": "d", + "named": false + }, + { + "type": "drop", + "named": false + }, + { + "type": "id", + "named": true + }, + { + "type": "p", + "named": false + }, + { + "type": "pick", + "named": false + }, + { + "type": "quote", + "named": true + }, + { + "type": "u", + "named": false + }, + { + "type": "url", + "named": false + }, + { + "type": "w", + "named": false + }, + { + "type": "watch", + "named": false + } +] \ No newline at end of file diff --git a/pkgs/sources/tree-sitter-yts/src/parser.c b/pkgs/sources/tree-sitter-yts/src/parser.c new file mode 100644 index 00000000..ded5c342 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/src/parser.c @@ -0,0 +1,1108 @@ +#include <tree_sitter/parser.h> + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#define LANGUAGE_VERSION 14 +#define STATE_COUNT 31 +#define LARGE_STATE_COUNT 2 +#define SYMBOL_COUNT 30 +#define ALIAS_COUNT 0 +#define TOKEN_COUNT 20 +#define EXTERNAL_TOKEN_COUNT 0 +#define FIELD_COUNT 0 +#define MAX_ALIAS_SEQUENCE_LENGTH 8 +#define PRODUCTION_ID_COUNT 1 + +enum +{ + anon_sym_LF = 1, + anon_sym_pick = 2, + anon_sym_p = 3, + anon_sym_watch = 4, + anon_sym_w = 5, + anon_sym_drop = 6, + anon_sym_d = 7, + anon_sym_url = 8, + anon_sym_u = 9, + sym_id = 10, + aux_sym_title_token1 = 11, + aux_sym_date_token1 = 12, + anon_sym_LBRACK = 13, + anon_sym_NoDuration = 14, + aux_sym_duration_token1 = 15, + aux_sym_duration_token2 = 16, + anon_sym_RBRACK = 17, + sym_comment = 18, + sym_quote = 19, + sym_source_file = 20, + sym_line = 21, + sym_command = 22, + sym_title = 23, + sym_date = 24, + sym_author = 25, + sym_duration = 26, + sym_url = 27, + sym__q = 28, + aux_sym_source_file_repeat1 = 29, +}; + +static const char *const ts_symbol_names[] = { + [ts_builtin_sym_end] = "end", + [anon_sym_LF] = "\n", + [anon_sym_pick] = "pick", + [anon_sym_p] = "p", + [anon_sym_watch] = "watch", + [anon_sym_w] = "w", + [anon_sym_drop] = "drop", + [anon_sym_d] = "d", + [anon_sym_url] = "url", + [anon_sym_u] = "u", + [sym_id] = "id", + [aux_sym_title_token1] = "title_token1", + [aux_sym_date_token1] = "date_token1", + [anon_sym_LBRACK] = "[", + [anon_sym_NoDuration] = "No Duration", + [aux_sym_duration_token1] = "duration_token1", + [aux_sym_duration_token2] = "duration_token2", + [anon_sym_RBRACK] = "]", + [sym_comment] = "comment", + [sym_quote] = "quote", + [sym_source_file] = "source_file", + [sym_line] = "line", + [sym_command] = "command", + [sym_title] = "title", + [sym_date] = "date", + [sym_author] = "author", + [sym_duration] = "duration", + [sym_url] = "url", + [sym__q] = "_q", + [aux_sym_source_file_repeat1] = "source_file_repeat1", +}; + +static const TSSymbol ts_symbol_map[] = { + [ts_builtin_sym_end] = ts_builtin_sym_end, + [anon_sym_LF] = anon_sym_LF, + [anon_sym_pick] = anon_sym_pick, + [anon_sym_p] = anon_sym_p, + [anon_sym_watch] = anon_sym_watch, + [anon_sym_w] = anon_sym_w, + [anon_sym_drop] = anon_sym_drop, + [anon_sym_d] = anon_sym_d, + [anon_sym_url] = anon_sym_url, + [anon_sym_u] = anon_sym_u, + [sym_id] = sym_id, + [aux_sym_title_token1] = aux_sym_title_token1, + [aux_sym_date_token1] = aux_sym_date_token1, + [anon_sym_LBRACK] = anon_sym_LBRACK, + [anon_sym_NoDuration] = anon_sym_NoDuration, + [aux_sym_duration_token1] = aux_sym_duration_token1, + [aux_sym_duration_token2] = aux_sym_duration_token2, + [anon_sym_RBRACK] = anon_sym_RBRACK, + [sym_comment] = sym_comment, + [sym_quote] = sym_quote, + [sym_source_file] = sym_source_file, + [sym_line] = sym_line, + [sym_command] = sym_command, + [sym_title] = sym_title, + [sym_date] = sym_date, + [sym_author] = sym_author, + [sym_duration] = sym_duration, + [sym_url] = sym_url, + [sym__q] = sym__q, + [aux_sym_source_file_repeat1] = aux_sym_source_file_repeat1, +}; + +static const TSSymbolMetadata ts_symbol_metadata[] = { + [ts_builtin_sym_end] = { + .visible = false, + .named = true, + }, + [anon_sym_LF] = { + .visible = true, + .named = false, + }, + [anon_sym_pick] = { + .visible = true, + .named = false, + }, + [anon_sym_p] = { + .visible = true, + .named = false, + }, + [anon_sym_watch] = { + .visible = true, + .named = false, + }, + [anon_sym_w] = { + .visible = true, + .named = false, + }, + [anon_sym_drop] = { + .visible = true, + .named = false, + }, + [anon_sym_d] = { + .visible = true, + .named = false, + }, + [anon_sym_url] = { + .visible = true, + .named = false, + }, + [anon_sym_u] = { + .visible = true, + .named = false, + }, + [sym_id] = { + .visible = true, + .named = true, + }, + [aux_sym_title_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_date_token1] = { + .visible = false, + .named = false, + }, + [anon_sym_LBRACK] = { + .visible = true, + .named = false, + }, + [anon_sym_NoDuration] = { + .visible = true, + .named = false, + }, + [aux_sym_duration_token1] = { + .visible = false, + .named = false, + }, + [aux_sym_duration_token2] = { + .visible = false, + .named = false, + }, + [anon_sym_RBRACK] = { + .visible = true, + .named = false, + }, + [sym_comment] = { + .visible = true, + .named = true, + }, + [sym_quote] = { + .visible = true, + .named = true, + }, + [sym_source_file] = { + .visible = true, + .named = true, + }, + [sym_line] = { + .visible = true, + .named = true, + }, + [sym_command] = { + .visible = true, + .named = true, + }, + [sym_title] = { + .visible = true, + .named = true, + }, + [sym_date] = { + .visible = true, + .named = true, + }, + [sym_author] = { + .visible = true, + .named = true, + }, + [sym_duration] = { + .visible = true, + .named = true, + }, + [sym_url] = { + .visible = true, + .named = true, + }, + [sym__q] = { + .visible = false, + .named = true, + }, + [aux_sym_source_file_repeat1] = { + .visible = false, + .named = false, + }, +}; + +static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT] + [MAX_ALIAS_SEQUENCE_LENGTH] + = { + [0] = { 0 }, + }; + +static const uint16_t ts_non_terminal_alias_map[] = { + 0, +}; + +static const TSStateId ts_primary_state_ids[STATE_COUNT] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5, [6] = 6, + [7] = 7, [8] = 8, [9] = 9, [10] = 10, [11] = 11, [12] = 12, [13] = 13, + [14] = 14, [15] = 15, [16] = 16, [17] = 17, [18] = 18, [19] = 19, [20] = 20, + [21] = 21, [22] = 22, [23] = 23, [24] = 24, [25] = 25, [26] = 26, [27] = 27, + [28] = 28, [29] = 29, [30] = 30, +}; + +static bool +ts_lex (TSLexer *lexer, TSStateId state) +{ + START_LEXER (); + eof = lexer->eof (lexer); + switch (state) + { + case 0: + if (eof) + ADVANCE (47); + if (lookahead == '"') + ADVANCE (74); + if (lookahead == '#') + ADVANCE (73); + if (lookahead == 'N') + ADVANCE (30); + if (lookahead == '[') + ADVANCE (68); + if (lookahead == '\\') + SKIP (46) + if (lookahead == ']') + ADVANCE (72); + if (lookahead == 'd') + ADVANCE (54); + if (lookahead == 'p') + ADVANCE (50); + if (lookahead == 'u') + ADVANCE (56); + if (lookahead == 'w') + ADVANCE (52); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' + || lookahead == ' ') + SKIP (0) + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (60); + END_STATE (); + case 1: + if (lookahead == '\n') + SKIP (14) + END_STATE (); + case 2: + if (lookahead == '\n') + SKIP (14) + if (lookahead == '\r') + SKIP (1) + END_STATE (); + case 3: + if (lookahead == '\n') + SKIP (16) + END_STATE (); + case 4: + if (lookahead == '\n') + SKIP (16) + if (lookahead == '\r') + SKIP (3) + END_STATE (); + case 5: + if (lookahead == '\n') + SKIP (7) + END_STATE (); + case 6: + if (lookahead == '\n') + SKIP (7) + if (lookahead == '\r') + SKIP (5) + END_STATE (); + case 7: + if (lookahead == '\n') + ADVANCE (48); + if (lookahead == '\\') + SKIP (6) + if (lookahead == '\t' || lookahead == '\r' || lookahead == ' ') + SKIP (7) + END_STATE (); + case 8: + if (lookahead == ' ') + ADVANCE (13); + END_STATE (); + case 9: + if (lookahead == ' ') + ADVANCE (39); + END_STATE (); + case 10: + if (lookahead == ' ') + ADVANCE (40); + END_STATE (); + case 11: + if (lookahead == '-') + ADVANCE (43); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (24); + END_STATE (); + case 12: + if (lookahead == '-') + ADVANCE (44); + END_STATE (); + case 13: + if (lookahead == 'D') + ADVANCE (38); + END_STATE (); + case 14: + if (lookahead == 'N') + ADVANCE (30); + if (lookahead == '\\') + SKIP (2) + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' + || lookahead == ' ') + SKIP (14) + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (23); + END_STATE (); + case 15: + if (lookahead == '\\') + ADVANCE (63); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' + || lookahead == ' ') + ADVANCE (65); + if (lookahead != 0 && lookahead != '"') + ADVANCE (66); + END_STATE (); + case 16: + if (lookahead == '\\') + SKIP (4) + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' + || lookahead == ' ') + SKIP (16) + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (62); + END_STATE (); + case 17: + if (lookahead == 'a') + ADVANCE (36); + END_STATE (); + case 18: + if (lookahead == 'c') + ADVANCE (26); + END_STATE (); + case 19: + if (lookahead == 'c') + ADVANCE (20); + END_STATE (); + case 20: + if (lookahead == 'h') + ADVANCE (51); + END_STATE (); + case 21: + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (11); + END_STATE (); + case 22: + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (21); + END_STATE (); + case 23: + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (22); + END_STATE (); + case 24: + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (24); + END_STATE (); + case 25: + if (lookahead == 'i') + ADVANCE (32); + END_STATE (); + case 26: + if (lookahead == 'k') + ADVANCE (49); + END_STATE (); + case 27: + if (lookahead == 'l') + ADVANCE (55); + END_STATE (); + case 28: + if (lookahead == 'm') + ADVANCE (71); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (28); + END_STATE (); + case 29: + if (lookahead == 'n') + ADVANCE (69); + END_STATE (); + case 30: + if (lookahead == 'o') + ADVANCE (8); + END_STATE (); + case 31: + if (lookahead == 'o') + ADVANCE (33); + END_STATE (); + case 32: + if (lookahead == 'o') + ADVANCE (29); + END_STATE (); + case 33: + if (lookahead == 'p') + ADVANCE (53); + END_STATE (); + case 34: + if (lookahead == 'r') + ADVANCE (17); + END_STATE (); + case 35: + if (lookahead == 's') + ADVANCE (70); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (35); + END_STATE (); + case 36: + if (lookahead == 't') + ADVANCE (25); + END_STATE (); + case 37: + if (lookahead == 't') + ADVANCE (19); + END_STATE (); + case 38: + if (lookahead == 'u') + ADVANCE (34); + END_STATE (); + case 39: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (28); + END_STATE (); + case 40: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (35); + END_STATE (); + case 41: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (12); + END_STATE (); + case 42: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (67); + END_STATE (); + case 43: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (41); + END_STATE (); + case 44: + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (42); + END_STATE (); + case 45: + if (eof) + ADVANCE (47); + if (lookahead == '\n') + SKIP (0) + END_STATE (); + case 46: + if (eof) + ADVANCE (47); + if (lookahead == '\n') + SKIP (0) + if (lookahead == '\r') + SKIP (45) + END_STATE (); + case 47: + ACCEPT_TOKEN (ts_builtin_sym_end); + END_STATE (); + case 48: + ACCEPT_TOKEN (anon_sym_LF); + if (lookahead == '\n') + ADVANCE (48); + END_STATE (); + case 49: + ACCEPT_TOKEN (anon_sym_pick); + END_STATE (); + case 50: + ACCEPT_TOKEN (anon_sym_p); + if (lookahead == 'i') + ADVANCE (18); + END_STATE (); + case 51: + ACCEPT_TOKEN (anon_sym_watch); + END_STATE (); + case 52: + ACCEPT_TOKEN (anon_sym_w); + if (lookahead == 'a') + ADVANCE (37); + END_STATE (); + case 53: + ACCEPT_TOKEN (anon_sym_drop); + END_STATE (); + case 54: + ACCEPT_TOKEN (anon_sym_d); + if (lookahead == 'r') + ADVANCE (31); + END_STATE (); + case 55: + ACCEPT_TOKEN (anon_sym_url); + END_STATE (); + case 56: + ACCEPT_TOKEN (anon_sym_u); + if (lookahead == 'r') + ADVANCE (27); + END_STATE (); + case 57: + ACCEPT_TOKEN (sym_id); + if (lookahead == '-') + ADVANCE (43); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (61); + END_STATE (); + case 58: + ACCEPT_TOKEN (sym_id); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (57); + END_STATE (); + case 59: + ACCEPT_TOKEN (sym_id); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (58); + END_STATE (); + case 60: + ACCEPT_TOKEN (sym_id); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (59); + END_STATE (); + case 61: + ACCEPT_TOKEN (sym_id); + if (lookahead == 'h') + ADVANCE (9); + if (lookahead == 'm') + ADVANCE (10); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (61); + END_STATE (); + case 62: + ACCEPT_TOKEN (sym_id); + if (('0' <= lookahead && lookahead <= '9')) + ADVANCE (62); + END_STATE (); + case 63: + ACCEPT_TOKEN (aux_sym_title_token1); + if (lookahead == '\n') + ADVANCE (65); + if (lookahead == '\r') + ADVANCE (64); + if (lookahead != 0 && lookahead != '"') + ADVANCE (66); + END_STATE (); + case 64: + ACCEPT_TOKEN (aux_sym_title_token1); + if (lookahead == '\n') + ADVANCE (65); + if (lookahead != 0 && lookahead != '"') + ADVANCE (66); + END_STATE (); + case 65: + ACCEPT_TOKEN (aux_sym_title_token1); + if (lookahead == '\\') + ADVANCE (63); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' + || lookahead == ' ') + ADVANCE (65); + if (lookahead != 0 && lookahead != '"') + ADVANCE (66); + END_STATE (); + case 66: + ACCEPT_TOKEN (aux_sym_title_token1); + if (lookahead != 0 && lookahead != '"') + ADVANCE (66); + END_STATE (); + case 67: + ACCEPT_TOKEN (aux_sym_date_token1); + END_STATE (); + case 68: + ACCEPT_TOKEN (anon_sym_LBRACK); + END_STATE (); + case 69: + ACCEPT_TOKEN (anon_sym_NoDuration); + END_STATE (); + case 70: + ACCEPT_TOKEN (aux_sym_duration_token1); + END_STATE (); + case 71: + ACCEPT_TOKEN (aux_sym_duration_token2); + END_STATE (); + case 72: + ACCEPT_TOKEN (anon_sym_RBRACK); + END_STATE (); + case 73: + ACCEPT_TOKEN (sym_comment); + if (lookahead != 0 && lookahead != '\n') + ADVANCE (73); + END_STATE (); + case 74: + ACCEPT_TOKEN (sym_quote); + END_STATE (); + default: + return false; + } +} + +static const TSLexMode ts_lex_modes[STATE_COUNT] = { + [0] = { .lex_state = 0 }, [1] = { .lex_state = 0 }, + [2] = { .lex_state = 0 }, [3] = { .lex_state = 0 }, + [4] = { .lex_state = 0 }, [5] = { .lex_state = 0 }, + [6] = { .lex_state = 0 }, [7] = { .lex_state = 0 }, + [8] = { .lex_state = 14 }, [9] = { .lex_state = 0 }, + [10] = { .lex_state = 0 }, [11] = { .lex_state = 0 }, + [12] = { .lex_state = 0 }, [13] = { .lex_state = 0 }, + [14] = { .lex_state = 0 }, [15] = { .lex_state = 0 }, + [16] = { .lex_state = 15 }, [17] = { .lex_state = 0 }, + [18] = { .lex_state = 0 }, [19] = { .lex_state = 16 }, + [20] = { .lex_state = 0 }, [21] = { .lex_state = 7 }, + [22] = { .lex_state = 15 }, [23] = { .lex_state = 14 }, + [24] = { .lex_state = 0 }, [25] = { .lex_state = 15 }, + [26] = { .lex_state = 16 }, [27] = { .lex_state = 0 }, + [28] = { .lex_state = 7 }, [29] = { .lex_state = 0 }, + [30] = { .lex_state = 0 }, +}; + +static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { + [0] = { + [ts_builtin_sym_end] = ACTIONS(1), + [anon_sym_pick] = ACTIONS(1), + [anon_sym_p] = ACTIONS(1), + [anon_sym_watch] = ACTIONS(1), + [anon_sym_w] = ACTIONS(1), + [anon_sym_drop] = ACTIONS(1), + [anon_sym_d] = ACTIONS(1), + [anon_sym_url] = ACTIONS(1), + [anon_sym_u] = ACTIONS(1), + [sym_id] = ACTIONS(1), + [aux_sym_date_token1] = ACTIONS(1), + [anon_sym_LBRACK] = ACTIONS(1), + [anon_sym_NoDuration] = ACTIONS(1), + [aux_sym_duration_token1] = ACTIONS(1), + [aux_sym_duration_token2] = ACTIONS(1), + [anon_sym_RBRACK] = ACTIONS(1), + [sym_comment] = ACTIONS(1), + [sym_quote] = ACTIONS(1), + }, + [1] = { + [sym_source_file] = STATE(29), + [sym_line] = STATE(2), + [sym_command] = STATE(26), + [aux_sym_source_file_repeat1] = STATE(2), + [ts_builtin_sym_end] = ACTIONS(3), + [anon_sym_pick] = ACTIONS(5), + [anon_sym_p] = ACTIONS(7), + [anon_sym_watch] = ACTIONS(5), + [anon_sym_w] = ACTIONS(7), + [anon_sym_drop] = ACTIONS(5), + [anon_sym_d] = ACTIONS(7), + [anon_sym_url] = ACTIONS(5), + [anon_sym_u] = ACTIONS(7), + [sym_comment] = ACTIONS(9), + }, +}; + +static const uint16_t ts_small_parse_table[] = { + [0] = 6, + ACTIONS (11), + 1, + ts_builtin_sym_end, + ACTIONS (13), + 1, + sym_comment, + STATE (26), + 1, + sym_command, + STATE (3), + 2, + sym_line, + aux_sym_source_file_repeat1, + ACTIONS (5), + 4, + anon_sym_pick, + anon_sym_watch, + anon_sym_drop, + anon_sym_url, + ACTIONS (7), + 4, + anon_sym_p, + anon_sym_w, + anon_sym_d, + anon_sym_u, + [26] = 6, + ACTIONS (15), + 1, + ts_builtin_sym_end, + ACTIONS (23), + 1, + sym_comment, + STATE (26), + 1, + sym_command, + STATE (3), + 2, + sym_line, + aux_sym_source_file_repeat1, + ACTIONS (17), + 4, + anon_sym_pick, + anon_sym_watch, + anon_sym_drop, + anon_sym_url, + ACTIONS (20), + 4, + anon_sym_p, + anon_sym_w, + anon_sym_d, + anon_sym_u, + [52] = 2, + ACTIONS (28), + 4, + anon_sym_p, + anon_sym_w, + anon_sym_d, + anon_sym_u, + ACTIONS (26), + 6, + ts_builtin_sym_end, + anon_sym_pick, + anon_sym_watch, + anon_sym_drop, + anon_sym_url, + sym_comment, + [67] = 3, + ACTIONS (30), + 1, + sym_quote, + STATE (21), + 1, + sym_url, + STATE (22), + 1, + sym__q, + [77] = 3, + ACTIONS (32), + 1, + sym_quote, + STATE (7), + 1, + sym_title, + STATE (25), + 1, + sym__q, + [87] = 3, + ACTIONS (34), + 1, + sym_quote, + STATE (9), + 1, + sym_date, + STATE (23), + 1, + sym__q, + [97] = 1, + ACTIONS (36), + 3, + anon_sym_NoDuration, + aux_sym_duration_token1, + aux_sym_duration_token2, + [103] = 3, + ACTIONS (38), + 1, + sym_quote, + STATE (10), + 1, + sym_author, + STATE (16), + 1, + sym__q, + [113] = 3, + ACTIONS (40), + 1, + sym_quote, + STATE (5), + 1, + sym_duration, + STATE (18), + 1, + sym__q, + [123] = 2, + ACTIONS (42), + 1, + sym_quote, + STATE (30), + 1, + sym__q, + [130] = 2, + ACTIONS (44), + 1, + sym_quote, + STATE (28), + 1, + sym__q, + [137] = 2, + ACTIONS (46), + 1, + sym_quote, + STATE (24), + 1, + sym__q, + [144] = 2, + ACTIONS (48), + 1, + sym_quote, + STATE (17), + 1, + sym__q, + [151] = 2, + ACTIONS (50), + 1, + sym_quote, + STATE (20), + 1, + sym__q, + [158] = 1, + ACTIONS (52), + 1, + aux_sym_title_token1, + [162] = 1, + ACTIONS (54), + 1, + sym_quote, + [166] = 1, + ACTIONS (56), + 1, + anon_sym_LBRACK, + [170] = 1, + ACTIONS (58), + 1, + sym_id, + [174] = 1, + ACTIONS (60), + 1, + sym_quote, + [178] = 1, + ACTIONS (62), + 1, + anon_sym_LF, + [182] = 1, + ACTIONS (64), + 1, + aux_sym_title_token1, + [186] = 1, + ACTIONS (66), + 1, + aux_sym_date_token1, + [190] = 1, + ACTIONS (68), + 1, + sym_quote, + [194] = 1, + ACTIONS (70), + 1, + aux_sym_title_token1, + [198] = 1, + ACTIONS (72), + 1, + sym_id, + [202] = 1, + ACTIONS (74), + 1, + anon_sym_RBRACK, + [206] = 1, + ACTIONS (76), + 1, + anon_sym_LF, + [210] = 1, + ACTIONS (78), + 1, + ts_builtin_sym_end, + [214] = 1, + ACTIONS (80), + 1, + sym_quote, +}; + +static const uint32_t ts_small_parse_table_map[] = { + [SMALL_STATE (2)] = 0, [SMALL_STATE (3)] = 26, [SMALL_STATE (4)] = 52, + [SMALL_STATE (5)] = 67, [SMALL_STATE (6)] = 77, [SMALL_STATE (7)] = 87, + [SMALL_STATE (8)] = 97, [SMALL_STATE (9)] = 103, [SMALL_STATE (10)] = 113, + [SMALL_STATE (11)] = 123, [SMALL_STATE (12)] = 130, [SMALL_STATE (13)] = 137, + [SMALL_STATE (14)] = 144, [SMALL_STATE (15)] = 151, [SMALL_STATE (16)] = 158, + [SMALL_STATE (17)] = 162, [SMALL_STATE (18)] = 166, [SMALL_STATE (19)] = 170, + [SMALL_STATE (20)] = 174, [SMALL_STATE (21)] = 178, [SMALL_STATE (22)] = 182, + [SMALL_STATE (23)] = 186, [SMALL_STATE (24)] = 190, [SMALL_STATE (25)] = 194, + [SMALL_STATE (26)] = 198, [SMALL_STATE (27)] = 202, [SMALL_STATE (28)] = 206, + [SMALL_STATE (29)] = 210, [SMALL_STATE (30)] = 214, +}; + +static const TSParseActionEntry ts_parse_actions[] = { + [0] = { .entry = { .count = 0, .reusable = false } }, + [1] = { .entry = { .count = 1, .reusable = false } }, + RECOVER (), + [3] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_source_file, 0), + [5] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (19), + [7] = { .entry = { .count = 1, .reusable = false } }, + SHIFT (19), + [9] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (2), + [11] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_source_file, 1), + [13] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (3), + [15] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (aux_sym_source_file_repeat1, 2), + [17] = { .entry = { .count = 2, .reusable = true } }, + REDUCE (aux_sym_source_file_repeat1, 2), + SHIFT_REPEAT (19), + [20] = { .entry = { .count = 2, .reusable = false } }, + REDUCE (aux_sym_source_file_repeat1, 2), + SHIFT_REPEAT (19), + [23] = { .entry = { .count = 2, .reusable = true } }, + REDUCE (aux_sym_source_file_repeat1, 2), + SHIFT_REPEAT (3), + [26] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_line, 8), + [28] = { .entry = { .count = 1, .reusable = false } }, + REDUCE (sym_line, 8), + [30] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (22), + [32] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (25), + [34] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (23), + [36] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (27), + [38] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (16), + [40] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (18), + [42] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (30), + [44] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (28), + [46] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (24), + [48] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (17), + [50] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (20), + [52] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (13), + [54] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_title, 3), + [56] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (8), + [58] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_command, 1), + [60] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_date, 3), + [62] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (4), + [64] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (12), + [66] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (15), + [68] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_author, 3), + [70] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (14), + [72] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (6), + [74] = { .entry = { .count = 1, .reusable = true } }, + SHIFT (11), + [76] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_url, 3), + [78] = { .entry = { .count = 1, .reusable = true } }, + ACCEPT_INPUT (), + [80] = { .entry = { .count = 1, .reusable = true } }, + REDUCE (sym_duration, 5), +}; + +#ifdef __cplusplus +extern "C" +{ +#endif +#ifdef _WIN32 +#define extern __declspec (dllexport) +#endif + + extern const TSLanguage * + tree_sitter_yts (void) + { + static const TSLanguage language = { + .version = LANGUAGE_VERSION, + .symbol_count = SYMBOL_COUNT, + .alias_count = ALIAS_COUNT, + .token_count = TOKEN_COUNT, + .external_token_count = EXTERNAL_TOKEN_COUNT, + .state_count = STATE_COUNT, + .large_state_count = LARGE_STATE_COUNT, + .production_id_count = PRODUCTION_ID_COUNT, + .field_count = FIELD_COUNT, + .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, + .parse_table = &ts_parse_table[0][0], + .small_parse_table = ts_small_parse_table, + .small_parse_table_map = ts_small_parse_table_map, + .parse_actions = ts_parse_actions, + .symbol_names = ts_symbol_names, + .symbol_metadata = ts_symbol_metadata, + .public_symbol_map = ts_symbol_map, + .alias_map = ts_non_terminal_alias_map, + .alias_sequences = &ts_alias_sequences[0][0], + .lex_modes = ts_lex_modes, + .lex_fn = ts_lex, + .primary_state_ids = ts_primary_state_ids, + }; + return &language; + } +#ifdef __cplusplus +} +#endif diff --git a/pkgs/sources/tree-sitter-yts/src/tree_sitter/parser.h b/pkgs/sources/tree-sitter-yts/src/tree_sitter/parser.h new file mode 100644 index 00000000..972913cf --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/src/tree_sitter/parser.h @@ -0,0 +1,241 @@ +#ifndef TREE_SITTER_PARSER_H_ +#define TREE_SITTER_PARSER_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define ts_builtin_sym_error ((TSSymbol)-1) +#define ts_builtin_sym_end 0 +#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 + + typedef uint16_t TSStateId; + +#ifndef TREE_SITTER_API_H_ + typedef uint16_t TSSymbol; + typedef uint16_t TSFieldId; + typedef struct TSLanguage TSLanguage; +#endif + + typedef struct + { + TSFieldId field_id; + uint8_t child_index; + bool inherited; + } TSFieldMapEntry; + + typedef struct + { + uint16_t index; + uint16_t length; + } TSFieldMapSlice; + + typedef struct + { + bool visible; + bool named; + bool supertype; + } TSSymbolMetadata; + + typedef struct TSLexer TSLexer; + + struct TSLexer + { + int32_t lookahead; + TSSymbol result_symbol; + void (*advance) (TSLexer *, bool); + void (*mark_end) (TSLexer *); + uint32_t (*get_column) (TSLexer *); + bool (*is_at_included_range_start) (const TSLexer *); + bool (*eof) (const TSLexer *); + }; + + typedef enum + { + TSParseActionTypeShift, + TSParseActionTypeReduce, + TSParseActionTypeAccept, + TSParseActionTypeRecover, + } TSParseActionType; + + typedef union + { + struct + { + uint8_t type; + TSStateId state; + bool extra; + bool repetition; + } shift; + struct + { + uint8_t type; + uint8_t child_count; + TSSymbol symbol; + int16_t dynamic_precedence; + uint16_t production_id; + } reduce; + uint8_t type; + } TSParseAction; + + typedef struct + { + uint16_t lex_state; + uint16_t external_lex_state; + } TSLexMode; + + typedef union + { + TSParseAction action; + struct + { + uint8_t count; + bool reusable; + } entry; + } TSParseActionEntry; + + struct TSLanguage + { + uint32_t version; + uint32_t symbol_count; + uint32_t alias_count; + uint32_t token_count; + uint32_t external_token_count; + uint32_t state_count; + uint32_t large_state_count; + uint32_t production_id_count; + uint32_t field_count; + uint16_t max_alias_sequence_length; + const uint16_t *parse_table; + const uint16_t *small_parse_table; + const uint32_t *small_parse_table_map; + const TSParseActionEntry *parse_actions; + const char *const *symbol_names; + const char *const *field_names; + const TSFieldMapSlice *field_map_slices; + const TSFieldMapEntry *field_map_entries; + const TSSymbolMetadata *symbol_metadata; + const TSSymbol *public_symbol_map; + const uint16_t *alias_map; + const TSSymbol *alias_sequences; + const TSLexMode *lex_modes; + bool (*lex_fn) (TSLexer *, TSStateId); + bool (*keyword_lex_fn) (TSLexer *, TSStateId); + TSSymbol keyword_capture_token; + struct + { + const bool *states; + const TSSymbol *symbol_map; + void *(*create) (void); + void (*destroy) (void *); + bool (*scan) (void *, TSLexer *, const bool *symbol_whitelist); + unsigned (*serialize) (void *, char *); + void (*deserialize) (void *, const char *, unsigned); + } external_scanner; + const TSStateId *primary_state_ids; + }; + + /* + * Lexer Macros + */ + +#define START_LEXER() \ + bool result = false; \ + bool skip = false; \ + bool eof = false; \ + int32_t lookahead; \ + goto start; \ + next_state: \ + lexer->advance (lexer, skip); \ + start: \ + skip = false; \ + lookahead = lexer->lookahead; + +#define ADVANCE(state_value) \ + { \ + state = state_value; \ + goto next_state; \ + } + +#define SKIP(state_value) \ + { \ + skip = true; \ + state = state_value; \ + goto next_state; \ + } + +#define ACCEPT_TOKEN(symbol_value) \ + result = true; \ + lexer->result_symbol = symbol_value; \ + lexer->mark_end (lexer); + +#define END_STATE() return result; + + /* + * Parse Table Macros + */ + +#define SMALL_STATE(id) id - LARGE_STATE_COUNT + +#define STATE(id) id + +#define ACTIONS(id) id + +#define SHIFT(state_value) \ + { \ + { \ + .shift = {.type = TSParseActionTypeShift, .state = state_value } \ + } \ + } + +#define SHIFT_REPEAT(state_value) \ + { \ + { \ + .shift \ + = {.type = TSParseActionTypeShift, \ + .state = state_value, \ + .repetition = true } \ + } \ + } + +#define SHIFT_EXTRA() \ + { \ + { \ + .shift = {.type = TSParseActionTypeShift, .extra = true } \ + } \ + } + +#define REDUCE(symbol_val, child_count_val, ...) \ + { \ + { \ + .reduce = { .type = TSParseActionTypeReduce, \ + .symbol = symbol_val, \ + .child_count = child_count_val, \ + __VA_ARGS__ }, \ + } \ + } + +#define RECOVER() \ + { \ + { \ + .type = TSParseActionTypeRecover \ + } \ + } + +#define ACCEPT_INPUT() \ + { \ + { \ + .type = TSParseActionTypeAccept \ + } \ + } + +#ifdef __cplusplus +} +#endif + +#endif // TREE_SITTER_PARSER_H_ diff --git a/pkgs/sources/tree-sitter-yts/treefmt.toml b/pkgs/sources/tree-sitter-yts/treefmt.toml new file mode 100644 index 00000000..3d604b40 --- /dev/null +++ b/pkgs/sources/tree-sitter-yts/treefmt.toml @@ -0,0 +1,35 @@ +[formatter.nix] +command = "nixpkgs-fmt" +includes = ["*.nix"] +excludes = ["test/**.nix"] + +[formatter.prettier] +command = "prettier" +options = ["--write"] +includes = [ + "*.css", + "*.html", + "*.js", + "*.json", + "*.jsx", + "*.md", + "*.mdx", + "*.scss", + "*.ts", +] +excludes = ["src/**.json"] + +[formatter.rust] +command = "rustfmt" +options = ["--edition", "2018"] +includes = ["*.rs"] + +[formatter.c] +command = "clang-format" +options = ["-i"] +includes = ["*.c", "*.cpp", "*.cc", "*.h", "*.hpp"] +excludes = [ + "bindings/node/binding.cc", + "src/parser.c", + "src/tree_sitter/parser.h", +] diff --git a/pkgs/sources/update_pkgs.sh b/pkgs/sources/update_pkgs.sh new file mode 100755 index 00000000..be1573c6 --- /dev/null +++ b/pkgs/sources/update_pkgs.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env sh + +die() { + printf "\033[31;1mError: \033[0m%s" "$1" + exit 1 +} + +cd "$(dirname "$0")" || die "Bug: run with false dirname ('$0')!" + +fd . --type directory --max-depth 1 | while read -r dir; do + cd "$dir" || die "Dir '$dir' does not exist" + + if [ -f update.sh ]; then + printf "\033[34;1m> \033[0m\033[34;1m%s\033[0m\n" "Running '${dir}update.sh'" + + [ -f flake.nix ] && nix flake update + + direnv allow + eval "$(direnv export bash 2>/dev/null)" + ./update.sh "$@" + fi + cd - >/dev/null || die "Bug: Last dir does not exist" +done + +# vim: ft=sh diff --git a/pkgs/sources/update_vim_plugins/.envrc b/pkgs/sources/update_vim_plugins/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/pkgs/sources/update_vim_plugins/.envrc @@ -0,0 +1 @@ +use flake diff --git a/pkgs/sources/update_vim_plugins/check-duplicates.sh b/pkgs/sources/update_vim_plugins/check-duplicates.sh new file mode 100755 index 00000000..781b8aeb --- /dev/null +++ b/pkgs/sources/update_vim_plugins/check-duplicates.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +plugins="$(grep -E "^ [a-zA-Z-]+ =" ./pkgs/vim-plugins.nix | sed -E 's/^ ([a-zA-Z-]+) =.*$/\1/' | sort)" +count=$(echo "$plugins" | uniq -d | wc -l) + +echo "duplicates count: $count" + +if [ "$count" -gt 0 ]; then + filtered_plugins=$(echo "$plugins" | uniq -d) + + if [ "$1" == "check-only" ]; then + echo "$filtered_plugins" + exit 1 + else + known_issues=$(gh issue list --state "open" --label "bot" --json "body" | jq -r ".[].body") + + echo "known_issues: $known_issues" + + # iterate over plugins we found missing and + # compare them to all open issues. + # We no matching issue was found, we create a new one + for f in $filtered_plugins; do # do not add " " here. It would break the plugin + found=false + + for k in $known_issues; do + if [[ $f == "$k" ]]; then + found=true + break + fi + done + + # test if matching issue was found + if ! $found; then + echo "Did not find an issue for $f. Creating a new one ..." + gh issue create --title "Detected broken plugin: $f" --label "bot" --body "$f" + else + echo "Issue for $f already exists" + fi + done + fi +else + echo "No duplicates found" +fi diff --git a/pkgs/sources/update_vim_plugins/default.nix b/pkgs/sources/update_vim_plugins/default.nix new file mode 100644 index 00000000..7f0b3f0d --- /dev/null +++ b/pkgs/sources/update_vim_plugins/default.nix @@ -0,0 +1,17 @@ +[ + ( + final: prev: { + update-vim-plugins = import ./package.nix { + inherit + (prev) + python3 + # dependencies + + nix + alejandra + nix-prefetch-git + ; + }; + } + ) +] diff --git a/pkgs/sources/update_vim_plugins/flake.lock b/pkgs/sources/update_vim_plugins/flake.lock new file mode 100644 index 00000000..50494465 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1715087517, + "narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b211b392b8486ee79df6cdfb1157ad2133427a29", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/update_vim_plugins/flake.nix b/pkgs/sources/update_vim_plugins/flake.nix new file mode 100644 index 00000000..ef440af0 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/flake.nix @@ -0,0 +1,24 @@ +{ + description = "update_vim_plugins"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + }: (flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; + in { + devShells.default = pkgs.mkShell { + packages = [ + pkgs.python3 + pkgs.poetry + ]; + }; + })); +} diff --git a/pkgs/sources/update_vim_plugins/package.nix b/pkgs/sources/update_vim_plugins/package.nix new file mode 100644 index 00000000..e74a29b1 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/package.nix @@ -0,0 +1,47 @@ +{ + python3, + # dependencies + nix, + alejandra, + nix-prefetch-git, +}: +python3.pkgs.buildPythonApplication { + pname = "update-vim-plugins"; + version = "0.1.0"; + format = "pyproject"; + + src = ./.; + + # NOTE: The test are not really meant to work <2023-12-09> + doCheck = false; + + nativeBuildInputs = [ + python3.pkgs.poetry-core + ]; + buildInputs = [ + alejandra + nix-prefetch-git + nix + ]; + propagatedBuildInputs = with python3.pkgs; [ + requests + cleo + jsonpickle + dateparser + ]; + nativeCheckInputs = with python3.pkgs; [ + pytestCheckHook + + pytest-cov + pytest-mock + ]; + pytestFlagsArray = [ + "--cov" + "update_vim_plugins" + "--cov-report" + "term-missing:skip-covered" + "--cov-fail-under" + "50" + "update_vim_plugins/tests" + ]; +} diff --git a/pkgs/sources/update_vim_plugins/poetry.lock b/pkgs/sources/update_vim_plugins/poetry.lock new file mode 100644 index 00000000..f4764b42 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/poetry.lock @@ -0,0 +1,680 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "cleo" +version = "2.1.0" +description = "Cleo allows you to create beautiful and testable command-line interfaces." +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "cleo-2.1.0-py3-none-any.whl", hash = "sha256:4a31bd4dd45695a64ee3c4758f583f134267c2bc518d8ae9a29cf237d009b07e"}, + {file = "cleo-2.1.0.tar.gz", hash = "sha256:0b2c880b5d13660a7ea651001fb4acb527696c01f15c9ee650f377aa543fd523"}, +] + +[package.dependencies] +crashtest = ">=0.4.1,<0.5.0" +rapidfuzz = ">=3.0.0,<4.0.0" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.5.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, + {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, + {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, + {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, + {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, + {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, + {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, + {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, + {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, + {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, + {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, + {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, + {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, + {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, + {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, + {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, + {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, + {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, + {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, + {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, + {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, + {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, + {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, + {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, + {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, + {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, + {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, + {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "crashtest" +version = "0.4.1" +description = "Manage Python errors with ease" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "crashtest-0.4.1-py3-none-any.whl", hash = "sha256:8d23eac5fa660409f57472e3851dab7ac18aba459a8d19cbbba86d3d5aecd2a5"}, + {file = "crashtest-0.4.1.tar.gz", hash = "sha256:80d7b1f316ebfbd429f648076d6275c877ba30ba48979de4191714a75266f0ce"}, +] + +[[package]] +name = "dateparser" +version = "1.2.0" +description = "Date parsing library designed to parse dates from HTML pages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dateparser-1.2.0-py2.py3-none-any.whl", hash = "sha256:0b21ad96534e562920a0083e97fd45fa959882d4162acc358705144520a35830"}, + {file = "dateparser-1.2.0.tar.gz", hash = "sha256:7975b43a4222283e0ae15be7b4999d08c9a70e2d378ac87385b1ccf2cffbbb30"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "hijri-converter"] +fasttext = ["fasttext"] +langdetect = ["langdetect"] + +[[package]] +name = "exceptiongroup" +version = "1.2.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, + {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + +[[package]] +name = "jsonpickle" +version = "3.0.4" +description = "Serialize any Python object to JSON" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonpickle-3.0.4-py3-none-any.whl", hash = "sha256:04ae7567a14269579e3af66b76bda284587458d7e8a204951ca8f71a3309952e"}, + {file = "jsonpickle-3.0.4.tar.gz", hash = "sha256:a1b14c8d6221cd8f394f2a97e735ea1d7edc927fbd135b26f2f8700657c8c62b"}, +] + +[package.extras] +docs = ["furo", "rst.linker (>=1.9)", "sphinx"] +packaging = ["build", "twine"] +testing = ["bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy", "scipy (>=1.9.3)", "simplejson", "sqlalchemy", "ujson"] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "pytest" +version = "7.4.4" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "4.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +description = "Thin-wrapper around the mock package for easier use with pytest" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + +[package.extras] +dev = ["pre-commit", "pytest-asyncio", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "rapidfuzz" +version = "3.9.0" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bd375c4830fee11d502dd93ecadef63c137ae88e1aaa29cc15031fa66d1e0abb"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:55e2c5076f38fc1dbaacb95fa026a3e409eee6ea5ac4016d44fb30e4cad42b20"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:488f74126904db6b1bea545c2f3567ea882099f4c13f46012fe8f4b990c683df"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3f2d1ea7cd57dfcd34821e38b4924c80a31bcf8067201b1ab07386996a9faee"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b11e602987bcb4ea22b44178851f27406fca59b0836298d0beb009b504dba266"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3083512e9bf6ed2bb3d25883922974f55e21ae7f8e9f4e298634691ae1aee583"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b33c6d4b3a1190bc0b6c158c3981535f9434e8ed9ffa40cf5586d66c1819fb4b"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcb95fde22f98e6d0480db8d6038c45fe2d18a338690e6f9bba9b82323f3469"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:08d8b49b3a4fb8572e480e73fcddc750da9cbb8696752ee12cca4bf8c8220d52"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e721842e6b601ebbeb8cc5e12c75bbdd1d9e9561ea932f2f844c418c31256e82"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7988363b3a415c5194ce1a68d380629247f8713e669ad81db7548eb156c4f365"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:2d267d4c982ab7d177e994ab1f31b98ff3814f6791b90d35dda38307b9e7c989"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0bb28ab5300cf974c7eb68ea21125c493e74b35b1129e629533468b2064ae0a2"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-win32.whl", hash = "sha256:1b1f74997b6d94d66375479fa55f70b1c18e4d865d7afcd13f0785bfd40a9d3c"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c56d2efdfaa1c642029f3a7a5bb76085c5531f7a530777be98232d2ce142553c"}, + {file = "rapidfuzz-3.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:6a83128d505cac76ea560bb9afcb3f6986e14e50a6f467db9a31faef4bd9b347"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e2218d62ab63f3c5ad48eced898854d0c2c327a48f0fb02e2288d7e5332a22c8"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:36bf35df2d6c7d5820da20a6720aee34f67c15cd2daf8cf92e8141995c640c25"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:905b01a9b633394ff6bb5ebb1c5fd660e0e180c03fcf9d90199cc6ed74b87cf7"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33cfabcb7fd994938a6a08e641613ce5fe46757832edc789c6a5602e7933d6fa"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1179dcd3d150a67b8a678cd9c84f3baff7413ff13c9e8fe85e52a16c97e24c9b"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47d97e28c42f1efb7781993b67c749223f198f6653ef177a0c8f2b1c516efcaf"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28da953eb2ef9ad527e536022da7afff6ace7126cdd6f3e21ac20f8762e76d2c"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:182b4e11de928fb4834e8f8b5ecd971b5b10a86fabe8636ab65d3a9b7e0e9ca7"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c74f2da334ce597f31670db574766ddeaee5d9430c2c00e28d0fbb7f76172036"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:014ac55b03f4074f903248ded181f3000f4cdbd134e6155cbf643f0eceb4f70f"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c4ef34b2ddbf448f1d644b4ec6475df8bbe5b9d0fee173ff2e87322a151663bd"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fc02157f521af15143fae88f92ef3ddcc4e0cff05c40153a9549dc0fbdb9adb3"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ff08081c49b18ba253a99e6a47f492e6ee8019e19bbb6ddc3ed360cd3ecb2f62"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-win32.whl", hash = "sha256:b9bf90b3d96925cbf8ef44e5ee3cf39ef0c422f12d40f7a497e91febec546650"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:d5d5684f54d82d9b0cf0b2701e55a630527a9c3dd5ddcf7a2e726a475ac238f2"}, + {file = "rapidfuzz-3.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:a2de844e0e971d7bd8aa41284627dbeacc90e750b90acfb016836553c7a63192"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f81fe99a69ac8ee3fd905e70c62f3af033901aeb60b69317d1d43d547b46e510"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:633b9d03fc04abc585c197104b1d0af04b1f1db1abc99f674d871224cd15557a"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab872cb57ae97c54ba7c71a9e3c9552beb57cb907c789b726895576d1ea9af6f"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bdd8c15c3a14e409507fdf0c0434ec481d85c6cbeec8bdcd342a8cd1eda03825"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2444d8155d9846f206e2079bb355b85f365d9457480b0d71677a112d0a7f7128"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f83bd3d01f04061c3660742dc85143a89d49fd23eb31eccbf60ad56c4b955617"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ca799f882364e69d0872619afb19efa3652b7133c18352e4a3d86a324fb2bb1"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6993d361f28b9ef5f0fa4e79b8541c2f3507be7471b9f9cb403a255e123b31e1"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:170822a1b1719f02b58e3dce194c8ad7d4c5b39be38c0fdec603bd19c6f9cf81"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0e86e39c1c1a0816ceda836e6f7bd3743b930cbc51a43a81bb433b552f203f25"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:731269812ea837e0b93d913648e404736407408e33a00b75741e8f27c590caa2"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:8e5ff882d3a3d081157ceba7e0ebc7fac775f95b08cbb143accd4cece6043819"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2003071aa633477a01509890c895f9ef56cf3f2eaa72c7ec0b567f743c1abcba"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-win32.whl", hash = "sha256:13857f9070600ea1f940749f123b02d0b027afbaa45e72186df0f278915761d0"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:134b7098ac109834eeea81424b6822f33c4c52bf80b81508295611e7a21be12a"}, + {file = "rapidfuzz-3.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:2a96209f046fe328be30fc43f06e3d4b91f0d5b74e9dcd627dbfd65890fa4a5e"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:544b0bf9d17170720809918e9ccd0d482d4a3a6eca35630d8e1459f737f71755"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d536f8beb8dd82d6efb20fe9f82c2cfab9ffa0384b5d184327e393a4edde91d"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:30f7609da871510583f87484a10820b26555a473a90ab356cdda2f3b4456256c"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f4a2468432a1db491af6f547fad8f6d55fa03e57265c2f20e5eaceb68c7907e"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a7ec4676242c8a430509cff42ce98bca2fbe30188a63d0f60fdcbfd7e84970"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dcb523243e988c849cf81220164ec3bbed378a699e595a8914fffe80596dc49f"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4eea3bf72c4fe68e957526ffd6bcbb403a21baa6b3344aaae2d3252313df6199"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4514980a5d204c076dd5b756960f6b1b7598f030009456e6109d76c4c331d03c"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9a06a99f1335fe43464d7121bc6540de7cd9c9475ac2025babb373fe7f27846b"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6c1ed63345d1581c39d4446b1a8c8f550709656ce2a3c88c47850b258167f3c2"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cd2e6e97daf17ebb3254285cf8dd86c60d56d6cf35c67f0f9a557ef26bd66290"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9bc0f7e6256a9c668482c41c8a3de5d0aa12e8ca346dcc427b97c7edb82cba48"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c09f4e87e82a164c9db769474bc61f8c8b677f2aeb0234b8abac73d2ecf9799"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-win32.whl", hash = "sha256:e65b8f7921bf60cbb207c132842a6b45eefef48c4c3b510eb16087d6c08c70af"}, + {file = "rapidfuzz-3.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9d6478957fb35c7844ad08f2442b62ba76c1857a56370781a707eefa4f4981e1"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65d9250a4b0bf86320097306084bc3ca479c8f5491927c170d018787793ebe95"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:47b7c0840afa724db3b1a070bc6ed5beab73b4e659b1d395023617fc51bf68a2"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a16c48c6df8fb633efbbdea744361025d01d79bca988f884a620e63e782fe5b"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48105991ff6e4a51c7f754df500baa070270ed3d41784ee0d097549bc9fcb16d"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a7f273906b3c7cc6d63a76e088200805947aa0bc1ada42c6a0e582e19c390d7"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c396562d304e974b4b0d5cd3afc4f92c113ea46a36e6bc62e45333d6aa8837e"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68da1b70458fea5290ec9a169fcffe0c17ff7e5bb3c3257e63d7021a50601a8e"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c5b8f9a7b177af6ce7c6ad5b95588b4b73e37917711aafa33b2e79ee80fe709"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3c42a238bf9dd48f4ccec4c6934ac718225b00bb3a438a008c219e7ccb3894c7"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a365886c42177b2beab475a50ba311b59b04f233ceaebc4c341f6f91a86a78e2"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ce897b5dafb7fb7587a95fe4d449c1ea0b6d9ac4462fbafefdbbeef6eee4cf6a"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:413ac49bae291d7e226a5c9be65c71b2630b3346bce39268d02cb3290232e4b7"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8982fc3bd49d55a91569fc8a3feba0de4cef0b391ff9091be546e9df075b81"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-win32.whl", hash = "sha256:3904d0084ab51f82e9f353031554965524f535522a48ec75c30b223eb5a0a488"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:3733aede16ea112728ffeafeb29ccc62e095ed8ec816822fa2a82e92e2c08696"}, + {file = "rapidfuzz-3.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:fc4e26f592b51f97acf0a3f8dfed95e4d830c6a8fbf359361035df836381ab81"}, + {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e33362e98c7899b5f60dcb06ada00acd8673ce0d59aefe9a542701251fd00423"}, + {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb67cf43ad83cb886cbbbff4df7dcaad7aedf94d64fca31aea0da7d26684283c"}, + {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e2e106cc66453bb80d2ad9c0044f8287415676df5c8036d737d05d4b9cdbf8e"}, + {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1256915f7e7a5cf2c151c9ac44834b37f9bd1c97e8dec6f936884f01b9dfc7d"}, + {file = "rapidfuzz-3.9.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ae643220584518cbff8bf2974a0494d3e250763af816b73326a512c86ae782ce"}, + {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:491274080742110427f38a6085bb12dffcaff1eef12dccf9e8758398c7e3957e"}, + {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bc5559b9b94326922c096b30ae2d8e5b40b2e9c2c100c2cc396ad91bcb84d30"}, + {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:849160dc0f128acb343af514ca827278005c1d00148d025e4035e034fc2d8c7f"}, + {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:623883fb78e692d54ed7c43b09beec52c6685f10a45a7518128e25746667403b"}, + {file = "rapidfuzz-3.9.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d20ab9abc7e19767f1951772a6ab14cb4eddd886493c2da5ee12014596ad253f"}, + {file = "rapidfuzz-3.9.0.tar.gz", hash = "sha256:b182f0fb61f6ac435e416eb7ab330d62efdbf9b63cf0c7fa12d1f57c2eaaf6f3"}, +] + +[package.extras] +full = ["numpy"] + +[[package]] +name = "regex" +version = "2024.4.28" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.4.28-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd196d056b40af073d95a2879678585f0b74ad35190fac04ca67954c582c6b61"}, + {file = "regex-2024.4.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8bb381f777351bd534462f63e1c6afb10a7caa9fa2a421ae22c26e796fe31b1f"}, + {file = "regex-2024.4.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:47af45b6153522733aa6e92543938e97a70ce0900649ba626cf5aad290b737b6"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d6a550425cc51c656331af0e2b1651e90eaaa23fb4acde577cf15068e2e20f"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf29304a8011feb58913c382902fde3395957a47645bf848eea695839aa101b7"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:92da587eee39a52c91aebea8b850e4e4f095fe5928d415cb7ed656b3460ae79a"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6277d426e2f31bdbacb377d17a7475e32b2d7d1f02faaecc48d8e370c6a3ff31"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:28e1f28d07220c0f3da0e8fcd5a115bbb53f8b55cecf9bec0c946eb9a059a94c"}, + {file = "regex-2024.4.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaa179975a64790c1f2701ac562b5eeb733946eeb036b5bcca05c8d928a62f10"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6f435946b7bf7a1b438b4e6b149b947c837cb23c704e780c19ba3e6855dbbdd3"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:19d6c11bf35a6ad077eb23852827f91c804eeb71ecb85db4ee1386825b9dc4db"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:fdae0120cddc839eb8e3c15faa8ad541cc6d906d3eb24d82fb041cfe2807bc1e"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e672cf9caaf669053121f1766d659a8813bd547edef6e009205378faf45c67b8"}, + {file = "regex-2024.4.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f57515750d07e14743db55d59759893fdb21d2668f39e549a7d6cad5d70f9fea"}, + {file = "regex-2024.4.28-cp310-cp310-win32.whl", hash = "sha256:a1409c4eccb6981c7baabc8888d3550df518add6e06fe74fa1d9312c1838652d"}, + {file = "regex-2024.4.28-cp310-cp310-win_amd64.whl", hash = "sha256:1f687a28640f763f23f8a9801fe9e1b37338bb1ca5d564ddd41619458f1f22d1"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84077821c85f222362b72fdc44f7a3a13587a013a45cf14534df1cbbdc9a6796"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b45d4503de8f4f3dc02f1d28a9b039e5504a02cc18906cfe744c11def942e9eb"}, + {file = "regex-2024.4.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:457c2cd5a646dd4ed536c92b535d73548fb8e216ebee602aa9f48e068fc393f3"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b51739ddfd013c6f657b55a508de8b9ea78b56d22b236052c3a85a675102dc6"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:459226445c7d7454981c4c0ce0ad1a72e1e751c3e417f305722bbcee6697e06a"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:670fa596984b08a4a769491cbdf22350431970d0112e03d7e4eeaecaafcd0fec"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe00f4fe11c8a521b173e6324d862ee7ee3412bf7107570c9b564fe1119b56fb"}, + {file = "regex-2024.4.28-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36f392dc7763fe7924575475736bddf9ab9f7a66b920932d0ea50c2ded2f5636"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:23a412b7b1a7063f81a742463f38821097b6a37ce1e5b89dd8e871d14dbfd86b"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f1d6e4b7b2ae3a6a9df53efbf199e4bfcff0959dbdb5fd9ced34d4407348e39a"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:499334ad139557de97cbc4347ee921c0e2b5e9c0f009859e74f3f77918339257"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:0940038bec2fe9e26b203d636c44d31dd8766abc1fe66262da6484bd82461ccf"}, + {file = "regex-2024.4.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:66372c2a01782c5fe8e04bff4a2a0121a9897e19223d9eab30c54c50b2ebeb7f"}, + {file = "regex-2024.4.28-cp311-cp311-win32.whl", hash = "sha256:c77d10ec3c1cf328b2f501ca32583625987ea0f23a0c2a49b37a39ee5c4c4630"}, + {file = "regex-2024.4.28-cp311-cp311-win_amd64.whl", hash = "sha256:fc0916c4295c64d6890a46e02d4482bb5ccf33bf1a824c0eaa9e83b148291f90"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:08a1749f04fee2811c7617fdd46d2e46d09106fa8f475c884b65c01326eb15c5"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b8eb28995771c087a73338f695a08c9abfdf723d185e57b97f6175c5051ff1ae"}, + {file = "regex-2024.4.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd7ef715ccb8040954d44cfeff17e6b8e9f79c8019daae2fd30a8806ef5435c0"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb0315a2b26fde4005a7c401707c5352df274460f2f85b209cf6024271373013"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2fc053228a6bd3a17a9b0a3f15c3ab3cf95727b00557e92e1cfe094b88cc662"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fe9739a686dc44733d52d6e4f7b9c77b285e49edf8570754b322bca6b85b4cc"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74fcf77d979364f9b69fcf8200849ca29a374973dc193a7317698aa37d8b01c"}, + {file = "regex-2024.4.28-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:965fd0cf4694d76f6564896b422724ec7b959ef927a7cb187fc6b3f4e4f59833"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2fef0b38c34ae675fcbb1b5db760d40c3fc3612cfa186e9e50df5782cac02bcd"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bc365ce25f6c7c5ed70e4bc674f9137f52b7dd6a125037f9132a7be52b8a252f"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:ac69b394764bb857429b031d29d9604842bc4cbfd964d764b1af1868eeebc4f0"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:144a1fc54765f5c5c36d6d4b073299832aa1ec6a746a6452c3ee7b46b3d3b11d"}, + {file = "regex-2024.4.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2630ca4e152c221072fd4a56d4622b5ada876f668ecd24d5ab62544ae6793ed6"}, + {file = "regex-2024.4.28-cp312-cp312-win32.whl", hash = "sha256:7f3502f03b4da52bbe8ba962621daa846f38489cae5c4a7b5d738f15f6443d17"}, + {file = "regex-2024.4.28-cp312-cp312-win_amd64.whl", hash = "sha256:0dd3f69098511e71880fb00f5815db9ed0ef62c05775395968299cb400aeab82"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:374f690e1dd0dbdcddea4a5c9bdd97632cf656c69113f7cd6a361f2a67221cb6"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f87ae6b96374db20f180eab083aafe419b194e96e4f282c40191e71980c666"}, + {file = "regex-2024.4.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5dbc1bcc7413eebe5f18196e22804a3be1bfdfc7e2afd415e12c068624d48247"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f85151ec5a232335f1be022b09fbbe459042ea1951d8a48fef251223fc67eee1"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57ba112e5530530fd175ed550373eb263db4ca98b5f00694d73b18b9a02e7185"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:224803b74aab56aa7be313f92a8d9911dcade37e5f167db62a738d0c85fdac4b"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a54a047b607fd2d2d52a05e6ad294602f1e0dec2291152b745870afc47c1397"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a2a512d623f1f2d01d881513af9fc6a7c46e5cfffb7dc50c38ce959f9246c94"}, + {file = "regex-2024.4.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c06bf3f38f0707592898428636cbb75d0a846651b053a1cf748763e3063a6925"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1031a5e7b048ee371ab3653aad3030ecfad6ee9ecdc85f0242c57751a05b0ac4"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d7a353ebfa7154c871a35caca7bfd8f9e18666829a1dc187115b80e35a29393e"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7e76b9cfbf5ced1aca15a0e5b6f229344d9b3123439ffce552b11faab0114a02"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5ce479ecc068bc2a74cb98dd8dba99e070d1b2f4a8371a7dfe631f85db70fe6e"}, + {file = "regex-2024.4.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d77b6f63f806578c604dca209280e4c54f0fa9a8128bb8d2cc5fb6f99da4150"}, + {file = "regex-2024.4.28-cp38-cp38-win32.whl", hash = "sha256:d84308f097d7a513359757c69707ad339da799e53b7393819ec2ea36bc4beb58"}, + {file = "regex-2024.4.28-cp38-cp38-win_amd64.whl", hash = "sha256:2cc1b87bba1dd1a898e664a31012725e48af826bf3971e786c53e32e02adae6c"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7413167c507a768eafb5424413c5b2f515c606be5bb4ef8c5dee43925aa5718b"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:108e2dcf0b53a7c4ab8986842a8edcb8ab2e59919a74ff51c296772e8e74d0ae"}, + {file = "regex-2024.4.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f1c5742c31ba7d72f2dedf7968998730664b45e38827637e0f04a2ac7de2f5f1"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecc6148228c9ae25ce403eade13a0961de1cb016bdb35c6eafd8e7b87ad028b1"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7d893c8cf0e2429b823ef1a1d360a25950ed11f0e2a9df2b5198821832e1947"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4290035b169578ffbbfa50d904d26bec16a94526071ebec3dadbebf67a26b25e"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a22ae1cfd82e4ffa2066eb3390777dc79468f866f0625261a93e44cdf6482b"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd24fd140b69f0b0bcc9165c397e9b2e89ecbeda83303abf2a072609f60239e2"}, + {file = "regex-2024.4.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:39fb166d2196413bead229cd64a2ffd6ec78ebab83fff7d2701103cf9f4dfd26"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9301cc6db4d83d2c0719f7fcda37229691745168bf6ae849bea2e85fc769175d"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c3d389e8d76a49923683123730c33e9553063d9041658f23897f0b396b2386f"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:99ef6289b62042500d581170d06e17f5353b111a15aa6b25b05b91c6886df8fc"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b91d529b47798c016d4b4c1d06cc826ac40d196da54f0de3c519f5a297c5076a"}, + {file = "regex-2024.4.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:43548ad74ea50456e1c68d3c67fff3de64c6edb85bcd511d1136f9b5376fc9d1"}, + {file = "regex-2024.4.28-cp39-cp39-win32.whl", hash = "sha256:05d9b6578a22db7dedb4df81451f360395828b04f4513980b6bd7a1412c679cc"}, + {file = "regex-2024.4.28-cp39-cp39-win_amd64.whl", hash = "sha256:3986217ec830c2109875be740531feb8ddafe0dfa49767cdcd072ed7e8927962"}, + {file = "regex-2024.4.28.tar.gz", hash = "sha256:83ab366777ea45d58f72593adf35d36ca911ea8bd838483c1823b883a121b0e4"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.10" +content-hash = "f65cd66387236673e2a5afb3b2a75362c97815cdde592a86712737fb9ca71695" diff --git a/pkgs/sources/update_vim_plugins/pyproject.toml b/pkgs/sources/update_vim_plugins/pyproject.toml new file mode 100644 index 00000000..38caf76d --- /dev/null +++ b/pkgs/sources/update_vim_plugins/pyproject.toml @@ -0,0 +1,45 @@ +[tool.poetry] +name = "update_vim_plugins" +version = "0.1.0" +description = "" +authors = ["Your Name <you@example.com>"] +packages = [{ include = "update_vim_plugins" }] + +[tool.poetry.scripts] +update-vim-plugins = "update_vim_plugins.__main__:main" + +[tool.poetry.dependencies] +python = "^3.10" +requests = "^2.28.2" +cleo = "^2.0.1" +jsonpickle = "*" +dateparser = "^1.1.8" + +[tool.poetry.group.test.dependencies] +pytest-cov = "^4.0.0" +pytest = "^7.3.1" +pytest-mock = "^3.10.0" + +[tool.poetry.group.dev] +optional = true + +[tool.poetry.group.dev.dependencies] +# black = "^23.3.0" +# ruff-lsp = "^0.0.24" +# mypy = "^1.2.0" +# types-requests = "^2.28.11.17" +# isort = "^5.12.0" +# ruff = "^0.0.262" + +[tool.isort] +profile = "black" + +[tool.black] +line-length = 120 + +[tool.ruff] +line-length = 120 + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/pkgs/sources/update_vim_plugins/update.sh b/pkgs/sources/update_vim_plugins/update.sh new file mode 100755 index 00000000..1bad12a9 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +poetry update --lock + +# vim: ft=sh diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/__init__.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/__init__.py diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/__main__.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/__main__.py new file mode 100644 index 00000000..a8d9e06f --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/__main__.py @@ -0,0 +1,15 @@ +from cleo.application import Application + +from .update import UpdateCommand +from .cleanup import CleanUpCommand + + +def main(): + application = Application() + application.add(UpdateCommand()) + application.add(CleanUpCommand()) + application.run() + + +if __name__ == "__main__": + main() diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/cleanup.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/cleanup.py new file mode 100644 index 00000000..fd313ed0 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/cleanup.py @@ -0,0 +1,100 @@ +from cleo.commands.command import Command +from cleo.helpers import argument + +from .helpers import read_manifest_to_spec, read_blacklist_to_spec, write_manifest_from_spec + + +class CleanUpCommand(Command): + name = "cleanup" + description = "Clean up manifest" + arguments = [argument("plug_dir", description="Path to the plugin directory", optional=False)] + + def handle(self): + """Main command function""" + + plug_dir = self.argument("plug_dir") + self.line("<comment>Checking manifest file</comment>") + # all cleaning up will be done during reading and writing automatically + manifest = read_manifest_to_spec(plug_dir) + blacklist = read_blacklist_to_spec(plug_dir) + + new_manifest = [spec for spec in manifest if spec not in blacklist] + + new_manifest_filterd = self.filter_renamed(new_manifest) + + write_manifest_from_spec(new_manifest_filterd, plug_dir) + + self.line("<comment>Done</comment>") + + def filter_renamed(self, specs): + """Filter specs that define the same plugin (same owner and same repo) but with different properties. + This could be a different name, source, or branch + """ + + error = False + for i, p in enumerate(specs): + for p2 in specs: + same_owner = p.owner.lower() == p2.owner.lower() + same_repo = p.repo.lower() == p2.repo.lower() + different_specs = p != p2 + marked_duplicate = p.marked_duplicate or p2.marked_duplicate + + if same_owner and same_repo and different_specs and not marked_duplicate: + self.line("<info>The following lines appear to define the same plugin</info>") + + p_props_defined = p.branch is not None or p.custom_name is not None + p2_props_defined = p2.branch is not None or p2.custom_name is not None + p_is_lower_case = p.owner == p.owner.lower() and p.name == p.name.lower() + p2_is_lower_case = p2.owner == p2.owner.lower() and p2.name == p2.name.lower() + + # list of conditions for selecting p + select_p = p_props_defined and not p2_props_defined or p2_is_lower_case and not p_is_lower_case + # list of conditions for selecting p2 + select_p2 = p2_props_defined and not p_props_defined or p_is_lower_case and not p2_is_lower_case + + # one is more defined and is all lower, but the other is not all lower + # (we assume the not all lower case is the correct naming) + error_props_lower = ( + p_props_defined and p_is_lower_case and not p2_props_defined and not p2_is_lower_case + ) + error_props_lower2 = ( + p2_props_defined and p2_is_lower_case and not p_props_defined and not p_is_lower_case + ) + + # both props are defined + error_props = p_props_defined and p2_props_defined + + # the sources are different + error_source = p.repository_host != p2.repository_host + + if error_props_lower or error_props_lower2 or error_props or error_source: + self.line(" • <error>Cannot determine which is the correct plugin</error>") + self.line(f" - {p.line}") + self.line(f" - {p2.line}") + error = True + # remove second spec to not encounter the error twice + # this will not be written to the manifest.txt because we set + # the error flag and will exit after the loop + specs.remove(p2) + elif select_p: + self.line(f" - <comment>{p.line}</comment>") + self.line(f" - {p2.line}") + specs.remove(p2) + elif select_p2: + self.line(f" - {p.line}") + self.line(f" - <comment>{p2.line}</comment>") + specs.remove(p) + else: + self.line(" • <error>Logic error in correct spec determination</error>") + self.line(f" - {p.line}") + self.line(f" - {p2.line}") + error = True + # remove second spec to not encounter the error twice + # this will not be written to the manifest.txt because we set + # the error flag and will exit after the loop + specs.remove(p) + if error: + # exit after all errors have been found + exit(1) + + return specs diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/helpers.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/helpers.py new file mode 100644 index 00000000..8a28b0e8 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/helpers.py @@ -0,0 +1,61 @@ +from .spec import PluginSpec + +MANIFEST_FILE = "manifest.txt" +BLACKLIST_FILE = "blacklist.txt" +PKGS_FILE = "default.nix" +JSON_FILE = ".plugins.json" +PLUGINS_LIST_FILE = "plugins.md" + + +def get_const(const: str, plug_dir: str) -> str: + out = plug_dir + "/" + const + return out + + +def read_manifest(plug_dir: str) -> list[str]: + with open(get_const(MANIFEST_FILE, plug_dir), "r") as file: + specs = set([spec.strip() for spec in file.readlines()]) + + return sorted(specs) + + +def read_manifest_to_spec(plug_dir: str) -> list[PluginSpec]: + manifest = read_manifest(plug_dir) + specs = [PluginSpec.from_spec(spec.strip()) for spec in manifest] + + return sorted(specs) + + +def read_blacklist(plug_dir: str) -> list[str]: + with open(get_const(BLACKLIST_FILE, plug_dir), "r") as file: + if len(file.readlines()) == 0: + return [""] + else: + blacklisted_specs = set([spec.strip() for spec in file.readlines()]) + + return sorted(blacklisted_specs) + + +def read_blacklist_to_spec(plug_dir: str) -> list[PluginSpec]: + blacklist = read_blacklist(plug_dir) + specs = [PluginSpec.from_spec(spec.strip()) for spec in blacklist] + + return sorted(specs) + + +def write_manifest(specs: list[str] | set[str], plug_dir: str): + """write specs to manifest file. Does some cleaning up""" + + with open(get_const(MANIFEST_FILE, plug_dir), "w") as file: + specs = sorted(set(specs), key=lambda x: x.lower()) + specs = [p for p in specs] + + for s in specs: + file.write(f"{s}\n") + + +def write_manifest_from_spec(specs: list[PluginSpec], plug_dir: str): + """write specs to manifest file. Does some cleaning up""" + + strings = [f"{spec}" for spec in specs] + write_manifest(strings, plug_dir) diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/nix.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/nix.py new file mode 100644 index 00000000..66a8df4c --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/nix.py @@ -0,0 +1,121 @@ +import abc +import enum +import json +import subprocess + + +def nix_prefetch_url(url): + """Return the sha256 hash of the given url.""" + subprocess_output = subprocess.check_output( + ["nix-prefetch-url", "--type", "sha256", url], + stderr=subprocess.DEVNULL, + ) + sha256 = subprocess_output.decode("utf-8").strip() + return sha256 + + +def nix_prefetch_git(url): + """Return the sha256 hash of the given git url.""" + subprocess_output = subprocess.check_output(["nix-prefetch-git", url], stderr=subprocess.DEVNULL) + sha256 = json.loads(subprocess_output)["sha256"] + return sha256 + + +class Source(abc.ABC): + """Abstract base class for sources.""" + + url: str + sha256: str + + @abc.abstractmethod + def __init__(self, url: str) -> None: + """Initialize a Source.""" + + @abc.abstractmethod + def get_nix_expression(self): + """Return the nix expression for this source.""" + + def __repr__(self): + """Return the representation of this source.""" + return self.get_nix_expression() + + +class UrlSource(Source): + """A source that is a url.""" + + def __init__(self, url: str) -> None: + """Initialize a UrlSource.""" + self.url = url + self.sha256 = nix_prefetch_url(url) + + def get_nix_expression(self): + """Return the nix expression for this source.""" + return f'fetchurl {{ url = "{self.url}"; sha256 = "{self.sha256}"; }}' + + +class GitSource(Source): + """A source that is a git repository.""" + + def __init__(self, url: str, rev: str) -> None: + """Initialize a GitSource.""" + self.url = url + self.rev = rev + self.sha256 = nix_prefetch_git(url) + + def get_nix_expression(self): + """Return the nix expression for this source.""" + return f'fetchgit {{ url = "{self.url}"; rev = "{self.rev}"; sha256 = "{self.sha256}"; }}' + + +class License(enum.Enum): + """An enumeration of licenses.""" + + AGPL_3_0 = "agpl3Only" + APACHE_2_0 = "asf20" + BSD_2_CLAUSE = "bsd2" + BSD_3_CLAUSE = "bsd3" + BSL_1_0 = "bsl1_0" + CC0_1_0 = "cc0" + EPL_2_0 = "epl20" + GPL_2_0 = "gpl2Only" + GPL_3_0 = "gpl3Only" + ISCLGPL_2_1 = "lgpl21Only" + MIT = "mit" + MPL_2_0 = "mpl20" + UNLUNLICENSE = "unlicense" + WTFPL = "wtfpl" + UNFREE = "unfree" + UNKNOWN = "" + + @classmethod + def from_spdx_id(cls, spdx_id: str | None) -> "License": + """Return the License from the given spdx_id.""" + mapping = { + "AGPL-3.0": cls.AGPL_3_0, + "AGPL-3.0-only": cls.AGPL_3_0, + "Apache-2.0": cls.APACHE_2_0, + "BSD-2-Clause": cls.BSD_2_CLAUSE, + "BSD-3-Clause": cls.BSD_3_CLAUSE, + "BSL-1.0": cls.BSL_1_0, + "CC0-1.0": cls.CC0_1_0, + "EPL-2.0": cls.EPL_2_0, + "GPL-2.0": cls.GPL_2_0, + "GPL-2.0-only": cls.GPL_2_0, + "GPL-3.0": cls.GPL_3_0, + "GPL-3.0-only": cls.GPL_3_0, + "LGPL-2.1-only": cls.ISCLGPL_2_1, + "MIT": cls.MIT, + "MPL-2.0": cls.MPL_2_0, + "Unlicense": cls.UNLUNLICENSE, + "WTFPL": cls.WTFPL, + } + + if spdx_id is None: + return cls.UNKNOWN + + spdx_id = spdx_id.upper() + return mapping.get(spdx_id, cls.UNKNOWN) + + def __str__(self): + """Return the string representation of this license.""" + return self.value diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/plugin.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/plugin.py new file mode 100644 index 00000000..8334ad53 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/plugin.py @@ -0,0 +1,182 @@ +import logging +import os +import urllib + +import requests +import jsonpickle +from datetime import datetime, date +from dateparser import parse + +from .nix import GitSource, License, Source, UrlSource +from .spec import PluginSpec, RepositoryHost + + +logger = logging.getLogger(__name__) + + +class VimPlugin: + """Abstract base class for vim plugins.""" + + name: str + owner: str + repo: str + version: date + source: Source + description: str = "No description" + homepage: str + license: License + source_line: str + checked: date = datetime.now().date() + + def to_nix(self): + """Return the nix expression for this plugin.""" + meta = f'with lib; {{ description = "{self.description}"; homepage = "{self.homepage}"; license = with licenses; [ {self.license.value} ]; }}' + return f'/* Generated from: {self.source_line} */ {self.name} = buildVimPlugin {{ pname = "{self.name}"; version = "{self.version}"; src = {self.source.get_nix_expression()}; meta = {meta}; }};' + + def to_json(self): + """Serizalize the plugin to json""" + return jsonpickle.encode(self) + + def to_markdown(self): + link = f"[{self.source_line}]({self.homepage})" + version = f"{self.version}" + package_name = f"{self.name}" + checked = f"{self.checked}" + + return f"| {link} | {version} | `{package_name}` | {checked} |" + + def __lt__(self, o: object) -> bool: + if not isinstance(o, VimPlugin): + return False + + return self.name.lower() < o.name.lower() + + def __repr__(self): + """Return the representation of this plugin.""" + return f"VimPlugin({self.name!r}, {self.version.strftime('%Y-%m-%d')})" + + +def _get_github_token(): + token = os.environ.get("GITHUB_TOKEN") + if token is None: + # NOTE: This should never use more than the free api requests <2023-12-09> + pass + # logger.warning("GITHUB_TOKEN environment variable not set") + return token + + +class GitHubPlugin(VimPlugin): + def __init__(self, plugin_spec: PluginSpec) -> None: + """Initialize a GitHubPlugin.""" + + full_name = f"{plugin_spec.owner}/{plugin_spec.repo}" + repo_info = self._api_call(f"repos/{full_name}") + default_branch = plugin_spec.branch or repo_info["default_branch"] + api_callback = self._api_call(f"repos/{full_name}/commits/{default_branch}") + latest_commit = api_callback["commit"] + sha = api_callback["sha"] + + self.name = plugin_spec.name + self.owner = plugin_spec.owner + self.version = parse(latest_commit["committer"]["date"]).date() + self.source = UrlSource(f"https://github.com/{full_name}/archive/{sha}.tar.gz") + self.description = (repo_info.get("description") or "").replace('"', '\\"') + self.homepage = repo_info["html_url"] + self.license = plugin_spec.license or License.from_spdx_id((repo_info.get("license") or {}).get("spdx_id")) + self.source_line = plugin_spec.line + + def _api_call(self, path: str, token: str | None = _get_github_token()): + """Call the GitHub API.""" + url = f"https://api.github.com/{path}" + headers = {"Content-Type": "application/json"} + if token is not None: + headers["Authorization"] = f"token {token}" + response = requests.get(url, headers=headers) + if response.status_code != 200: + raise RuntimeError(f"GitHub API call failed: {response.text}") + return response.json() + + +class GitlabPlugin(VimPlugin): + def __init__(self, plugin_spec: PluginSpec) -> None: + """Initialize a GitlabPlugin.""" + + full_name = urllib.parse.quote(f"{plugin_spec.owner}/{plugin_spec.repo}", safe="") + repo_info = self._api_call(f"projects/{full_name}") + default_branch = plugin_spec.branch or repo_info["default_branch"] + api_callback = self._api_call(f"projects/{full_name}/repository/branches/{default_branch}") + latest_commit = api_callback["commit"] + sha = latest_commit["id"] + + self.name = plugin_spec.name + self.owner = plugin_spec.owner + self.version = parse(latest_commit["created_at"]).date() + self.source = UrlSource(f"https://gitlab.com/api/v4/projects/{full_name}/repository/archive.tar.gz?sha={sha}") + self.description = (repo_info.get("description") or "").replace('"', '\\"') + self.homepage = repo_info["web_url"] + self.license = plugin_spec.license or License.from_spdx_id(repo_info.get("license", {}).get("key")) + self.source_line = plugin_spec.line + + def _api_call(self, path: str) -> dict: + """Call the Gitlab API.""" + url = f"https://gitlab.com/api/v4/{path}" + response = requests.get(url) + if response.status_code != 200: + raise RuntimeError(f"Gitlab API call failed: {response.text}") + return response.json() + + +def _get_sourcehut_token(): + token = os.environ.get("SOURCEHUT_TOKEN") + if token is None: + # NOTE: This should never use more than the free requests <2023-12-09> + pass + # logger.warning("SOURCEHUT_TOKEN environment variable not set") + return token + + +class SourceHutPlugin(VimPlugin): + def __init__(self, plugin_spec: PluginSpec) -> None: + """Initialize a SourceHutPlugin.""" + + repo_info = self._api_call(f"~{plugin_spec.owner}/repos/{plugin_spec.repo}") + if plugin_spec.branch is None: + commits = self._api_call(f"~{plugin_spec.owner}/repos/{plugin_spec.repo}/log") + else: + commits = self._api_call(f"~{plugin_spec.owner}/repos/{plugin_spec.repo}/log/{plugin_spec.branch}") + latest_commit = commits["results"][0] + sha = latest_commit["id"] + + self.name = plugin_spec.name + self.owner = plugin_spec.owner + self.version = parse(latest_commit["timestamp"]).date() + self.description = (repo_info.get("description") or "").replace('"', '\\"') + self.homepage = f"https://git.sr.ht/~{plugin_spec.owner}/{plugin_spec.repo}" + self.source = GitSource(self.homepage, sha) + self.license = plugin_spec.license or License.UNKNOWN # cannot be determined via API + self.source_line = plugin_spec.line + + def _api_call(self, path: str, token: str | None = _get_sourcehut_token()): + """Call the SourceHut API.""" + + url = f"https://git.sr.ht/api/{path}" + headers = {"Content-Type": "application/json"} + if token is not None: + headers["Authorization"] = f"token {token}" + response = requests.get(url, headers=headers) + if response.status_code != 200: + raise RuntimeError(f"SourceHut API call failed: {response.json()}") + return response.json() + + +def plugin_from_spec(plugin_spec: PluginSpec) -> VimPlugin: + """Initialize a VimPlugin.""" + + if plugin_spec.repository_host == RepositoryHost.GITHUB: + return GitHubPlugin(plugin_spec) + elif plugin_spec.repository_host == RepositoryHost.GITLAB: + return GitlabPlugin(plugin_spec) + elif plugin_spec.repository_host == RepositoryHost.SOURCEHUT: + return SourceHutPlugin(plugin_spec) + else: + raise NotImplementedError(f"Unsupported source: {plugin_spec.repository_host}") diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/spec.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/spec.py new file mode 100644 index 00000000..0f2fb29c --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/spec.py @@ -0,0 +1,143 @@ +import enum +import re + +from .nix import License + + +class RepositoryHost(enum.Enum): + """A repository host.""" + + GITHUB = "github" + GITLAB = "gitlab" + SOURCEHUT = "sourcehut" + + +class PluginSpec: + """A Vim plugin Spec.""" + + @classmethod + def from_spec(cls, spec): + """The spec line must be in the format: + [<repository_host>:]<owner>/<repo>[:<branch>][:name]. + + repository_host is one of github (default), gitlab, or sourcehut. + owner is the repository owner. + repo is the repository name. + branch is the git branch. + name is the name to use for the plugin (default is value of repo). + """ + repository_host = RepositoryHost.GITHUB + # gitref = "master" + + repository_host_regex = r"((?P<repository_host>[^:]+):)" + owner_regex = r"(?P<owner>[^/:]+)" + repo_regex = r"(?P<repo>[^:]+)" + branch_regex = r"(:(?P<branch>[^:]+)?)" + name_regex = r"(:(?P<name>[^:]+)?)" + license_regex = r"(:(?P<license>[^:]+)?)" + marked_duplicate_regex = r"(:(?P<duplicate>duplicate))" + + spec_regex = re.compile( + f"^{repository_host_regex}?{owner_regex}/{repo_regex}{branch_regex}?{name_regex}?{license_regex}?{marked_duplicate_regex}?$", + ) + + match = spec_regex.match(spec) + if match is None: + raise ValueError(f"Invalid spec: {spec}") + + group_dict = match.groupdict() + + repository_host = RepositoryHost(group_dict.get("repository_host") or "github") + + owner = group_dict.get("owner") + if owner is None: + raise RuntimeError("Could not get owner") + + repo = group_dict.get("repo") + if repo is None: + raise RuntimeError("Could not get repo") + + branch = group_dict.get("branch") + name = group_dict.get("name") + license = group_dict.get("license") + marked_duplicate = bool(group_dict.get("duplicate")) # True if 'duplicate', False if None + + line = spec + + return cls(repository_host, owner, repo, line, branch, name, license, marked_duplicate) + + def __init__( + self, + repository_host: RepositoryHost, + owner: str, + repo: str, + line: str, + branch: str | None = None, + name: str | None = None, + license: str | None = None, + marked_duplicate: bool = False, + ) -> None: + """Initialize a VimPluginSpec.""" + self.repository_host = repository_host + self.owner = owner + self.repo = repo + self.branch = branch + self.custom_name = name + self.name = name or repo.replace(".", "-").replace("_", "-") + self.license = License(license) if license else None + self.line = line + self.marked_duplicate = marked_duplicate + + def __str__(self) -> str: + """Return a string representation of a VimPluginSpec.""" + spec = "" + + if self.repository_host != RepositoryHost.GITHUB: + spec += f"{self.repository_host.value}:" + + spec += f"{self.owner}/{self.repo}" + + spec += ":" + if self.branch is not None: + spec += self.branch + + spec += ":" + if self.custom_name is not None: + spec += self.custom_name + + spec += ":" + if self.license is not None: + spec += str(self.license) + + spec += ":" + if self.marked_duplicate: + spec += "duplicate" + + return spec.rstrip(":") + + def __repr__(self): + """Return the representation of the specs""" + return f"PluginSpec({self.owner}/{self.repo}, {self.name})" + + def to_spec(self): + """Return a spec line for a VimPluginSpec.""" + return str(self) + + def __lt__(self, o: object) -> bool: + if not isinstance(o, PluginSpec): + return False + + return self.name.lower() < o.name.lower() + + def __eq__(self, o: object) -> bool: + """Return True if the two specs are equal.""" + if not isinstance(o, PluginSpec): + return False + + return ( + self.repository_host == o.repository_host + and self.owner == o.owner + and self.repo == o.repo + and self.branch == o.branch + and self.name == o.name + ) diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/__init__.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/__init__.py diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/fixtures.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/fixtures.py new file mode 100644 index 00000000..75dd251a --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/fixtures.py @@ -0,0 +1,44 @@ +import json + +import pytest +from pytest_mock import MockerFixture + +from update_vim_plugins.nix import GitSource, UrlSource + + +@pytest.fixture() +def url(): + return "https://example.com" + + +@pytest.fixture() +def rev(): + return "1234567890abcdef" + + +@pytest.fixture() +def sha256(): + return "sha256-1234567890abcdef" + + +@pytest.fixture() +def url_source(mocker: MockerFixture, url: str, sha256: str): + mocker.patch("subprocess.check_output", return_value=bytes(sha256, "utf-8")) + return UrlSource(url) + + +@pytest.fixture() +def git_source(mocker: MockerFixture, url: str, rev: str, sha256: str): + return_value = { + "url": url, + "rev": rev, + "date": "1970-01-01T00:00:00+00:00", + "path": "", + "sha256": sha256, + "fetchLFS": False, + "fetchSubmodules": False, + "deepClone": False, + "leaveDotGit": False, + } + mocker.patch("subprocess.check_output", return_value=json.dumps(return_value).encode("utf-8")) + return GitSource(url, rev) diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_nix.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_nix.py new file mode 100644 index 00000000..46e59f76 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_nix.py @@ -0,0 +1,32 @@ +from update_vim_plugins.nix import GitSource, License, UrlSource + + +def test_url_source(url_source: UrlSource, url: str, sha256: str): + assert url_source.url == url + assert url_source.sha256 == sha256 + + +def test_url_source_nix_expression(url_source: UrlSource, url: str, sha256: str): + assert url_source.get_nix_expression() == f'fetchurl {{ url = "{url}"; sha256 = "{sha256}"; }}' + + +def test_git_source(git_source: GitSource, url: str, rev: str, sha256: str): + assert git_source.url == url + assert git_source.sha256 == sha256 + assert git_source.rev == rev + + +def test_git_source_nix_expression(git_source: GitSource, url: str, rev: str, sha256: str): + assert git_source.get_nix_expression() == f'fetchgit {{ url = "{url}"; rev = "{rev}"; sha256 = "{sha256}"; }}' + + +def test_license_github(): + github_license = "MIT" + license = License.from_spdx_id(github_license) + assert license == License.MIT + + +def test_license_gitlab(): + gitlab_license = "mit" + license = License.from_spdx_id(gitlab_license) + assert license == License.MIT diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_plugin.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_plugin.py new file mode 100644 index 00000000..32377e24 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_plugin.py @@ -0,0 +1,144 @@ +import json +from typing import Callable + +import pytest +from pytest_mock import MockFixture + +from update_vim_plugins.nix import License, UrlSource +from update_vim_plugins.plugin import GitHubPlugin, VimPlugin +from update_vim_plugins.spec import PluginSpec + + +@pytest.fixture() +def mock_source(sha256: str): + class MockSource: + def __init__(self, *args, **kwargs): + pass + + def get_nix_expression(self): + return "src" + + return MockSource() + + +@pytest.fixture() +def mock_plugin(mock_source): + class MockVimPlugin(VimPlugin): + def __init__(self): + self.name = "test" + self.version = "1.0.0" + self.source = mock_source + self.description = "No description" + self.homepage = "https://example.com" + self.license = License.UNKNOWN + + return MockVimPlugin() + + +def test_vim_plugin_nix_expression(mock_plugin): + assert ( + mock_plugin.get_nix_expression() + == 'test = buildVimPluginFrom2Nix { pname = "test"; version = "1.0.0"; src = src; meta = with lib; { description = "No description"; homepage = "https://example.com"; license = with licenses; [ ]; }; };' + ) + + +class MockResponse: + def __init__(self, status_code: int, content: bytes): + self.status_code = status_code + self.content = content + + def json(self): + return json.loads(self.content) + + +def mock_request_get(repsonses: dict[str, MockResponse]): + respones_not_found = MockResponse(404, b'{"message": "Not Found"}') + + def mock_get(url: str, *args, **kwargs): + return repsonses.get(url, respones_not_found) + + return mock_get + + +@pytest.fixture() +def github_commits_response(): + return MockResponse( + 200, + json.dumps( + { + "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", + "commit": { + "committer": { + "date": "2011-04-14T16:00:49Z", + }, + }, + } + ), + ) + + +@pytest.fixture() +def github_get(github_commits_response: MockResponse): + repos_response = MockResponse( + 200, + json.dumps( + { + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": False, + "default_branch": "master", + "license": { + "spdx_id": "MIT", + }, + } + ), + ) + responses = { + "https://api.github.com/repos/octocat/Hello-World": repos_response, + "https://api.github.com/repos/octocat/Hello-World/commits/master": github_commits_response, + } + return mock_request_get(responses) + + +@pytest.fixture() +def github_get_no_license(github_commits_response: MockResponse): + repos_response = MockResponse( + 200, + json.dumps( + { + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": False, + "default_branch": "master", + } + ), + ) + responses = { + "https://api.github.com/repos/octocat/Hello-World": repos_response, + "https://api.github.com/repos/octocat/Hello-World/commits/master": github_commits_response, + } + return mock_request_get(responses) + + +def test_github_plugin(mocker: MockFixture, github_get: Callable, url_source: UrlSource): + mocker.patch("requests.get", github_get) + url_source = mocker.patch("update_vim_plugins.nix.UrlSource", url_source) + + spec = PluginSpec.from_spec("octocat/Hello-World") + plugin = GitHubPlugin(spec) + + assert plugin.name == "Hello-World" + assert plugin.version == "2011-04-14" + assert plugin.description == "This your first repo!" + assert plugin.homepage == "https://github.com/octocat/Hello-World" + assert plugin.license == License.MIT + + +def test_github_plugin_no_license(mocker: MockFixture, github_get_no_license: Callable, url_source: UrlSource): + mocker.patch("requests.get", github_get_no_license) + url_source = mocker.patch("update_vim_plugins.nix.UrlSource", url_source) + + spec = PluginSpec.from_spec("octocat/Hello-World") + plugin = GitHubPlugin(spec) + + assert plugin.license == License.UNKNOWN diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_spec.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_spec.py new file mode 100644 index 00000000..2b9a1d24 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/tests/test_spec.py @@ -0,0 +1,136 @@ +import pytest + +from update_vim_plugins.spec import PluginSpec, RepositoryHost + + +@pytest.fixture() +def owner(): + return "owner" + + +@pytest.fixture() +def repo(): + return "repo.nvim" + + +@pytest.fixture() +def branch(): + return "main" + + +@pytest.fixture() +def name(): + return "repo-nvim" + + +@pytest.fixture() +def license(): + return "mit" + + +def test_from_spec_simple(owner: str, repo: str): + vim_plugin = PluginSpec.from_spec(f"{owner}/{repo}") + + assert vim_plugin.owner == owner + assert vim_plugin.repo == repo + + +def test_from_spec_with_gitref(owner: str, repo: str, branch: str): + vim_plugin = PluginSpec.from_spec(f"{owner}/{repo}:{branch}") + + assert vim_plugin.branch == branch + + +def test_from_spec_with_name(owner: str, repo: str, name: str): + vim_plugin = PluginSpec.from_spec(f"{owner}/{repo}::{name}") + + assert vim_plugin.name == name + + +@pytest.mark.parametrize("host", RepositoryHost) +def test_from_spec_with_repository_host(owner: str, repo: str, host: RepositoryHost): + vim_plugin = PluginSpec.from_spec(f"{host.value}:{owner}/{repo}") + + assert vim_plugin.repository_host == host + + +def test_from_spec_without_repository_host(owner: str, repo: str): + vim_plugin = PluginSpec.from_spec(f"{owner}/{repo}") + + assert vim_plugin.repository_host == RepositoryHost.GITHUB + + +def test_from_spec_complex(owner: str, repo: str, branch: str, name: str): + vim_plugin = PluginSpec.from_spec(f"gitlab:{owner}/{repo}:{branch}:{name}") + + assert vim_plugin.repository_host == RepositoryHost.GITLAB + assert vim_plugin.owner == owner + assert vim_plugin.repo == repo + assert vim_plugin.branch == branch + assert vim_plugin.name == name + + +def test_from_spec_invalid_spec(): + with pytest.raises(ValueError): + PluginSpec.from_spec("invalid_spec") + + +def test_to_spec_simple(owner: str, repo: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo) + + assert vim_plugin.to_spec() == f"{owner}/{repo}" + + +def test_to_spec_with_branch(owner: str, repo: str, branch: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo, branch=branch) + assert vim_plugin.to_spec() == f"{owner}/{repo}:{branch}" + + +def test_to_spec_with_name(owner: str, repo: str, name: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo, name=name) + + assert vim_plugin.to_spec() == f"{owner}/{repo}::{name}" + + +@pytest.mark.parametrize("host", [RepositoryHost.GITLAB, RepositoryHost.SOURCEHUT]) +def test_to_spec_with_repository_host(host: RepositoryHost, owner: str, repo: str): + vim_plugin = PluginSpec(host, owner, repo) + + assert vim_plugin.to_spec() == f"{host.value}:{owner}/{repo}" + + +def test_to_spec_complex(owner: str, repo: str, branch: str, name: str, license: str): + vim_plugin = PluginSpec(RepositoryHost.GITLAB, owner, repo, branch=branch, name=name, license=license) + + assert vim_plugin.to_spec() == f"gitlab:{owner}/{repo}:{branch}:{name}:{license}" + + +def test_spec_equal(owner: str, repo: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo) + vim_plugin2 = PluginSpec(RepositoryHost.GITHUB, owner, repo) + + assert vim_plugin == vim_plugin2 + + +def test_spec_not_equal_different_branch(owner: str, repo: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo) + vim_plugin2 = PluginSpec(RepositoryHost.GITHUB, owner, repo, branch="main") + + assert vim_plugin != vim_plugin2 + + +def test_spec_not_equal_different_name(owner: str, repo: str): + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo) + vim_plugin2 = PluginSpec(RepositoryHost.GITHUB, owner, repo, name="renamed") + + assert vim_plugin != vim_plugin2 + + +def test_spec_equal_same_normalized_name(owner: str): + repo = "repo.nvim" + name = "repo-nvim" + + vim_plugin = PluginSpec(RepositoryHost.GITHUB, owner, repo) + vim_plugin2 = PluginSpec(RepositoryHost.GITHUB, owner, repo, name=name) + + assert vim_plugin == vim_plugin2 diff --git a/pkgs/sources/update_vim_plugins/update_vim_plugins/update.py b/pkgs/sources/update_vim_plugins/update_vim_plugins/update.py new file mode 100644 index 00000000..7eb3eeb4 --- /dev/null +++ b/pkgs/sources/update_vim_plugins/update_vim_plugins/update.py @@ -0,0 +1,212 @@ +import subprocess +from random import shuffle +from cleo.helpers import argument, option +from cleo.commands.command import Command +from concurrent.futures import ThreadPoolExecutor, as_completed + +from pprint import pprint + +from .plugin import plugin_from_spec + +from .helpers import read_manifest_to_spec, get_const +from .helpers import JSON_FILE, PLUGINS_LIST_FILE, PKGS_FILE + +import json +import jsonpickle + +jsonpickle.set_encoder_options("json", sort_keys=True) + + +class UpdateCommand(Command): + name = "update" + description = "Generate nix module from input file" + arguments = [argument("plug_dir", description="Path to the plugin directory", optional=False)] + options = [ + option("all", "a", description="Update all plugins. Else only update new plugins", flag=True), + option("dry-run", "d", description="Show which plugins would be updated", flag=True), + ] + + def handle(self): + """Main command function""" + + plug_dir = self.argument("plug_dir") + self.specs = read_manifest_to_spec(plug_dir) + + if self.option("all"): + # update all plugins + spec_list = self.specs + known_plugins = [] + else: + # filter plugins we already know + spec_list = self.specs + + with open(get_const(JSON_FILE, plug_dir), "r") as json_file: + data = json.load(json_file) + + known_specs = list(filter(lambda x: x.line in data, spec_list)) + known_plugins = [jsonpickle.decode(data[x.line]) for x in known_specs] + + spec_list = list(filter(lambda x: x.line not in data, spec_list)) + + if self.option("dry-run"): + self.line("<comment>These plugins would be updated</comment>") + pprint(spec_list) + self.line(f"<info>Total:</info> {len(spec_list)}") + exit(0) + + processed_plugins, failed_plugins, failed_but_known = self.process_manifest(spec_list, plug_dir) + + processed_plugins += known_plugins # add plugins from .plugins.json + processed_plugins: list = sorted(set(processed_plugins)) # remove duplicates based only on source line + + self.check_duplicates(processed_plugins) + + if failed_plugins != []: + self.line("<error>Not processed:</error> The following plugins could not be updated") + for s, e in failed_plugins: + self.line(f" - {s!r} - {e}") + + if failed_but_known != []: + self.line( + "<error>Not updated:</error> The following plugins could not be updated but an older version is known" + ) + for s, e in failed_but_known: + self.line(f" - {s!r} - {e}") + + # update plugin "database" + self.write_plugins_json(processed_plugins, plug_dir) + + # generate output + self.write_plugins_nix(processed_plugins, plug_dir) + + self.write_plugins_markdown(processed_plugins, plug_dir) + + self.line("<comment>Done</comment>") + + def write_plugins_markdown(self, plugins, plug_dir): + """Write the list of all plugins to PLUGINS_LIST_FILE in markdown""" + + plugins.sort() + + self.line("<info>Updating plugins.md</info>") + + header = f" - Plugin count: {len(plugins)}\n\n| Repo | Last Update | Nix package name | Last checked |\n|:---|:---|:---|:---|\n" + + with open(get_const(PLUGINS_LIST_FILE, plug_dir), "w") as file: + file.write(header) + for plugin in plugins: + file.write(f"{plugin.to_markdown()}\n") + + def write_plugins_nix(self, plugins, plug_dir): + self.line("<info>Generating nix output</info>") + + plugins.sort() + + header = "{ lib, buildVimPlugin, fetchurl, fetchgit }: {" + footer = "}" + + with open(get_const(PKGS_FILE, plug_dir), "w") as file: + file.write(header) + for plugin in plugins: + file.write(f"{plugin.to_nix()}\n") + file.write(footer) + + self.line("<info>Formatting nix output</info>") + + subprocess.run( + ["alejandra", get_const(PKGS_FILE, plug_dir)], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + def write_plugins_json(self, plugins, plug_dir): + self.line("<info>Storing results in .plugins.json</info>") + + plugins.sort() + + with open(get_const(JSON_FILE, plug_dir), "r+") as json_file: + data = json.load(json_file) + + for plugin in plugins: + data.update({f"{plugin.source_line}": plugin.to_json()}) + + json_file.seek(0) + json_file.write(json.dumps(data, indent=2, sort_keys=True)) + json_file.truncate() + + def check_duplicates(self, plugins): + """check for duplicates in proccesed_plugins""" + error = False + for i, plugin in enumerate(plugins): + for p in plugins[i + 1 :]: + if plugin.name == p.name: + self.line( + f"<error>Error:</error> The following two lines produce the same plugin name:\n - {plugin.source_line}\n - {p.source_line}\n -> {p.name}" + ) + error = True + + # We want to exit if the resulting nix file would be broken + # But we want to go through all plugins before we do so + if error: + exit(1) + + def generate_plugin(self, spec, i, size, plug_dir): + debug_string = "" + + processed_plugin = None + failed_but_known = None + failed_plugin = None + try: + debug_string += f" - <info>({i+1}/{size}) Processing</info> {spec!r}\n" + vim_plugin = plugin_from_spec(spec) + debug_string += f" • <comment>Success</comment> {vim_plugin!r}\n" + processed_plugin = vim_plugin + except Exception as e: + debug_string += f" • <error>Error:</error> Could not update <info>{spec.name}</info>. Keeping old values. Reason: {e}\n" + with open(get_const(JSON_FILE, plug_dir), "r") as json_file: + data = json.load(json_file) + + plugin_json = data.get(spec.line) + if plugin_json: + vim_plugin = jsonpickle.decode(plugin_json) + processed_plugin = vim_plugin + failed_but_known = (vim_plugin, e) + else: + debug_string += f" • <error>Error:</error> No entries for <info>{spec.name}</info> in '.plugins.json'. Skipping...\n" + failed_plugin = (spec, e) + + self.line(debug_string.strip()) + + return processed_plugin, failed_plugin, failed_but_known + + def process_manifest(self, spec_list, plug_dir): + """Read specs in 'spec_list' and generate plugins""" + + size = len(spec_list) + + # We have to assume that we will reach an api limit. Therefore + # we randomize the spec list to give every entry the same change to be updated and + # not favor those at the start of the list + shuffle(spec_list) + + with ThreadPoolExecutor() as executor: + futures = [ + executor.submit(self.generate_plugin, spec, i, size, plug_dir) for i, spec in enumerate(spec_list) + ] + results = [future.result() for future in as_completed(futures)] + + processed_plugins = [r[0] for r in results] + failed_plugins = [r[1] for r in results] + failed_but_known = [r[2] for r in results] + + processed_plugins = list(filter(lambda x: x is not None, processed_plugins)) + failed_plugins = list(filter(lambda x: x is not None, failed_plugins)) + failed_but_known = list(filter(lambda x: x is not None, failed_but_known)) + + processed_plugins.sort() + failed_plugins.sort() + failed_but_known.sort() + + assert len(processed_plugins) == len(spec_list) - len(failed_plugins) + + return processed_plugins, failed_plugins, failed_but_known diff --git a/pkgs/sources/yt/.env b/pkgs/sources/yt/.env new file mode 100755 index 00000000..8018a738 --- /dev/null +++ b/pkgs/sources/yt/.env @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +PATH="$(pwd)/target/release/:$(pwd)/target/debug/:$PATH" diff --git a/pkgs/sources/yt/.envrc b/pkgs/sources/yt/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/pkgs/sources/yt/.envrc @@ -0,0 +1 @@ +use flake diff --git a/pkgs/sources/yt/.gitignore b/pkgs/sources/yt/.gitignore new file mode 100644 index 00000000..c84fa049 --- /dev/null +++ b/pkgs/sources/yt/.gitignore @@ -0,0 +1,3 @@ +# build dirs +/target +/result diff --git a/pkgs/sources/yt/Cargo.lock b/pkgs/sources/yt/Cargo.lock new file mode 100644 index 00000000..ef2a53fd --- /dev/null +++ b/pkgs/sources/yt/Cargo.lock @@ -0,0 +1,640 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[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.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "cli-log" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d2ab00dc4c82ec28af25ac085aecc11ffeabf353755715a3113a7aa044ca5cc" +dependencies = [ + "chrono", + "file-size", + "log", + "proc-status", +] + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "file-size" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9544f10105d33957765016b8a9baea7e689bf1f0f2f32c2fa2f568770c38d2b3" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-status" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e0c0ac915e7b76b47850ba4ffc377abde6c6ff9eeace61d0a89623db449712" +dependencies = [ + "thiserror", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.201" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "yt" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "cli-log", + "log", + "serde", + "serde_json", + "tempfile", + "url", +] diff --git a/pkgs/sources/yt/Cargo.toml b/pkgs/sources/yt/Cargo.toml new file mode 100644 index 00000000..7c17d20b --- /dev/null +++ b/pkgs/sources/yt/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "yt" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.83" +clap = { version = "4.5.4", features = ["derive"] } +cli-log = "2.0.0" +log = "0.4.21" +serde = { version = "1.0.201", features = ["derive"] } +serde_json = "1.0.117" +tempfile = "3.10.1" +url = "2.5.0" + +# This is here to be able to tell nix which binary to build +[features] +yts = [] +ytc = [] +yt = [] +default = ["yt", "yts", "ytc"] + +[[bin]] +name = "yts" +required-features = ["yts"] + +[[bin]] +name = "ytc" +required-features = ["ytc"] + +[[bin]] +name = "yt" +required-features = ["yt"] diff --git a/pkgs/sources/yt/default.nix b/pkgs/sources/yt/default.nix new file mode 100644 index 00000000..32396051 --- /dev/null +++ b/pkgs/sources/yt/default.nix @@ -0,0 +1,51 @@ +[ + ( + final: prev: { + yt = import ./yt.nix { + inherit + (prev) + lib + makeWrapper + rustPlatform + # dependencies + + ytcc + yt-dlp + mpv + ; + }; + } + ) + ( + final: prev: { + yts = import ./yts.nix { + inherit + (prev) + lib + makeWrapper + rustPlatform + # dependencies + + ytcc + ; + }; + } + ) + ( + final: prev: { + ytc = import ./ytc.nix { + inherit + (prev) + lib + makeWrapper + rustPlatform + # dependencies + + ytcc + yt-dlp + mpv + ; + }; + } + ) +] diff --git a/pkgs/sources/yt/flake.lock b/pkgs/sources/yt/flake.lock new file mode 100644 index 00000000..50494465 --- /dev/null +++ b/pkgs/sources/yt/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1715087517, + "narHash": "sha256-CLU5Tsg24Ke4+7sH8azHWXKd0CFd4mhLWfhYgUiDBpQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b211b392b8486ee79df6cdfb1157ad2133427a29", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/pkgs/sources/yt/flake.nix b/pkgs/sources/yt/flake.nix new file mode 100644 index 00000000..561b1c0d --- /dev/null +++ b/pkgs/sources/yt/flake.nix @@ -0,0 +1,30 @@ +{ + description = "yt"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + }: (flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages."${system}"; + in { + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + # rust stuff + cargo + clippy + rustc + rustfmt + + cargo-edit + cargo-expand + ]; + }; + })); +} diff --git a/pkgs/sources/yt/src/bin/yt/main.rs b/pkgs/sources/yt/src/bin/yt/main.rs new file mode 100644 index 00000000..37348834 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yt/main.rs @@ -0,0 +1,91 @@ +use anyhow::{bail, Context, Result}; +use std::{ + env, fs, + io::{BufRead, BufReader, BufWriter, Write}, + process::Command as StdCmd, +}; +use tempfile::Builder; +use yt::{ + constants::{last_select, HELP_STR}, + downloader::Downloader, + filter_line, YtccListData, +}; + +fn main() -> Result<()> { + cli_log::init_cli_log!(); + + let json_map = { + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args([ + "--output", + "json", + "list", + "--order-by", + "publish_date", + "desc", + ]); + + serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to json from ytcc")?.stdout, + ) + .context("Failed to deserialize json output")? + }; + + let temp_file = Builder::new() + .prefix("yt_video_select-") + .suffix(".yts") + .rand_bytes(6) + .tempfile() + .context("Failed to get tempfile")?; + + { + let mut edit_file = BufWriter::new(&temp_file); + + json_map.iter().for_each(|line| { + let line = line.to_string(); + edit_file + .write_all(line.as_bytes()) + .expect("This write should not fail"); + }); + + edit_file.write_all(HELP_STR.as_bytes())?; + edit_file.flush().context("Failed to flush edit file")?; + + let mut nvim = StdCmd::new("nvim"); + nvim.arg(temp_file.path()); + let status = nvim.status().context("Falied to run nvim")?; + if !status.success() { + bail!("nvim exited with error status: {}", status) + } + } + + let read_file = temp_file.reopen()?; + fs::copy( + temp_file.path(), + last_select().context("Failed to get the persistent selection file path")?, + ) + .context("Failed to persist selection file")?; + + let mut watching = Vec::new(); + let reader = BufReader::new(&read_file); + for line in reader.lines() { + let line = line.context("Failed to read line")?; + + if let Some(downloadable) = + filter_line(&line).with_context(|| format!("Failed to process line: '{}'", line))? + { + watching.push(downloadable); + } + } + + if watching.is_empty() { + return Ok(()); + } + + let downloader = Downloader::new(watching).context("Failed to construct downloader")?; + downloader + .consume() + .context("Failed to consume downloader")?; + + Ok(()) +} diff --git a/pkgs/sources/yt/src/bin/ytc/args.rs b/pkgs/sources/yt/src/bin/ytc/args.rs new file mode 100644 index 00000000..8b2d6a61 --- /dev/null +++ b/pkgs/sources/yt/src/bin/ytc/args.rs @@ -0,0 +1,26 @@ +use clap::{Parser, Subcommand}; +/// A helper for downloading and playing youtube videos +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + #[command(subcommand)] + /// The subcommand to execute + pub subcommand: Command, +} +#[derive(Subcommand, Debug)] +pub enum Command { + #[clap(value_parser)] + /// Work based of ytcc ids + Id { + #[clap(value_parser)] + /// A list of ids to play + ids: Vec<u32>, + }, + #[clap(value_parser)] + /// Work based of raw youtube urls + Url { + #[clap(value_parser)] + /// A list of urls to play + urls: Vec<String>, + }, +} diff --git a/pkgs/sources/yt/src/bin/ytc/main.rs b/pkgs/sources/yt/src/bin/ytc/main.rs new file mode 100644 index 00000000..b38157df --- /dev/null +++ b/pkgs/sources/yt/src/bin/ytc/main.rs @@ -0,0 +1,77 @@ +use std::{env, process::Command as StdCmd}; + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use log::debug; +use url::Url; +use yt::{ + downloader::{Downloadable, Downloader}, + YtccListData, +}; + +use crate::args::{Args, Command}; + +mod args; + +fn main() -> Result<()> { + let args = Args::parse(); + cli_log::init_cli_log!(); + + let playspec: Vec<Downloadable> = match args.subcommand { + Command::Id { ids } => { + let mut output = Vec::with_capacity(ids.len()); + for id in ids { + debug!("Adding {}", id); + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args([ + "--output", + "json", + "list", + "--watched", + "--unwatched", + "--attributes", + "url", + "--ids", + id.to_string().as_str(), + ]); + let json = serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to get url from id")?.stdout, + ) + .context("Failed to deserialize json output")?; + + if json.is_empty() { + bail!("Could not find a video with id: {}", id); + } + assert_eq!(json.len(), 1); + let json = json.first().expect("Has only one element"); + + debug!("Id resolved to: '{}'", &json.url); + + output.push(Downloadable { + url: Url::parse(&json.url)?, + id: Some(json.id), + }) + } + output + } + Command::Url { urls } => { + let mut output = Vec::with_capacity(urls.len()); + for url in urls { + output.push(Downloadable { + url: Url::parse(&url).context("Failed to parse url")?, + id: None, + }) + } + output + } + }; + + debug!("Initializing downloader"); + let downloader = Downloader::new(playspec)?; + + downloader + .consume() + .context("Failed to consume downloader")?; + + Ok(()) +} diff --git a/pkgs/sources/yt/src/bin/yts/args.rs b/pkgs/sources/yt/src/bin/yts/args.rs new file mode 100644 index 00000000..56989421 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yts/args.rs @@ -0,0 +1,41 @@ +use clap::{Parser, Subcommand}; +/// A helper for selecting which videos to download from ytcc to ytc +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +pub struct Args { + #[command(subcommand)] + /// subcommand to execute + pub subcommand: Option<Command>, +} + +#[derive(Subcommand, Debug)] +pub enum Command { + #[clap(value_parser)] + /// Which ordering to use + Order { + #[command(subcommand)] + command: OrderCommand, + }, +} + +#[derive(Subcommand, Debug)] +pub enum OrderCommand { + #[clap(value_parser)] + /// Order by date + #[group(required = true)] + Date { + #[arg(value_parser)] + /// Order descending + desc: bool, + #[clap(value_parser)] + /// Order ascending + asc: bool, + }, + #[clap(value_parser)] + /// Pass a raw SQL 'ORDER BY' value + Raw { + #[clap(value_parser)] + /// The raw value(s) to pass + value: Vec<String>, + }, +} diff --git a/pkgs/sources/yt/src/bin/yts/main.rs b/pkgs/sources/yt/src/bin/yts/main.rs new file mode 100644 index 00000000..7398db61 --- /dev/null +++ b/pkgs/sources/yt/src/bin/yts/main.rs @@ -0,0 +1,91 @@ +use anyhow::{bail, Context, Result}; +use clap::Parser; +use std::{ + env, + io::{BufRead, BufReader, Write}, + process::Command as StdCmd, +}; +use tempfile::NamedTempFile; +use yt::{constants::HELP_STR, filter_line, YtccListData}; + +use crate::args::{Args, Command, OrderCommand}; + +mod args; + +fn main() -> Result<()> { + let args = Args::parse(); + cli_log::init_cli_log!(); + + let ordering = match args.subcommand.unwrap_or(Command::Order { + command: OrderCommand::Date { + desc: true, + asc: false, + }, + }) { + Command::Order { command } => match command { + OrderCommand::Date { desc, asc } => { + if desc { + vec!["--order-by".into(), "publish_date".into(), "desc".into()] + } else if asc { + vec!["--order-by".into(), "publish_date".into(), "asc".into()] + } else { + vec!["--order-by".into(), "publish_date".into(), "desc".into()] + } + } + OrderCommand::Raw { value } => [vec!["--order-by".into()], value].concat(), + }, + }; + + let json_map = { + let mut ytcc = StdCmd::new("ytcc"); + ytcc.args(["--output", "json", "list"]); + ytcc.args(ordering); + + serde_json::from_slice::<Vec<YtccListData>>( + &ytcc.output().context("Failed to json from ytcc")?.stdout, + ) + .context("Failed to deserialize json output")? + }; + + let mut edit_file = NamedTempFile::new().context("Failed to get tempfile")?; + + json_map.iter().for_each(|line| { + let line = line.to_string(); + edit_file + .write_all(line.as_bytes()) + .expect("This write should not fail"); + }); + + write!(&edit_file, "{}", HELP_STR)?; + edit_file.flush().context("Failed to flush edit file")?; + + let read_file = edit_file.reopen()?; + + let mut nvim = StdCmd::new("nvim"); + nvim.arg(edit_file.path()); + + let status = nvim.status().context("Falied to run nvim")?; + if !status.success() { + bail!("Nvim exited with error status: {}", status) + } + + let mut watching = Vec::new(); + let reader = BufReader::new(&read_file); + for line in reader.lines() { + let line = line.context("Failed to read line")?; + + if let Some(downloadable) = + filter_line(&line).with_context(|| format!("Failed to process line: '{}'", line))? + { + watching.push(downloadable); + } + } + + let watching: String = watching + .iter() + .map(|d| d.to_string()) + .collect::<Vec<String>>() + .join("\n"); + println!("{}", &watching); + Ok(()) +} diff --git a/pkgs/sources/yt/src/constants.rs b/pkgs/sources/yt/src/constants.rs new file mode 100644 index 00000000..5e233656 --- /dev/null +++ b/pkgs/sources/yt/src/constants.rs @@ -0,0 +1,51 @@ +use std::{env, fs, path::PathBuf}; + +pub const HELP_STR: &str = include_str!("./help.str"); + +pub const YT_DLP_FLAGS: [&str; 13] = [ + // Ignore errors arising of unavailable sponsor block API + "--ignore-errors", + "--format", + "bestvideo[height<=?1080]+bestaudio/best", + "--embed-chapters", + "--progress", + "--write-comments", + "--extractor-args", + "youtube:max_comments=150,all,100;comment_sort=top", + "--write-info-json", + "--sponsorblock-mark", + "default", + "--sponsorblock-remove", + "sponsor", +]; +pub const MPV_FLAGS: [&str; 4] = [ + "--speed=2.7", + "--volume=75", + "--keep-open=yes", + "--msg-level=osd/libass=fatal", +]; + +pub const CONCURRENT: u32 = 5; + +pub const DOWNLOAD_DIR: &str = "/tmp/ytcc"; + +fn get_runtime_path(component: &'static str) -> anyhow::Result<PathBuf> { + let out: PathBuf = format!( + "{}/{}", + env::var("XDG_RUNTIME_DIR").expect("This should always exist"), + component + ) + .into(); + fs::create_dir_all(out.parent().expect("Parent should exist"))?; + Ok(out) +} + +const STATUS_PATH: &str = "ytcc/running"; +pub fn status_path() -> anyhow::Result<PathBuf> { + get_runtime_path(STATUS_PATH) +} + +const LAST_SELECT: &str = "ytcc/selected.yts"; +pub fn last_select() -> anyhow::Result<PathBuf> { + get_runtime_path(LAST_SELECT) +} diff --git a/pkgs/sources/yt/src/downloader.rs b/pkgs/sources/yt/src/downloader.rs new file mode 100644 index 00000000..e915700d --- /dev/null +++ b/pkgs/sources/yt/src/downloader.rs @@ -0,0 +1,212 @@ +use std::{ + fs::{self, canonicalize}, + io::{stderr, stdout, Read}, + mem, + os::unix::fs::symlink, + path::PathBuf, + process::Command, + sync::mpsc::{self, Receiver, Sender}, + thread::{self, JoinHandle}, +}; + +use anyhow::{bail, Context, Result}; +use log::{debug, error, warn}; +use url::Url; + +use crate::constants::{status_path, CONCURRENT, DOWNLOAD_DIR, MPV_FLAGS, YT_DLP_FLAGS}; + +#[derive(Debug)] +pub struct Downloadable { + pub url: Url, + pub id: Option<u32>, +} + +impl std::fmt::Display for Downloadable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + "{}|{}", + self.url.as_str().replace('|', ";"), + self.id.unwrap_or(0), + ) + } +} + +pub struct Downloader { + sent: usize, + download_thread: JoinHandle<Result<()>>, + orx: Receiver<(PathBuf, Option<u32>)>, + itx: Option<Sender<Downloadable>>, + playspec: Vec<Downloadable>, +} + +impl Downloader { + pub fn new(mut playspec: Vec<Downloadable>) -> anyhow::Result<Downloader> { + let (itx, irx): (Sender<Downloadable>, Receiver<Downloadable>) = mpsc::channel(); + let (otx, orx) = mpsc::channel(); + let jh = thread::spawn(move || -> Result<()> { + while let Ok(pt) = irx.recv() { + debug!("Got '{}' to be downloaded", pt); + let path = download_url(&pt.url) + .with_context(|| format!("Failed to download url: '{}'", &pt.url))?; + otx.send((path, pt.id)).expect("Should not be dropped"); + } + debug!("Finished Downloading everything"); + Ok(()) + }); + + playspec.reverse(); + let mut output = Downloader { + sent: 0, + download_thread: jh, + orx, + itx: Some(itx), + playspec, + }; + if output.playspec.len() <= CONCURRENT as usize { + output.add(output.playspec.len() as u32)?; + } else { + output.add(CONCURRENT)?; + } + Ok(output) + } + + pub fn add(&mut self, number_to_add: u32) -> Result<()> { + debug!("Adding {} to be downloaded concurrently", number_to_add); + for _ in 0..number_to_add { + let pt = self.playspec.pop().expect("This call should be guarded"); + self.itx.as_ref().expect("Should still be valid").send(pt)?; + self.sent += 1; + } + Ok(()) + } + + /// Return the next video already downloaded, will block until the download is complete + pub fn next(&mut self) -> Option<(PathBuf, Option<u32>)> { + debug!("Requesting next output"); + match self.orx.recv() { + Ok(ok) => { + debug!("Output downloaded to: {}", ok.0.display()); + if !self.playspec.is_empty() { + self.add(1).ok()?; + } else { + debug!( + "Done sending videos to be downloaded, downoladed: {} videos", + self.sent + ); + let itx = mem::take(&mut self.itx); + drop(itx) + } + debug!("Returning: {}|{}", ok.0.display(), ok.1.unwrap_or(0)); + Some(ok) + } + Err(err) => { + debug!("Received error while listening: {}", err); + None + } + } + } + + pub fn drop(self) -> anyhow::Result<()> { + // Check that we really downloaded everything + assert_eq!(self.playspec.len(), 0); + match self.download_thread.join() { + Ok(ok) => ok, + Err(err) => panic!("Failed to join downloader thread: '{:#?}'", err), + } + } + + pub fn consume(mut self) -> anyhow::Result<()> { + while let Some((path, id)) = self.next() { + debug!("Next path to play is: '{}'", path.display()); + let mut info_json = canonicalize(&path).context("Failed to canoncialize path")?; + info_json.set_extension("info.json"); + + if status_path()?.is_symlink() { + fs::remove_file(status_path()?).context("Failed to delete old status file")?; + } else if !status_path()?.exists() { + debug!( + "The status path at '{}' does not exists", + status_path()?.display() + ); + } else { + bail!( + "The status path ('{}') is not a symlink but exists!", + status_path()?.display() + ); + } + + symlink(info_json, status_path()?).context("Failed to symlink")?; + + let mut mpv = Command::new("mpv"); + mpv.stdout(stdout()); + mpv.stderr(stderr()); + mpv.args(MPV_FLAGS); + // TODO: Set the title to the name of the video, not the path <2024-02-09> + // mpv.arg(format!("--title=")) + mpv.arg(&path); + + let status = mpv.status().context("Failed to run mpv")?; + if status.success() { + fs::remove_file(&path)?; + if let Some(id) = id { + println!("\x1b[32;1mMarking {} as watched!\x1b[0m", id); + let mut ytcc = std::process::Command::new("ytcc"); + ytcc.stdout(stdout()); + ytcc.stderr(stderr()); + ytcc.args(["mark"]); + ytcc.arg(id.to_string()); + let status = ytcc.status().context("Failed to run ytcc")?; + if let Some(code) = status.code() { + if code != 0 { + bail!("Ytcc failed with status: {}", code); + } + } + } + debug!("mpv exited with: '{}'", status); + } else { + warn!("mpv exited with: '{}'", status); + } + } + self.drop()?; + Ok(()) + } +} + +fn download_url(url: &Url) -> Result<PathBuf> { + let output_file = tempfile::NamedTempFile::new().context("Failed to create tempfile")?; + output_file + .as_file() + .set_len(0) + .context("Failed to truncate temp-file")?; + if !Into::<PathBuf>::into(DOWNLOAD_DIR).exists() { + fs::create_dir_all(DOWNLOAD_DIR) + .with_context(|| format!("Failed to create download dir at: {}", DOWNLOAD_DIR))? + } + let mut yt_dlp = Command::new("yt-dlp"); + yt_dlp.current_dir(DOWNLOAD_DIR); + yt_dlp.stdout(stdout()); + yt_dlp.stderr(stderr()); + yt_dlp.args(YT_DLP_FLAGS); + yt_dlp.args([ + "--output", + "%(channel)s/%(title)s.%(ext)s", + url.as_str(), + "--print-to-file", + "after_move:filepath", + ]); + yt_dlp.arg(output_file.path().as_os_str()); + + let status = yt_dlp.status().context("Failed to run yt-dlp")?; + if !status.success() { + error!("yt-dlp execution failed with error: '{}'", status); + } + + let mut path = String::new(); + output_file + .as_file() + .read_to_string(&mut path) + .context("Failed to read output file temp file")?; + let path = path.trim(); + Ok(path.into()) +} diff --git a/pkgs/sources/yt/src/help.str b/pkgs/sources/yt/src/help.str new file mode 100644 index 00000000..130fe42a --- /dev/null +++ b/pkgs/sources/yt/src/help.str @@ -0,0 +1,8 @@ +# Commands: +# w, watch = watch id +# d, drop = mark id as watched +# u, url = open the associated URL in the `timesinks.youtube` Firefox profile +# p, pick = leave id as is; This is a noop +# +# These lines can be re-ordered; they are executed from top to bottom. +# vim: filetype=yts conceallevel=2 concealcursor=nc colorcolumn= diff --git a/pkgs/sources/yt/src/lib.rs b/pkgs/sources/yt/src/lib.rs new file mode 100644 index 00000000..b089c1a2 --- /dev/null +++ b/pkgs/sources/yt/src/lib.rs @@ -0,0 +1,185 @@ +use anyhow::{bail, Context}; +use downloader::Downloadable; +use serde::Deserialize; +use url::Url; + +pub mod constants; +pub mod downloader; + +#[derive(Deserialize)] +pub struct YtccListData { + pub url: String, + pub title: String, + pub description: String, + pub publish_date: String, + pub watch_date: Option<f64>, + pub duration: String, + pub thumbnail_url: Option<String>, + pub extractor_hash: String, + pub id: u32, + pub playlists: Vec<YtccPlaylistData>, +} + +impl std::fmt::Display for YtccListData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!( + f, + r#"pick {} "{}" "{}" "{}" "{}" "{}"{}"#, + self.id, + self.title.replace(['"', '„', '”'], "'"), + self.publish_date, + self.playlists + .iter() + .map(|p| p.name.replace('"', "'")) + .collect::<Vec<String>>() + .join(", "), + Duration::from(self.duration.trim()), + self.url.replace('"', "'"), + "\n" + ) + } +} + +#[derive(Deserialize)] +pub struct YtccPlaylistData { + pub name: String, + pub url: String, + pub reverse: bool, +} + +pub enum LineCommand { + Pick, + Drop, + Watch, + Url, +} + +impl std::str::FromStr for LineCommand { + type Err = anyhow::Error; + fn from_str(v: &str) -> Result<Self, <Self as std::str::FromStr>::Err> { + match v { + "pick" | "p" => Ok(Self::Pick), + "drop" | "d" => Ok(Self::Drop), + "watch" | "w" => Ok(Self::Watch), + "url" | "u" => Ok(Self::Url), + other => bail!("'{}' is not a recognized command!", other), + } + } +} + +pub struct Line { + pub cmd: LineCommand, + pub id: u32, + pub url: Url, +} + +/// We expect that each line is correctly formatted, and simply use default ones if they are not +impl From<&str> for Line { + fn from(v: &str) -> Self { + let buf: Vec<_> = v.split_whitespace().collect(); + let url: Url = Url::parse( + buf.last() + .expect("This should always exists") + .trim_matches('"'), + ) + .expect("This parsing should work,as the url is generated"); + + Line { + cmd: buf + .get(0) + .unwrap_or(&"pick") + .parse() + .unwrap_or(LineCommand::Pick), + id: buf.get(1).unwrap_or(&"0").parse().unwrap_or(0), + url, + } + } +} + +pub struct Duration { + time: u32, +} + +impl From<&str> for Duration { + fn from(v: &str) -> Self { + let buf: Vec<_> = v.split(':').take(2).collect(); + Self { + time: (buf[0].parse::<u32>().expect("Should be a number") * 60) + + buf[1].parse::<u32>().expect("Should be a number"), + } + } +} + +impl std::fmt::Display for Duration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + const SECOND: u32 = 1; + const MINUTE: u32 = 60 * SECOND; + const HOUR: u32 = 60 * MINUTE; + + let base_hour = self.time - (self.time % HOUR); + let base_min = (self.time % HOUR) - ((self.time % HOUR) % MINUTE); + let base_sec = (self.time % HOUR) % MINUTE; + + let h = base_hour / HOUR; + let m = base_min / MINUTE; + let s = base_sec / SECOND; + + if self.time == 0 { + write!(f, "[No Duration]") + } else if h > 0 { + write!(f, "[{h}h {m}m]") + } else { + write!(f, "[{m}m {s}s]") + } + } +} +#[cfg(test)] +mod test { + use crate::Duration; + + #[test] + fn test_display_duration_1h() { + let dur = Duration { time: 60 * 60 }; + assert_eq!("[1h 0m]".to_owned(), dur.to_string()); + } + #[test] + fn test_display_duration_30min() { + let dur = Duration { time: 60 * 30 }; + assert_eq!("[30m 0s]".to_owned(), dur.to_string()); + } +} + +pub fn ytcc_drop(id: u32) -> anyhow::Result<()> { + let mut ytcc = std::process::Command::new("ytcc"); + ytcc.args(["mark", &format!("{}", id)]); + if !ytcc.status().context("Failed to run ytcc")?.success() { + bail!("`ytcc mark {}` failed to execute", id) + } + Ok(()) +} + +pub fn filter_line(line: &str) -> anyhow::Result<Option<Downloadable>> { + // Filter out comments and empty lines + if line.starts_with('#') || line.trim().is_empty() { + return Ok(None); + } + + let line = Line::from(line); + match line.cmd { + LineCommand::Pick => Ok(None), + LineCommand::Drop => ytcc_drop(line.id) + .with_context(|| format!("Failed to drop: {}", line.id)) + .map(|_| None), + LineCommand::Watch => Ok(Some(Downloadable { + id: Some(line.id), + url: line.url, + })), + LineCommand::Url => { + let mut firefox = std::process::Command::new("firefox"); + firefox.args(["-P", "timesinks.youtube"]); + firefox.arg(line.url.as_str()); + let _handle = firefox.spawn().context("Failed to run firefox")?; + Ok(None) + } + } +} diff --git a/pkgs/sources/yt/todo b/pkgs/sources/yt/todo new file mode 100644 index 00000000..3f22042c --- /dev/null +++ b/pkgs/sources/yt/todo @@ -0,0 +1 @@ +subtitles diff --git a/pkgs/sources/yt/update.sh b/pkgs/sources/yt/update.sh new file mode 100755 index 00000000..e500bb23 --- /dev/null +++ b/pkgs/sources/yt/update.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +nix flake update + +[ "$1" = "upgrade" ] && cargo upgrade +cargo update + +# vim: ft=sh diff --git a/pkgs/sources/yt/yt.nix b/pkgs/sources/yt/yt.nix new file mode 100644 index 00000000..aaa971c3 --- /dev/null +++ b/pkgs/sources/yt/yt.nix @@ -0,0 +1,29 @@ +{ + lib, + rustPlatform, + ytcc, + yt-dlp, + mpv, + makeWrapper, +}: +rustPlatform.buildRustPackage { + pname = "yt"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + + buildNoDefaultFeatures = true; + buildFeatures = ["yt"]; + + nativeBuildInputs = [ + makeWrapper + ]; + + postInstall = '' + wrapProgram $out/bin/yt \ + --prefix PATH : ${lib.makeBinPath [mpv yt-dlp ytcc]} + ''; +} diff --git a/pkgs/sources/yt/ytc.nix b/pkgs/sources/yt/ytc.nix new file mode 100644 index 00000000..dff5bcf8 --- /dev/null +++ b/pkgs/sources/yt/ytc.nix @@ -0,0 +1,29 @@ +{ + lib, + rustPlatform, + ytcc, + yt-dlp, + mpv, + makeWrapper, +}: +rustPlatform.buildRustPackage { + pname = "ytc"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + + buildNoDefaultFeatures = true; + buildFeatures = ["ytc"]; + + nativeBuildInputs = [ + makeWrapper + ]; + + postInstall = '' + wrapProgram $out/bin/ytc \ + --set PATH ${lib.makeBinPath [mpv yt-dlp ytcc]} + ''; +} diff --git a/pkgs/sources/yt/yts.nix b/pkgs/sources/yt/yts.nix new file mode 100644 index 00000000..9a8b172e --- /dev/null +++ b/pkgs/sources/yt/yts.nix @@ -0,0 +1,27 @@ +{ + lib, + rustPlatform, + ytcc, + makeWrapper, +}: +rustPlatform.buildRustPackage { + pname = "yts"; + version = "0.1.0"; + + src = ./.; + cargoLock = { + lockFile = ./Cargo.lock; + }; + + buildNoDefaultFeatures = true; + buildFeatures = ["yts"]; + + nativeBuildInputs = [ + makeWrapper + ]; + + postInstall = '' + wrapProgram $out/bin/yts \ + --prefix PATH : ${lib.makeBinPath [ytcc]} + ''; +} |