initial commit

This commit is contained in:
Antoine Langlois 2024-03-31 00:36:57 +01:00
commit a34a3de30a
Signed by: DataHearth
GPG Key ID: 946E2D0C410C7B3D
18 changed files with 4349 additions and 0 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.direnv
/target

650
Cargo.lock generated Normal file
View File

@ -0,0 +1,650 @@
# 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 = "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.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "autocfg"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[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 = "bendy"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8133e404c8bec821e531f347dab1247bf64f60882826e7228f8ffeb33a35a658"
dependencies = [
"failure",
"serde",
"serde_bytes",
]
[[package]]
name = "brs"
version = "0.1.0"
dependencies = [
"bendy",
"chrono",
"hex",
"human_bytes",
"serde",
"serde_bencode",
"serde_bytes",
"serde_json",
"thiserror",
]
[[package]]
name = "brs-cli"
version = "0.1.0"
dependencies = [
"brs",
"clap",
]
[[package]]
name = "brs-gui"
version = "0.1.0"
dependencies = [
"bendy",
]
[[package]]
name = "bumpalo"
version = "3.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
[[package]]
name = "cc"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
[[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.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"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 2.0.55",
]
[[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.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "human_bytes"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e"
[[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.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[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 = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[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 = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "serde"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_bencode"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a70dfc7b7438b99896e7f8992363ab8e2c4ba26aa5ec675d32d1c3c2c33d413e"
dependencies = [
"serde",
"serde_bytes",
]
[[package]]
name = "serde_bytes"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
]
[[package]]
name = "serde_json"
version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "strsim"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[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 2.0.55",
"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 2.0.55",
"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.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[workspace]
members = [
"brs",
"cli",
"gui"
]
resolver = "2"

17
brs/Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "brs"
version = "0.1.0"
edition = "2021"
description = "BRS stands for \"BitTorrent Rust\". It a library for the BitTorrent protocol."
authors = [ "Antoine Langlois <dev@antoine-langlois.net>" ]
[dependencies]
bendy = { version = "0.3", features = ["serde"] }
chrono = { version = "0.4", features = ["serde"] }
hex = "0.4"
human_bytes = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_bencode = "0.2"
serde_bytes = "0.11"
serde_json = "1.0"
thiserror = "1.0"

11
brs/src/error.rs Normal file
View File

@ -0,0 +1,11 @@
use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum TorrentError {
#[error("Failed to parse torrent file: {0}")]
ParseTorrent(String),
#[error("Failed to read torrent file: {0}")]
ReadTorrent(#[from] io::Error)
}

4
brs/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod torrent;
// pub mod torrent;
mod error;
mod macros;

92
brs/src/macros.rs Normal file
View File

@ -0,0 +1,92 @@
#[macro_export]
macro_rules! write_option {
($f:expr, $k:expr, $v:expr) => {{
if let Some(v) = $v {
write!($f, "{}: {}\n", $k, v)?;
}
}};
}
#[macro_export]
macro_rules! match_dict {
($value:expr, $expected:expr) => {{
match $value {
serde_bencode::value::Value::Dict(v) => Ok(v),
serde_bencode::value::Value::List(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Seq,
&$expected,
)),
serde_bencode::value::Value::Int(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Signed(v),
&$expected,
)),
serde_bencode::value::Value::Bytes(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Bytes(&v),
&$expected,
)),
}
}};
}
#[macro_export]
macro_rules! match_list {
($value:expr, $expected:expr) => {{
match $value {
serde_bencode::value::Value::List(v) => Ok(v),
serde_bencode::value::Value::Dict(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Map,
&$expected,
)),
serde_bencode::value::Value::Int(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Signed(*v),
&$expected,
)),
serde_bencode::value::Value::Bytes(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Bytes(&v),
&$expected,
)),
}
}};
}
#[macro_export]
macro_rules! match_bytes {
($value:expr, $expected:expr) => {{
match $value {
serde_bencode::value::Value::Bytes(v) => Ok(v),
serde_bencode::value::Value::Dict(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Map,
&$expected,
)),
serde_bencode::value::Value::Int(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Signed(*v),
&$expected,
)),
serde_bencode::value::Value::List(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Seq,
&$expected,
)),
}
}};
}
#[macro_export]
macro_rules! match_int {
($value:expr, $expected:expr) => {{
match $value {
serde_bencode::value::Value::Int(v) => Ok(v),
serde_bencode::value::Value::Dict(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Map,
&$expected,
)),
serde_bencode::value::Value::Bytes(v) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Bytes(&v),
&$expected,
)),
serde_bencode::value::Value::List(_) => Err(serde::de::Error::invalid_type(
serde::de::Unexpected::Seq,
&$expected,
)),
}
}};
}

