diff --git a/Cargo.lock b/Cargo.lock index 19dbbc1..d8e140f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,7 +79,7 @@ dependencies = [ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -93,6 +93,11 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "heck" version = "0.3.1" @@ -134,9 +139,10 @@ dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bencode 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -281,13 +287,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "regex" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -475,7 +481,7 @@ dependencies = [ [[package]] name = "thread_local" -version = "0.3.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -581,6 +587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" @@ -603,7 +610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rustversion 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" @@ -626,7 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b561e267b2326bb4cebfc0ef9e68355c7abe6c6f522aeac2f5bf95d56c59bdcf" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" diff --git a/Cargo.toml b/Cargo.toml index 24fb81a..97cd4d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ categories = ["command-line-utilities"] homepage = "https://github.com/casey/intermodal" repository = "https://github.com/casey/intermodal" edition = "2018" +default-run = "imdl" [dependencies] ansi_term = "0.12" @@ -31,3 +32,14 @@ walkdir = "2" [dependencies.serde] version = "1" features = ["derive"] + +[dev-dependencies] +glob = "0.3.0" +regex = "1.3.3" + +# generates the table of supported BEPs in README.md +# not an example, but included as an example and not +# a binary because examples can use dev dependencies +[[example]] +name = "generate-bep-table" +path = "bin/generate-bep-table.rs" diff --git a/README.md b/README.md index 7300261..91e50d4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,76 @@ # intermodal: a 40' shipping container for the Internet -## Colored Output +## Bittorrent + +### BEP Support + +| Symbol | Meaning | +|--------------------|----------------| +| :white_check_mark: | Supported | +| :x: | Unsupported | +| :heavy_minus_sign: | Not Applicable | + +| BEP | Status | Title | +|:----------------------------------------------:|:------------------:|:-----------------------------------------------------------------| +| [00](http://bittorrent.org/beps/bep_0000.html) | :heavy_minus_sign: | Index of BitTorrent Enhancement Proposals | +| [01](http://bittorrent.org/beps/bep_0001.html) | :heavy_minus_sign: | The BitTorrent Enhancement Proposal Process | +| [02](http://bittorrent.org/beps/bep_0002.html) | :heavy_minus_sign: | Sample reStructured Text BEP Template | +| [03](http://bittorrent.org/beps/bep_0003.html) | :x: | The BitTorrent Protocol Specification | +| [04](http://bittorrent.org/beps/bep_0004.html) | :heavy_minus_sign: | Assigned Numbers | +| [05](http://bittorrent.org/beps/bep_0005.html) | :x: | DHT Protocol | +| [06](http://bittorrent.org/beps/bep_0006.html) | :heavy_minus_sign: | Fast Extension | +| [07](http://bittorrent.org/beps/bep_0007.html) | :heavy_minus_sign: | IPv6 Tracker Extension | +| [08](http://bittorrent.org/beps/bep_0008.html) | :heavy_minus_sign: | Tracker Peer Obfuscation | +| [09](http://bittorrent.org/beps/bep_0009.html) | :x: | Extension for Peers to Send Metadata Files | +| [10](http://bittorrent.org/beps/bep_0010.html) | :heavy_minus_sign: | Extension Protocol | +| [11](http://bittorrent.org/beps/bep_0011.html) | :heavy_minus_sign: | Peer Exchange (PEX) | +| [12](http://bittorrent.org/beps/bep_0012.html) | :white_check_mark: | Multitracker Metadata Extension | +| [14](http://bittorrent.org/beps/bep_0014.html) | :heavy_minus_sign: | Local Service Discovery | +| [15](http://bittorrent.org/beps/bep_0015.html) | :heavy_minus_sign: | UDP Tracker Protocol for BitTorrent | +| [16](http://bittorrent.org/beps/bep_0016.html) | :heavy_minus_sign: | Superseeding | +| [17](http://bittorrent.org/beps/bep_0017.html) | :x: | HTTP Seeding | +| [18](http://bittorrent.org/beps/bep_0018.html) | :heavy_minus_sign: | Search Engine Specificiation | +| [19](http://bittorrent.org/beps/bep_0019.html) | :x: | WebSeed - HTTP/FTP Seeding (GetRight style) | +| [20](http://bittorrent.org/beps/bep_0020.html) | :heavy_minus_sign: | Peer ID Conventions | +| [21](http://bittorrent.org/beps/bep_0021.html) | :heavy_minus_sign: | Extension for partial seeds | +| [22](http://bittorrent.org/beps/bep_0022.html) | :heavy_minus_sign: | BitTorrent Local Tracker Discovery Protocol | +| [23](http://bittorrent.org/beps/bep_0023.html) | :heavy_minus_sign: | Tracker Returns Compact Peer Lists | +| [24](http://bittorrent.org/beps/bep_0024.html) | :heavy_minus_sign: | Tracker Returns External IP | +| [25](http://bittorrent.org/beps/bep_0025.html) | :heavy_minus_sign: | An Alternate BitTorrent Cache Discovery Protocol | +| [26](http://bittorrent.org/beps/bep_0026.html) | :heavy_minus_sign: | Zeroconf Peer Advertising and Discovery | +| [27](http://bittorrent.org/beps/bep_0027.html) | :white_check_mark: | Private Torrents | +| [28](http://bittorrent.org/beps/bep_0028.html) | :heavy_minus_sign: | Tracker exchange extension | +| [29](http://bittorrent.org/beps/bep_0029.html) | :heavy_minus_sign: | uTorrent transport protocol | +| [30](http://bittorrent.org/beps/bep_0030.html) | :x: | Merkle hash torrent extension | +| [31](http://bittorrent.org/beps/bep_0031.html) | :heavy_minus_sign: | Failure Retry Extension | +| [32](http://bittorrent.org/beps/bep_0032.html) | :heavy_minus_sign: | BitTorrent DHT Extensions for IPv6 | +| [33](http://bittorrent.org/beps/bep_0033.html) | :heavy_minus_sign: | DHT Scrapes | +| [34](http://bittorrent.org/beps/bep_0034.html) | :x: | DNS Tracker Preferences | +| [35](http://bittorrent.org/beps/bep_0035.html) | :x: | Torrent Signing | +| [36](http://bittorrent.org/beps/bep_0036.html) | :x: | Torrent RSS feeds | +| [37](http://bittorrent.org/beps/bep_0037.html) | :heavy_minus_sign: | Anonymous BitTorrent over proxies | +| [38](http://bittorrent.org/beps/bep_0038.html) | :x: | Finding Local Data Via Torrent File Hints | +| [39](http://bittorrent.org/beps/bep_0039.html) | :x: | Updating Torrents Via Feed URL | +| [40](http://bittorrent.org/beps/bep_0040.html) | :heavy_minus_sign: | Canonical Peer Priority | +| [41](http://bittorrent.org/beps/bep_0041.html) | :x: | UDP Tracker Protocol Extensions | +| [42](http://bittorrent.org/beps/bep_0042.html) | :heavy_minus_sign: | DHT Security extension | +| [43](http://bittorrent.org/beps/bep_0043.html) | :heavy_minus_sign: | Read-only DHT Nodes | +| [44](http://bittorrent.org/beps/bep_0044.html) | :x: | Storing arbitrary data in the DHT | +| [45](http://bittorrent.org/beps/bep_0045.html) | :x: | Multiple-address operation for the BitTorrent DHT | +| [46](http://bittorrent.org/beps/bep_0046.html) | :x: | Updating Torrents Via DHT Mutable Items | +| [47](http://bittorrent.org/beps/bep_0047.html) | :x: | Padding files and extended file attributes | +| [48](http://bittorrent.org/beps/bep_0048.html) | :x: | Tracker Protocol Extension: Scrape | +| [49](http://bittorrent.org/beps/bep_0049.html) | :x: | Distributed Torrent Feeds | +| [50](http://bittorrent.org/beps/bep_0050.html) | :heavy_minus_sign: | Publish/Subscribe Protocol | +| [51](http://bittorrent.org/beps/bep_0051.html) | :heavy_minus_sign: | DHT Infohash Indexing | +| [52](http://bittorrent.org/beps/bep_0052.html) | :x: | The BitTorrent Protocol Specification v2 | +| [53](http://bittorrent.org/beps/bep_0053.html) | :x: | Magnet URI extension - Select specific file indices for download | +| [54](http://bittorrent.org/beps/bep_0054.html) | :heavy_minus_sign: | The lt_donthave extension | +| [55](http://bittorrent.org/beps/bep_0055.html) | :heavy_minus_sign: | Holepunch extension | + +## General Functionality + +### Colored Output Intermodal features colored help, error, and informational output. Colored output is disabled if Intermodal detects that it is not printing to a TTY. diff --git a/bin/generate-bep-table.rs b/bin/generate-bep-table.rs new file mode 100644 index 0000000..51e5ecd --- /dev/null +++ b/bin/generate-bep-table.rs @@ -0,0 +1,190 @@ +use std::{ + error::Error, + fmt::{self, Display, Formatter}, + fs, + str::FromStr, +}; + +use glob::glob; +use regex::Regex; + +const README: &str = "README.md"; + +struct Bep { + number: usize, + title: String, + status: Status, +} + +enum Status { + Unknown, + NotApplicable, + Supported, + NotSupported, +} + +impl FromStr for Status { + type Err = String; + + fn from_str(text: &str) -> Result { + match text { + "x" => Ok(Self::NotSupported), + "+" => Ok(Self::Supported), + "-" => Ok(Self::NotApplicable), + "?" => Ok(Self::Unknown), + ":x:" => Ok(Self::NotSupported), + ":white_check_mark:" => Ok(Self::Supported), + ":heavy_minus_sign:" => Ok(Self::NotApplicable), + ":grey_question:" => Ok(Self::Unknown), + _ => Err(format!("invalid status: {}", text)), + } + } +} + +impl Display for Status { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Unknown => write!(f, ":grey_question:"), + Self::NotApplicable => write!(f, ":heavy_minus_sign:"), + Self::Supported => write!(f, ":white_check_mark:"), + Self::NotSupported => write!(f, ":x:"), + } + } +} + +fn main() -> Result<(), Box> { + let title_re = Regex::new("(?m)^:Title: (?P.*)$")?; + + let mut beps = Vec::new(); + + for result in glob("tmp/bittorrent.org/beps/bep_*.rst")? { + let path = result?; + + let number = path + .file_stem() + .unwrap() + .to_string_lossy() + .split('_') + .nth(1) + .unwrap() + .parse::<usize>()?; + + if number == 1000 { + continue; + } + + let rst = fs::read_to_string(path)?; + + let title = title_re + .captures(&rst) + .unwrap() + .name("title") + .unwrap() + .as_str() + .trim() + .to_owned(); + + beps.push(Bep { + status: Status::Unknown, + number, + title, + }); + } + + beps.sort_by_key(|bep| bep.number); + + let table_re = Regex::new( + r"(?mx) + ^[|]\ BEP.* + ( + \n + [|] + .* + )* + ", + )?; + + let readme = fs::read_to_string(README)?; + + let parts = table_re.split(&readme).into_iter().collect::<Vec<&str>>(); + + assert_eq!(parts.len(), 2); + + let before = parts[0]; + let after = parts[1]; + let original = table_re + .captures(&readme) + .unwrap() + .get(0) + .unwrap() + .as_str() + .trim(); + + let row_re = Regex::new( + r"(?x) + ^ + \| + \s* + \[ + (?P<number>[0-9]+) + \] + .* + \s* + \| + (?P<status>.*) + \| + (?P<title>.*) + \| + $ + ", + )?; + + let mut originals = Vec::new(); + + for row in original.lines().skip(2) { + let captures = row_re.captures(row).unwrap(); + originals.push(Bep { + number: captures.name("number").unwrap().as_str().parse()?, + status: captures.name("status").unwrap().as_str().trim().parse()?, + title: captures.name("title").unwrap().as_str().to_owned(), + }); + } + + assert_eq!(originals.len(), beps.len()); + + let mut lines = Vec::new(); + + let width = beps.iter().map(|bep| bep.title.len()).max().unwrap_or(0); + + lines.push(format!( + "| BEP | Status | {:width$} |", + "Title", + width = width + )); + + lines.push(format!( + "|:----------------------------------------------:|:------------------:|:{:-<width$}-|", + "", + width = width + )); + + for (bep, original) in beps.into_iter().zip(originals) { + assert_eq!(bep.number, original.number); + lines.push(format!( + "| [{:02}](http://bittorrent.org/beps/bep_{:04}.html) | {:18} | {:width$} |", + bep.number, + bep.number, + original.status.to_string(), + bep.title, + width = width + )); + } + + let table = lines.join("\n"); + + let readme = &[before.trim(), "", &table, "", after.trim(), ""].join("\n"); + + fs::write(README, readme)?; + + Ok(()) +} diff --git a/justfile b/justfile index 41a9f82..a080c61 100644 --- a/justfile +++ b/justfile @@ -20,13 +20,25 @@ done BRANCH: test: cargo test -# lint code lint: cargo clippy +preview-readme: + grip -b README.md + +dev-deps: + brew install grip + +generate-bep-table: + cargo run --example generate-bep-table + # retrieve large collection of torrents from the Internet Archive get-torrents: aria2c \ -d dat \ -x 10 \ 'https://ia802701.us.archive.org/21/items/2014_torrent_archive_organized/torrent_archive_organized.zip' + +# download bittorrent.org repository +get-beps: + git clone git@github.com:bittorrent/bittorrent.org.git tmp/bittorrent.org