94
brs/src/torrent/de.rs Normal file
View File

@ -0,0 +1,94 @@
use chrono::{DateTime, Utc};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer,
};
use crate::{match_bytes, match_dict, match_int, match_list, torrent::TorrentFiles};
pub fn from_bool_to_int<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
match u8::deserialize(deserializer)? {
0 => Ok(false),
1 => Ok(true),
other => Err(Error::invalid_value(
Unexpected::Unsigned(other as u64),
&"zero or one",
)),
}
}
pub fn from_i64_to_datetime<'de, D>(deserializer: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: Deserializer<'de>,
{
let timestamp: Option<i64> = Option::deserialize(deserializer)?;
if let Some(v) = timestamp {
return Ok(DateTime::from_timestamp(v, 0));
}
Ok(None)
}
pub fn from_bytes_to_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
let bytes = serde_bytes::ByteBuf::deserialize(deserializer)?;
Ok(bytes
.chunks(20)
.map(|v| hex::encode(v))
.collect::<Vec<String>>())
}
pub fn from_files_list_to_struct<'de, D>(
deserializer: D,
) -> Result<Option<Vec<TorrentFiles>>, D::Error>
where
D: Deserializer<'de>,
{
let list: Vec<serde_bencode::value::Value> = match Option::deserialize(deserializer)? {
Some(v) => v,
None => return Ok(None),
};
let mut torrent_files = vec![];
for v in list {
let file_dict = match_dict!(v, "map with keys \"path\" & \"length\"")?;
if file_dict.len() > 2 {
return Err(Error::invalid_length(
file_dict.len(),
&"path and length only",
));
}
let path = file_dict.get(b"path".as_ref());
let length = file_dict.get(b"length".as_ref());
if path.is_none() {
return Err(Error::missing_field(
"\"path\" is mandatory in a files list",
));
} else if length.is_none() {
return Err(Error::missing_field(
"\"length\" is mandatory in a files list",
));
}
let path = {
let mut str_path = String::new();
for chunks in match_list!(path.unwrap(), "list of bytes")? {
let chunks = match_bytes!(chunks, "sequence of bytes")?;
str_path.push_str(std::str::from_utf8(chunks).map_err(|_| {
Error::invalid_value(Unexpected::Bytes(&chunks), &"Invalid bytes string")
})?);
}
str_path
};
let length = match_int!(length.unwrap(), "integer")?;
torrent_files.push(TorrentFiles {
path,
length: *length as u64,
})
}
Ok(Some(torrent_files))
}

116
brs/src/torrent/display.rs Normal file
View File

@ -0,0 +1,116 @@
use std::collections::HashMap;
use human_bytes::human_bytes;
use crate::write_option;
use super::Torrent;
impl std::fmt::Display for Torrent {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "tracker: {}\n", self.announce)?;
write_option!(f, "created by", &self.additional_fields.created_by);
write_option!(f, "creation date", &self.additional_fields.creation_date);
write_option!(f, "comment", &self.additional_fields.comment);
if self.additional_fields.extra_fields.len() > 0 {
for (k, v) in self.additional_fields.extra_fields.clone().into_iter() {
let value = match v {
serde_bencode::value::Value::Bytes(v) => std::str::from_utf8(&v)
.map_err(|_| std::fmt::Error)?
.to_string(),
serde_bencode::value::Value::Int(v) => v.to_string(),
serde_bencode::value::Value::List(v) => {
display_list(f, v)?;
continue;
}
serde_bencode::value::Value::Dict(v) => {
display_dict(f, v)?;
continue;
}
};
write!(f, "{k}: {value}\n")?;
}
}
write!(f, "\n")?;
write!(f, "Torrent information:\n")?;
write!(f, "\tname: {}\n", self.info.name)?;
write!(f, "\tprivate: {}\n", self.info.additional_fields.private)?;
if let Some(v) = self.info.length {
write!(f, "\tfile size: {}\n", human_bytes(v as f64))?;
}
write!(f, "\tpieces: {:?}\n", self.info.pieces.len())?;
write!(
f,
"\tpiece size: {}\n",
human_bytes(self.info.piece_length as f64)
)?;
if std::env::var("BRS_PRINT_TORRENT_FILES").is_ok() {
if let Some(v) = &self.info.files {
write!(f, "\tfiles:\n")?;
for file in v {
write!(f, "\t - {}\n", file.path)?;
write!(f, "\t size: {}\n", human_bytes(file.length as f64))?;
}
}
}
if self.info.additional_fields.extra_fields.len() > 0 {
for (k, v) in &self.info.additional_fields.extra_fields {
write!(f, "\t{}: {:#?}\n", k, v)?;
}
}
Ok(())
}
}
fn display_dict(
f: &mut std::fmt::Formatter,
dict: HashMap<Vec<u8>, serde_bencode::value::Value>,
) -> std::fmt::Result {
for (k, v) in dict {
let key = std::str::from_utf8(&k).map_err(|_| std::fmt::Error)?;
let value = match v {
serde_bencode::value::Value::Bytes(v) => std::str::from_utf8(&v)
.map_err(|_| std::fmt::Error)?
.to_string(),
serde_bencode::value::Value::Int(v) => v.to_string(),
serde_bencode::value::Value::List(v) => {
display_list(f, v)?;
continue;
},
serde_bencode::value::Value::Dict(v) => {
display_dict(f, v)?;
continue;
}
};
write!(f, "{key}: {value}\n").map_err(|_| std::fmt::Error)?
}
Ok(())
}
fn display_list(
f: &mut std::fmt::Formatter,
list: Vec<serde_bencode::value::Value>,
) -> std::fmt::Result {
for element in list {
let value = match element {
serde_bencode::value::Value::Bytes(v) => std::str::from_utf8(&v)
.map_err(|_| std::fmt::Error)?
.to_string(),
serde_bencode::value::Value::Int(v) => v.to_string(),
serde_bencode::value::Value::List(v) => {
display_list(f, v)?;
continue;
}
serde_bencode::value::Value::Dict(v) => {
display_dict(f, v)?;
continue;
}
};
write!(f, " - {value}\n")?;
}
Ok(())
}

91
brs/src/torrent/mod.rs Normal file
View File

@ -0,0 +1,91 @@
mod de;
mod display;
use std::{collections::HashMap, fs};
use chrono::{DateTime, Utc};
use serde::Deserialize;
use crate::{error::TorrentError, torrent::de::*};
#[derive(Debug, Deserialize)]
pub struct Torrent {
/// Announcer URL
pub announce: String,
/// Torrent information
pub info: TorrentInfo,
/// Non official fields
#[serde(flatten)]
pub additional_fields: RootAdditionalFields,
}
/// TorrentInfo is a struct that contains all the information about the torrent file.
#[derive(Debug, Deserialize)]
pub struct TorrentInfo {
/// Recommanded output file or root directory
pub name: String,
/// Size of each data piece
#[serde(rename = "piece length")]
pub piece_length: u64,
/// SHA1 hashes of each pieces
#[serde(deserialize_with = "from_bytes_to_vec")]
pub pieces: Vec<String>,
/// In case of a single file, represents the file size
pub length: Option<u64>,
#[serde(default, deserialize_with = "from_files_list_to_struct")]
/// In case of multiple files/directories, represents all files/directories available
pub files: Option<Vec<TorrentFiles>>,
// Additional fields available that are not part of the original specification
#[serde(flatten)]
pub additional_fields: TorrentInfoAdditionalFields,
}
#[derive(Debug, Deserialize)]
pub struct TorrentFiles {
/// Output file path
pub path: String,
/// File size
pub length: u64,
}
/// RootAdditionalFields is a struct that contains all the additional fields that are not part of the
/// original [BitTorrent](https://www.bittorrent.org/beps/bep_0003.html) specification.
#[derive(Debug, Deserialize)]
pub struct RootAdditionalFields {
/// Torrent creator or software name
#[serde(rename = "created by")]
pub created_by: Option<String>,
/// Torrent creation date
#[serde(
default,
rename = "creation date",
deserialize_with = "from_i64_to_datetime"
)]
pub creation_date: Option<DateTime<Utc>>,
/// Comment about the torrent
pub comment: Option<String>,
// #[serde(rename = "url-list")]
// /// List of resources available
// pub url_list: Option<Vec<String>>,
#[serde(flatten)]
/// Extra fields not explicitly covered by the struct
pub extra_fields: HashMap<String, serde_bencode::value::Value>,
}
/// TorrentInfoAdditionalFields is a struct that contains all the additional fields that are not part of the
/// original [BitTorrent](https://www.bittorrent.org/beps/bep_0003.html) specification.
#[derive(Debug, Deserialize)]
pub struct TorrentInfoAdditionalFields {
/// Is the torrent private
#[serde(default, deserialize_with = "from_bool_to_int")]
pub private: bool,
/// Extra fields not explicitly covered by the struct
#[serde(flatten)]
pub extra_fields: HashMap<String, serde_bencode::value::Value>,
}
pub fn parse(path: String) -> Result<Torrent, TorrentError> {
let torrent_file = fs::read(path).map_err(|e| TorrentError::ReadTorrent(e))?;
serde_bencode::from_bytes(&torrent_file).map_err(|e| TorrentError::ParseTorrent(e.to_string()))
}

10
cli/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "brs-cli"
version = "0.1.0"
edition = "2021"
description = "BRS desktop GUI and WebUI"
authors = [ "Antoine Langlois <dev@antoine-langlois.net>" ]
[dependencies]
brs = { path = "../brs" }
clap = { version = "4.5", features = ["derive"] }

3059
cli/out Normal file

File diff suppressed because it is too large Load Diff

28
cli/src/main.rs Normal file
View File

@ -0,0 +1,28 @@
use clap::{self, Parser};
#[derive(clap::Parser)]
#[command(version, about)]
struct Cli {
#[command(subcommand)]
commands: Commands,
}
#[derive(clap::Subcommand)]
enum Commands {
Info {
path: String,
}
}
fn main() {
let cli = Cli::parse();
match cli.commands {
Commands::Info { path } => {
match brs::torrent::parse(path) {
Ok(v) => println!("{v}"),
Err(e) => println!("{e}"),
}
}
}
}

130
flake.lock Normal file
View File

@ -0,0 +1,130 @@
{
"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"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1711523803,
"narHash": "sha256-UKcYiHWHQynzj6CN/vTcix4yd1eCu1uFdsuarupdCQQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2726f127c15a4cc9810843b96cad73c7eb39e443",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1706487304,
"narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "90f456026d284c22b3e3497be980b2e47d0b28ac",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1711592024,
"narHash": "sha256-oD4OJ3TRmVrbAuKZWxElRCyCagNCDuhfw2exBmNOy48=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "aa858717377db2ed8ffd2d44147d907baee656e5",
"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"
}
},
"systems_2": {
"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
}

24
flake.nix Normal file
View File

@ -0,0 +1,24 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
rust-bin.stable.latest.default
];
};
}
);
}

9
gui/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "brs-gui"
version = "0.1.0"
edition = "2021"
description = "BRS desktop GUI and WebUI"
authors = [ "Antoine Langlois <dev@antoine-langlois.net>" ]
[dependencies]
bendy = "0.3"

3
gui/src/main.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}