Compare commits
	
		
			5 Commits
		
	
	
		
			2346c30fec
			...
			ea3b22fccc
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ea3b22fccc | |||
|  | 984543fcb9 | ||
|  | 6c4805890b | ||
|  | b82ccf1882 | ||
|  | 379a001f47 | 
							
								
								
									
										13
									
								
								.github/workflows/build.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.github/workflows/build.yaml
									
									
									
									
										vendored
									
									
								
							| @ -39,7 +39,7 @@ jobs: | |||||||
|     runs-on: ${{matrix.os}} |     runs-on: ${{matrix.os}} | ||||||
| 
 | 
 | ||||||
|     env: |     env: | ||||||
|       RUSTFLAGS: "-D warnings" |       RUSTFLAGS: --deny warnings | ||||||
| 
 | 
 | ||||||
|     steps: |     steps: | ||||||
|     - uses: actions/checkout@v2 |     - uses: actions/checkout@v2 | ||||||
| @ -63,14 +63,9 @@ jobs: | |||||||
|           target |           target | ||||||
|         key: cargo-${{ env.CACHE_KEY }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} |         key: cargo-${{ env.CACHE_KEY }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} | ||||||
| 
 | 
 | ||||||
|     - name: Install Stable |     - name: Install Rust Toolchain Components | ||||||
|       uses: actions-rs/toolchain@v1 |       run: | | ||||||
|       with: |         rustup component add clippy rustfmt | ||||||
|         toolchain: stable |  | ||||||
|         target: ${{matrix.target}} |  | ||||||
|         profile: minimal |  | ||||||
|         components: clippy, rustfmt |  | ||||||
|         override: true |  | ||||||
| 
 | 
 | ||||||
|     - name: Info |     - name: Info | ||||||
|       run: | |       run: | | ||||||
|  | |||||||
							
								
								
									
										657
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										657
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										15
									
								
								bin/package
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								bin/package
									
									
									
									
									
								
							| @ -26,16 +26,13 @@ case $os in | |||||||
|     ;; |     ;; | ||||||
| esac | esac | ||||||
| 
 | 
 | ||||||
| case $os in | RUSTFLAGS='--deny warnings --codegen target-feature=+crt-static' \ | ||||||
|   ubuntu-latest | macos-latest) |   cargo build --bin $bin --target $target --release | ||||||
|     cargo rustc --bin $bin --target $target --release |  | ||||||
| executable=target/$target/release/$bin | executable=target/$target/release/$bin | ||||||
|     ;; | 
 | ||||||
|   windows-latest) | if [[ $os == windows-2016 ]]; then | ||||||
|     cargo rustc --bin $bin --target $target --release -- -C target-feature="+crt-static" |   executable=$executable.exe | ||||||
|     executable=target/$target/release/$bin.exe | fi | ||||||
|     ;; |  | ||||||
| esac |  | ||||||
| 
 | 
 | ||||||
| echo "Building completions..." | echo "Building completions..." | ||||||
| cargo run --package gen -- --bin $executable completion-scripts | cargo run --package gen -- --bin $executable completion-scripts | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								rust-toolchain
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								rust-toolchain
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | 1.51.0 | ||||||
| @ -7,9 +7,9 @@ const TI: u64 = GI << 10; | |||||||
| const PI: u64 = TI << 10; | const PI: u64 = TI << 10; | ||||||
| const EI: u64 = PI << 10; | const EI: u64 = PI << 10; | ||||||
| 
 | 
 | ||||||
| #[serde(transparent)] |  | ||||||
| #[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)] | #[derive(Debug, PartialEq, Copy, Clone, PartialOrd, Ord, Eq, Serialize, Deserialize, Default)] | ||||||
| pub(crate) struct Bytes(pub(crate) u64); | #[serde(transparent)] | ||||||
|  | pub struct Bytes(pub(crate) u64); | ||||||
| 
 | 
 | ||||||
| impl Bytes { | impl Bytes { | ||||||
|   pub(crate) fn kib() -> Self { |   pub(crate) fn kib() -> Self { | ||||||
| @ -89,7 +89,7 @@ impl FromStr for Bytes { | |||||||
|       _ => { |       _ => { | ||||||
|         return Err(Error::ByteSuffix { |         return Err(Error::ByteSuffix { | ||||||
|           text: text.to_owned(), |           text: text.to_owned(), | ||||||
|           suffix: suffix.to_owned(), |           suffix: suffix.clone(), | ||||||
|         }) |         }) | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct Env { | pub struct Env { | ||||||
|   args: Vec<OsString>, |   args: Vec<OsString>, | ||||||
|   dir: PathBuf, |   dir: PathBuf, | ||||||
|   input: Box<dyn InputStream>, |   input: Box<dyn InputStream>, | ||||||
| @ -41,7 +41,7 @@ impl Env { | |||||||
|         .and_then(|width| width.parse::<usize>().ok()); |         .and_then(|width| width.parse::<usize>().ok()); | ||||||
| 
 | 
 | ||||||
|       if let Some(width) = width { |       if let Some(width) = width { | ||||||
|         app = app.set_term_width(width) |         app = app.set_term_width(width); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       app |       app | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ use crate::common::*; | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Snafu)] | #[derive(Debug, Snafu)] | ||||||
| #[snafu(visibility(pub(crate)))] | #[snafu(visibility(pub(crate)))] | ||||||
| pub(crate) enum Error { | pub enum Error { | ||||||
|   #[snafu(display("Failed to parse announce URL: {}", source))] |   #[snafu(display("Failed to parse announce URL: {}", source))] | ||||||
|   AnnounceUrlParse { source: url::ParseError }, |   AnnounceUrlParse { source: url::ParseError }, | ||||||
|   #[snafu(display("Failed to parse byte count `{}`: {}", text, source))] |   #[snafu(display("Failed to parse byte count `{}`: {}", text, source))] | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[serde(transparent)] |  | ||||||
| #[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)] | #[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)] | ||||||
|  | #[serde(transparent)] | ||||||
| pub(crate) struct FilePath { | pub(crate) struct FilePath { | ||||||
|   components: Vec<String>, |   components: Vec<String>, | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, PartialEq, Clone)] | #[derive(Debug, PartialEq, Clone)] | ||||||
| pub(crate) struct HostPort { | pub struct HostPort { | ||||||
|   host: Host, |   host: Host, | ||||||
|   port: u16, |   port: u16, | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ use crate::common::*; | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Snafu)] | #[derive(Debug, Snafu)] | ||||||
| #[snafu(visibility(pub(crate)))] | #[snafu(visibility(pub(crate)))] | ||||||
| pub(crate) enum HostPortParseError { | pub enum HostPortParseError { | ||||||
|   #[snafu(display("Failed to parse host `{}`: {}", text, source))] |   #[snafu(display("Failed to parse host `{}`: {}", text, source))] | ||||||
|   Host { |   Host { | ||||||
|     text: String, |     text: String, | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								src/info.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/info.rs
									
									
									
									
									
								
							| @ -1,22 +1,22 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | ||||||
| pub(crate) struct Info { | pub struct Info { | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) private: Option<bool>, |   pub private: Option<bool>, | ||||||
|   #[serde(rename = "piece length")] |   #[serde(rename = "piece length")] | ||||||
|   pub(crate) piece_length: Bytes, |   pub piece_length: Bytes, | ||||||
|   pub(crate) name: String, |   pub name: String, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) source: Option<String>, |   pub source: Option<String>, | ||||||
|   pub(crate) pieces: PieceList, |   pub(crate) pieces: PieceList, | ||||||
|   #[serde(flatten)] |   #[serde(flatten)] | ||||||
|   pub(crate) mode: Mode, |   pub(crate) mode: Mode, | ||||||
| @ -26,7 +26,7 @@ pub(crate) struct Info { | |||||||
|     with = "unwrap_or_skip", |     with = "unwrap_or_skip", | ||||||
|     rename = "update-url" |     rename = "update-url" | ||||||
|   )] |   )] | ||||||
|   pub(crate) update_url: Option<Url>, |   pub update_url: Option<Url>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Info { | impl Info { | ||||||
|  | |||||||
| @ -1,12 +1,12 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Eq, PartialEq, Copy, Clone)] | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||||||
| pub(crate) struct Infohash { | pub struct Infohash { | ||||||
|   inner: Sha1Digest, |   pub inner: Sha1Digest, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Infohash { | impl Infohash { | ||||||
|   pub(crate) fn from_input(input: &Input) -> Result<Infohash, Error> { |   pub fn from_input(input: &Input) -> Result<Infohash, Error> { | ||||||
|     let value = Value::from_bencode(input.data()).map_err(|error| Error::MetainfoDecode { |     let value = Value::from_bencode(input.data()).map_err(|error| Error::MetainfoDecode { | ||||||
|       input: input.source().clone(), |       input: input.source().clone(), | ||||||
|       error, |       error, | ||||||
| @ -43,7 +43,7 @@ impl Infohash { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn from_bencoded_info_dict(info: &[u8]) -> Infohash { |   pub fn from_bencoded_info_dict(info: &[u8]) -> Infohash { | ||||||
|     Infohash { |     Infohash { | ||||||
|       inner: Sha1Digest::from_data(info), |       inner: Sha1Digest::from_data(info), | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,20 +1,20 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct Input { | pub struct Input { | ||||||
|   source: InputTarget, |   source: InputTarget, | ||||||
|   data: Vec<u8>, |   data: Vec<u8>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Input { | impl Input { | ||||||
|   pub(crate) fn new(source: InputTarget, data: Vec<u8>) -> Input { |   pub fn new(source: InputTarget, data: Vec<u8>) -> Input { | ||||||
|     Self { source, data } |     Self { source, data } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn data(&self) -> &[u8] { |   pub fn data(&self) -> &[u8] { | ||||||
|     &self.data |     &self.data | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn source(&self) -> &InputTarget { |   pub fn source(&self) -> &InputTarget { | ||||||
|     &self.source |     &self.source | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,13 +1,13 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(PartialEq, Debug, Clone)] | #[derive(PartialEq, Debug, Clone)] | ||||||
| pub(crate) enum InputTarget { | pub enum InputTarget { | ||||||
|   Path(PathBuf), |   Path(PathBuf), | ||||||
|   Stdin, |   Stdin, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl InputTarget { | impl InputTarget { | ||||||
|   pub(crate) fn try_from_os_str(text: &OsStr) -> Result<Self, OsString> { |   pub fn try_from_os_str(text: &OsStr) -> Result<Self, OsString> { | ||||||
|     text |     text | ||||||
|       .try_into() |       .try_into() | ||||||
|       .map_err(|err: Error| OsString::from(err.to_string())) |       .map_err(|err: Error| OsString::from(err.to_string())) | ||||||
|  | |||||||
							
								
								
									
										126
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -41,85 +41,85 @@ | |||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod assert_matches; | pub mod assert_matches; | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod errln; | pub mod errln; | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod err; | pub mod err; | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod out; | pub mod out; | ||||||
| 
 | 
 | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod outln; | pub mod outln; | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod test_env; | pub mod test_env; | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod test_env_builder; | pub mod test_env_builder; | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod capture; | pub mod capture; | ||||||
| 
 | 
 | ||||||
| mod arguments; | pub mod arguments; | ||||||
| mod bytes; | pub mod bytes; | ||||||
| mod common; | pub mod common; | ||||||
| mod consts; | pub mod consts; | ||||||
| mod env; | pub mod env; | ||||||
| mod error; | pub mod error; | ||||||
| mod file_error; | pub mod file_error; | ||||||
| mod file_info; | pub mod file_info; | ||||||
| mod file_path; | pub mod file_path; | ||||||
| mod file_status; | pub mod file_status; | ||||||
| mod files; | pub mod files; | ||||||
| mod hasher; | pub mod hasher; | ||||||
| mod host_port; | pub mod host_port; | ||||||
| mod host_port_parse_error; | pub mod host_port_parse_error; | ||||||
| mod info; | pub mod info; | ||||||
| mod infohash; | pub mod infohash; | ||||||
| mod input; | pub mod input; | ||||||
| mod input_stream; | pub mod input_stream; | ||||||
| mod input_target; | pub mod input_target; | ||||||
| mod into_u64; | pub mod into_u64; | ||||||
| mod into_usize; | pub mod into_usize; | ||||||
| mod invariant; | pub mod invariant; | ||||||
| mod lint; | pub mod lint; | ||||||
| mod linter; | pub mod linter; | ||||||
| mod magnet_link; | pub mod magnet_link; | ||||||
| mod magnet_link_parse_error; | pub mod magnet_link_parse_error; | ||||||
| mod md5_digest; | pub mod md5_digest; | ||||||
| mod metainfo; | pub mod metainfo; | ||||||
| mod metainfo_error; | pub mod metainfo_error; | ||||||
| mod mode; | pub mod mode; | ||||||
| mod options; | pub mod options; | ||||||
| mod output_stream; | pub mod output_stream; | ||||||
| mod output_target; | pub mod output_target; | ||||||
| mod piece_length_picker; | pub mod piece_length_picker; | ||||||
| mod piece_list; | pub mod piece_list; | ||||||
| mod platform; | pub mod platform; | ||||||
| mod platform_interface; | pub mod platform_interface; | ||||||
| mod print; | pub mod print; | ||||||
| mod reckoner; | pub mod reckoner; | ||||||
| mod run; | pub mod run; | ||||||
| mod sha1_digest; | pub mod sha1_digest; | ||||||
| mod shell; | pub mod shell; | ||||||
| mod sort_key; | pub mod sort_key; | ||||||
| mod sort_order; | pub mod sort_order; | ||||||
| mod sort_spec; | pub mod sort_spec; | ||||||
| mod status; | pub mod status; | ||||||
| mod step; | pub mod step; | ||||||
| mod style; | pub mod style; | ||||||
| mod subcommand; | pub mod subcommand; | ||||||
| mod table; | pub mod table; | ||||||
| mod torrent_summary; | pub mod torrent_summary; | ||||||
| mod use_color; | pub mod use_color; | ||||||
| mod verifier; | pub mod verifier; | ||||||
| mod walker; | pub mod walker; | ||||||
| mod xor_args; | pub mod xor_args; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "bench")] | #[cfg(feature = "bench")] | ||||||
| pub mod bench; | pub mod bench; | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ impl Linter { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn allow(&mut self, allowed: impl IntoIterator<Item = Lint>) { |   pub(crate) fn allow(&mut self, allowed: impl IntoIterator<Item = Lint>) { | ||||||
|     self.allowed.extend(allowed) |     self.allowed.extend(allowed); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn is_allowed(&self, lint: Lint) -> bool { |   pub(crate) fn is_allowed(&self, lint: Lint) -> bool { | ||||||
|  | |||||||
| @ -1,17 +1,17 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, PartialEq)] | #[derive(Debug, PartialEq)] | ||||||
| pub(crate) struct MagnetLink { | pub struct MagnetLink { | ||||||
|   infohash: Infohash, |   pub infohash: Infohash, | ||||||
|   name: Option<String>, |   pub name: Option<String>, | ||||||
|   peers: Vec<HostPort>, |   pub peers: Vec<HostPort>, | ||||||
|   trackers: Vec<Url>, |   pub trackers: Vec<Url>, | ||||||
|   indices: BTreeSet<u64>, |   pub indices: BTreeSet<u64>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl MagnetLink { | impl MagnetLink { | ||||||
|   /// See `Info::infohash_lossy` for details on when this function is lossy.
 |   /// See `Info::infohash_lossy` for details on when this function is lossy.
 | ||||||
|   pub(crate) fn from_metainfo_lossy(metainfo: &Metainfo) -> Result<MagnetLink> { |   pub fn from_metainfo_lossy(metainfo: &Metainfo) -> Result<MagnetLink> { | ||||||
|     let mut link = Self::with_infohash(metainfo.infohash_lossy()?); |     let mut link = Self::with_infohash(metainfo.infohash_lossy()?); | ||||||
| 
 | 
 | ||||||
|     link.set_name(metainfo.info.name.clone()); |     link.set_name(metainfo.info.name.clone()); | ||||||
| @ -23,7 +23,7 @@ impl MagnetLink { | |||||||
|     Ok(link) |     Ok(link) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn with_infohash(infohash: Infohash) -> Self { |   pub fn with_infohash(infohash: Infohash) -> Self { | ||||||
|     MagnetLink { |     MagnetLink { | ||||||
|       infohash, |       infohash, | ||||||
|       name: None, |       name: None, | ||||||
| @ -34,23 +34,23 @@ impl MagnetLink { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[allow(dead_code)] |   #[allow(dead_code)] | ||||||
|   pub(crate) fn set_name(&mut self, name: impl Into<String>) { |   pub fn set_name(&mut self, name: impl Into<String>) { | ||||||
|     self.name = Some(name.into()); |     self.name = Some(name.into()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn add_peer(&mut self, peer: HostPort) { |   pub fn add_peer(&mut self, peer: HostPort) { | ||||||
|     self.peers.push(peer); |     self.peers.push(peer); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn add_tracker(&mut self, tracker: Url) { |   pub fn add_tracker(&mut self, tracker: Url) { | ||||||
|     self.trackers.push(tracker); |     self.trackers.push(tracker); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn add_index(&mut self, index: u64) { |   pub fn add_index(&mut self, index: u64) { | ||||||
|     self.indices.insert(index); |     self.indices.insert(index); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn to_url(&self) -> Url { |   pub fn to_url(&self) -> Url { | ||||||
|     let mut url = Url::parse("magnet:").invariant_unwrap("`magnet:` is valid URL"); |     let mut url = Url::parse("magnet:").invariant_unwrap("`magnet:` is valid URL"); | ||||||
| 
 | 
 | ||||||
|     let mut query = format!("xt=urn:btih:{}", self.infohash); |     let mut query = format!("xt=urn:btih:{}", self.infohash); | ||||||
| @ -85,7 +85,7 @@ impl MagnetLink { | |||||||
|     url |     url | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fn parse(text: &str) -> Result<Self, MagnetLinkParseError> { |   pub fn parse(text: &str) -> Result<Self, MagnetLinkParseError> { | ||||||
|     let url = Url::parse(&text).context(magnet_link_parse_error::Url)?; |     let url = Url::parse(&text).context(magnet_link_parse_error::Url)?; | ||||||
| 
 | 
 | ||||||
|     if url.scheme() != "magnet" { |     if url.scheme() != "magnet" { | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ use crate::common::*; | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Snafu)] | #[derive(Debug, Snafu)] | ||||||
| #[snafu(visibility(pub(crate)))] | #[snafu(visibility(pub(crate)))] | ||||||
| pub(crate) enum MagnetLinkParseError { | pub enum MagnetLinkParseError { | ||||||
|   #[snafu(display("Failed to parse hex string `{}`: {}", text, source))] |   #[snafu(display("Failed to parse hex string `{}`: {}", text, source))] | ||||||
|   HexParse { |   HexParse { | ||||||
|     text: String, |     text: String, | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[serde(transparent)] |  | ||||||
| #[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)] | #[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Copy, Clone)] | ||||||
|  | #[serde(transparent)] | ||||||
| pub(crate) struct Md5Digest { | pub(crate) struct Md5Digest { | ||||||
|   #[serde(with = "SerHex::<serde_hex::Strict>")] |   #[serde(with = "SerHex::<serde_hex::Strict>")] | ||||||
|   bytes: [u8; 16], |   bytes: [u8; 16], | ||||||
|  | |||||||
| @ -1,68 +1,68 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | ||||||
| pub(crate) struct Metainfo { | pub struct Metainfo { | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) announce: Option<String>, |   pub announce: Option<String>, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     rename = "announce-list", |     rename = "announce-list", | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) announce_list: Option<Vec<Vec<String>>>, |   pub announce_list: Option<Vec<Vec<String>>>, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) comment: Option<String>, |   pub comment: Option<String>, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     rename = "created by", |     rename = "created by", | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) created_by: Option<String>, |   pub created_by: Option<String>, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     rename = "creation date", |     rename = "creation date", | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) creation_date: Option<u64>, |   pub creation_date: Option<u64>, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) encoding: Option<String>, |   pub encoding: Option<String>, | ||||||
|   pub(crate) info: Info, |   pub info: Info, | ||||||
|   #[serde(
 |   #[serde(
 | ||||||
|     skip_serializing_if = "Option::is_none", |     skip_serializing_if = "Option::is_none", | ||||||
|     default, |     default, | ||||||
|     with = "unwrap_or_skip" |     with = "unwrap_or_skip" | ||||||
|   )] |   )] | ||||||
|   pub(crate) nodes: Option<Vec<HostPort>>, |   pub nodes: Option<Vec<HostPort>>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Metainfo { | impl Metainfo { | ||||||
|   pub(crate) fn from_input(input: &Input) -> Result<Metainfo> { |   pub fn from_input(input: &Input) -> Result<Metainfo> { | ||||||
|     Self::deserialize(input.source(), input.data()) |     Self::deserialize(input.source(), input.data()) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> { |   pub fn deserialize(source: &InputTarget, data: &[u8]) -> Result<Metainfo, Error> { | ||||||
|     let metainfo = bendy::serde::de::from_bytes(&data).context(error::MetainfoDeserialize { |     let metainfo = bendy::serde::de::from_bytes(&data).context(error::MetainfoDeserialize { | ||||||
|       input: source.clone(), |       input: source.clone(), | ||||||
|     })?; |     })?; | ||||||
|     Ok(metainfo) |     Ok(metainfo) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn serialize(&self) -> Result<Vec<u8>, Error> { |   pub fn serialize(&self) -> Result<Vec<u8>, Error> { | ||||||
|     bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize) |     bendy::serde::ser::to_bytes(&self).context(error::MetainfoSerialize) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -99,7 +99,7 @@ impl Metainfo { | |||||||
|     self.info.content_size() |     self.info.content_size() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a { |   pub fn trackers<'a>(&'a self) -> impl Iterator<Item = Result<Url>> + 'a { | ||||||
|     let mut seen = HashSet::new(); |     let mut seen = HashSet::new(); | ||||||
|     iter::once(&self.announce) |     iter::once(&self.announce) | ||||||
|       .flatten() |       .flatten() | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Copy, Clone)] | #[derive(Debug, Copy, Clone)] | ||||||
| pub(crate) enum MetainfoError { | pub enum MetainfoError { | ||||||
|   Type, |   Type, | ||||||
|   InfoMissing, |   InfoMissing, | ||||||
|   InfoType, |   InfoType, | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[serde(untagged)] |  | ||||||
| #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | #[derive(Deserialize, Serialize, Debug, PartialEq, Clone)] | ||||||
|  | #[serde(untagged)] | ||||||
| pub(crate) enum Mode { | pub(crate) enum Mode { | ||||||
|   Single { |   Single { | ||||||
|     length: Bytes, |     length: Bytes, | ||||||
|  | |||||||
| @ -1,10 +1,10 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct OutputStream { | pub(crate) struct OutputStream { | ||||||
|  |   active: bool, | ||||||
|   stream: Box<dyn Write>, |   stream: Box<dyn Write>, | ||||||
|   style: bool, |   style: bool, | ||||||
|   term: bool, |   term: bool, | ||||||
|   active: bool, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl OutputStream { | impl OutputStream { | ||||||
|  | |||||||
| @ -1,22 +1,22 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| #[derive(Debug, Eq, PartialEq, Copy, Clone)] | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||||||
| pub(crate) struct Sha1Digest { | pub struct Sha1Digest { | ||||||
|   bytes: [u8; Self::LENGTH], |   pub bytes: [u8; Self::LENGTH], | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Sha1Digest { | impl Sha1Digest { | ||||||
|   pub(crate) const LENGTH: usize = 20; |   pub const LENGTH: usize = 20; | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self { |   pub fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self { | ||||||
|     Sha1Digest { bytes } |     Sha1Digest { bytes } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn bytes(self) -> [u8; Self::LENGTH] { |   pub fn bytes(self) -> [u8; Self::LENGTH] { | ||||||
|     self.bytes |     self.bytes | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn from_data(data: impl AsRef<[u8]>) -> Self { |   pub fn from_data(data: impl AsRef<[u8]>) -> Self { | ||||||
|     Sha1::from(data).digest().into() |     Sha1::from(data).digest().into() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ use structopt::clap; | |||||||
| 
 | 
 | ||||||
| #[derive(Copy, Clone, EnumVariantNames, IntoStaticStr, EnumString, EnumIter, Debug)] | #[derive(Copy, Clone, EnumVariantNames, IntoStaticStr, EnumString, EnumIter, Debug)] | ||||||
| #[strum(serialize_all = "kebab-case")] | #[strum(serialize_all = "kebab-case")] | ||||||
| pub(crate) enum Shell { | pub enum Shell { | ||||||
|   Zsh, |   Zsh, | ||||||
|   Bash, |   Bash, | ||||||
|   Fish, |   Fish, | ||||||
| @ -62,6 +62,6 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
|   fn variants() { |   fn variants() { | ||||||
|     assert_eq!(Shell::VARIANTS, clap::Shell::variants()) |     assert_eq!(Shell::VARIANTS, clap::Shell::variants()); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -338,9 +338,7 @@ impl Create { | |||||||
| 
 | 
 | ||||||
|     if let OutputTarget::Path(path) = &output { |     if let OutputTarget::Path(path) = &output { | ||||||
|       if !self.force && path.exists() { |       if !self.force && path.exists() { | ||||||
|         return Err(Error::OutputExists { |         return Err(Error::OutputExists { path: path.clone() }); | ||||||
|           path: path.to_owned(), |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -1151,7 +1149,7 @@ mod tests { | |||||||
|         length: Bytes(3), |         length: Bytes(3), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1182,7 +1180,7 @@ mod tests { | |||||||
|         length: Bytes(4), |         length: Bytes(4), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1213,7 +1211,7 @@ mod tests { | |||||||
|         length: Bytes(4), |         length: Bytes(4), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1258,7 +1256,7 @@ mod tests { | |||||||
|           }, |           }, | ||||||
|         ], |         ], | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1285,7 +1283,7 @@ mod tests { | |||||||
|         length: Bytes(3), |         length: Bytes(3), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1319,7 +1317,7 @@ mod tests { | |||||||
|         length: Bytes(3), |         length: Bytes(3), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1346,7 +1344,7 @@ mod tests { | |||||||
|         length: Bytes(0), |         length: Bytes(0), | ||||||
|         md5sum: None, |         md5sum: None, | ||||||
|       } |       } | ||||||
|     ) |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
| @ -1367,7 +1365,7 @@ mod tests { | |||||||
|     env.assert_ok(); |     env.assert_ok(); | ||||||
|     let metainfo = env.load_metainfo("foo.torrent"); |     let metainfo = env.load_metainfo("foo.torrent"); | ||||||
|     assert_eq!(metainfo.info.pieces.count(), 0); |     assert_eq!(metainfo.info.pieces.count(), 0); | ||||||
|     assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() }) |     assert_eq!(metainfo.info.mode, Mode::Multiple { files: Vec::new() }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   #[test] |   #[test] | ||||||
|  | |||||||
| @ -16,7 +16,19 @@ const INPUT_FLAG: &str = "input-flag"; | |||||||
|   version_message(consts::VERSION_MESSAGE), |   version_message(consts::VERSION_MESSAGE), | ||||||
|   about("Verify files against a .torrent file.") |   about("Verify files against a .torrent file.") | ||||||
| )] | )] | ||||||
|  | #[cfg_attr(test, structopt(setting = AppSettings::ColorNever))] | ||||||
| pub(crate) struct Verify { | pub(crate) struct Verify { | ||||||
|  |   #[structopt(
 | ||||||
|  |     long = "base-directory", | ||||||
|  |     short = "b", | ||||||
|  |     value_name = "BASE-DIRECTORY", | ||||||
|  |     conflicts_with = "content", | ||||||
|  |     empty_values(false), | ||||||
|  |     parse(from_os_str), | ||||||
|  |     help = "Look for torrent content in `BASE-DIRECTORY`/`NAME`, where `NAME` is the `name` field \ | ||||||
|  |             of the torrent info dictionary." | ||||||
|  |   )] | ||||||
|  |   base_directory: Option<PathBuf>, | ||||||
|   #[structopt(
 |   #[structopt(
 | ||||||
|     long = "content", |     long = "content", | ||||||
|     short = "c", |     short = "c", | ||||||
| @ -24,7 +36,7 @@ pub(crate) struct Verify { | |||||||
|     empty_values(false), |     empty_values(false), | ||||||
|     parse(from_os_str), |     parse(from_os_str), | ||||||
|     help = "Verify torrent content at `PATH` against torrent metainfo. Defaults to `name` field \ |     help = "Verify torrent content at `PATH` against torrent metainfo. Defaults to `name` field \ | ||||||
|             of torrent info dictionary." |             of the torrent info dictionary." | ||||||
|   )] |   )] | ||||||
|   content: Option<PathBuf>, |   content: Option<PathBuf>, | ||||||
|   #[structopt(
 |   #[structopt(
 | ||||||
| @ -64,13 +76,19 @@ impl Verify { | |||||||
| 
 | 
 | ||||||
|     let metainfo = Metainfo::from_input(&input)?; |     let metainfo = Metainfo::from_input(&input)?; | ||||||
| 
 | 
 | ||||||
|     let content = self.content.as_ref().map_or_else( |     let content = self | ||||||
|       || match target { |       .content | ||||||
|  |       .as_ref() | ||||||
|  |       .cloned() | ||||||
|  |       .or_else(|| { | ||||||
|  |         self | ||||||
|  |           .base_directory | ||||||
|  |           .map(|base_directory| base_directory.join(&metainfo.info.name).lexiclean()) | ||||||
|  |       }) | ||||||
|  |       .unwrap_or_else(|| match target { | ||||||
|         InputTarget::Path(path) => path.join("..").join(&metainfo.info.name).lexiclean(), |         InputTarget::Path(path) => path.join("..").join(&metainfo.info.name).lexiclean(), | ||||||
|         InputTarget::Stdin => PathBuf::from(&metainfo.info.name), |         InputTarget::Stdin => PathBuf::from(&metainfo.info.name), | ||||||
|       }, |       }); | ||||||
|       PathBuf::clone, |  | ||||||
|     ); |  | ||||||
| 
 | 
 | ||||||
|     let progress_bar = if env.err().is_styled_term() && !options.quiet { |     let progress_bar = if env.err().is_styled_term() && !options.quiet { | ||||||
|       let style = ProgressStyle::default_bar() |       let style = ProgressStyle::default_bar() | ||||||
| @ -137,6 +155,20 @@ mod tests { | |||||||
|     assert_matches!(env.run(), Err(Error::Clap { .. })); |     assert_matches!(env.run(), Err(Error::Clap { .. })); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   #[test] | ||||||
|  |   fn base_directory_conflicts_with_content() { | ||||||
|  |     let mut env = test_env! { | ||||||
|  |       args: ["torrent", "verify", "foo.torrent", "--content", "foo", "--base-directory", "dir"], | ||||||
|  |       tree: {}, | ||||||
|  |     }; | ||||||
|  |     assert_eq!( | ||||||
|  |       env.run().unwrap_err().to_string(), | ||||||
|  |       "error: The argument '--content <PATH>' cannot be used with '--base-directory \ | ||||||
|  |        <BASE-DIRECTORY>'\n\nUSAGE:\n    imdl torrent verify <INPUT> --base-directory \ | ||||||
|  |        <BASE-DIRECTORY> --content <PATH>\n\nFor more information try --help\n" | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   #[test] |   #[test] | ||||||
|   fn pass() -> Result<()> { |   fn pass() -> Result<()> { | ||||||
|     let mut create_env = test_env! { |     let mut create_env = test_env! { | ||||||
| @ -323,6 +355,62 @@ mod tests { | |||||||
|     Ok(()) |     Ok(()) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   #[test] | ||||||
|  |   fn base_directory() -> Result<()> { | ||||||
|  |     let mut create_env = test_env! { | ||||||
|  |       args: [ | ||||||
|  |         "torrent", | ||||||
|  |         "create", | ||||||
|  |         "--input", | ||||||
|  |         "foo", | ||||||
|  |         "--announce", | ||||||
|  |         "https://bar", | ||||||
|  |       ], | ||||||
|  |       tree: { | ||||||
|  |         foo: { | ||||||
|  |           a: "abc", | ||||||
|  |           d: "efg", | ||||||
|  |           h: "ijk", | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     create_env.assert_ok(); | ||||||
|  | 
 | ||||||
|  |     create_env.create_dir("dir"); | ||||||
|  | 
 | ||||||
|  |     create_env.rename("foo", "dir/foo"); | ||||||
|  | 
 | ||||||
|  |     let torrent = create_env.resolve("foo.torrent")?; | ||||||
|  |     let dir = create_env.resolve("dir")?; | ||||||
|  | 
 | ||||||
|  |     let mut verify_env = test_env! { | ||||||
|  |       args: [ | ||||||
|  |         "torrent", | ||||||
|  |         "verify", | ||||||
|  |         "--input", | ||||||
|  |         &torrent, | ||||||
|  |         "--base-directory", | ||||||
|  |         &dir, | ||||||
|  |       ], | ||||||
|  |       tree: {}, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     verify_env.assert_ok(); | ||||||
|  | 
 | ||||||
|  |     let want = format!( | ||||||
|  |       "[1/2] \u{1F4BE} Loading metainfo from `{}`…\n[2/2] \u{1F9EE} Verifying pieces from \ | ||||||
|  |        `{}`…\n\u{2728}\u{2728} Verification succeeded! \u{2728}\u{2728}\n",
 | ||||||
|  |       torrent.display(), | ||||||
|  |       dir.join("foo").display(), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     assert_eq!(verify_env.err(), want); | ||||||
|  |     assert_eq!(verify_env.out(), ""); | ||||||
|  | 
 | ||||||
|  |     Ok(()) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   #[test] |   #[test] | ||||||
|   fn verify_stdin() -> Result<()> { |   fn verify_stdin() -> Result<()> { | ||||||
|     let mut create_env = test_env! { |     let mut create_env = test_env! { | ||||||
|  | |||||||
| @ -37,19 +37,19 @@ macro_rules! test_env { | |||||||
| 
 | 
 | ||||||
| pub(crate) struct TestEnv { | pub(crate) struct TestEnv { | ||||||
|   env: Env, |   env: Env, | ||||||
|   #[allow(unused)] |  | ||||||
|   tempdir: TempDir, |  | ||||||
|   err: Capture, |   err: Capture, | ||||||
|   out: Capture, |   out: Capture, | ||||||
|  |   #[allow(unused)] | ||||||
|  |   tempdir: TempDir, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl TestEnv { | impl TestEnv { | ||||||
|   pub(crate) fn new(tempdir: TempDir, env: Env, err: Capture, out: Capture) -> TestEnv { |   pub(crate) fn new(tempdir: TempDir, env: Env, err: Capture, out: Capture) -> TestEnv { | ||||||
|     Self { |     Self { | ||||||
|       tempdir, |  | ||||||
|       err, |  | ||||||
|       env, |       env, | ||||||
|  |       err, | ||||||
|       out, |       out, | ||||||
|  |       tempdir, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -77,6 +77,14 @@ impl TestEnv { | |||||||
|     fs::create_dir(self.env.resolve(path).unwrap()).unwrap(); |     fs::create_dir(self.env.resolve(path).unwrap()).unwrap(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   pub(crate) fn rename(&self, from: impl AsRef<Path>, to: impl AsRef<Path>) { | ||||||
|  |     fs::rename( | ||||||
|  |       self.env.resolve(from).unwrap(), | ||||||
|  |       self.env.resolve(to).unwrap(), | ||||||
|  |     ) | ||||||
|  |     .unwrap(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   pub(crate) fn read_to_string(&self, path: impl AsRef<Path>) -> String { |   pub(crate) fn read_to_string(&self, path: impl AsRef<Path>) -> String { | ||||||
|     fs::read_to_string(self.env.resolve(path).unwrap()).unwrap() |     fs::read_to_string(self.env.resolve(path).unwrap()).unwrap() | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,30 +1,30 @@ | |||||||
| use crate::common::*; | use crate::common::*; | ||||||
| 
 | 
 | ||||||
| pub(crate) struct TorrentSummary { | pub struct TorrentSummary { | ||||||
|   infohash: Infohash, |   infohash: Infohash, | ||||||
|   metainfo: Metainfo, |   metainfo: Metainfo, | ||||||
|   size: Bytes, |   size: Bytes, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Serialize)] | #[derive(Serialize)] | ||||||
| pub(crate) struct TorrentSummaryJson { | pub struct TorrentSummaryJson { | ||||||
|   name: String, |   pub name: String, | ||||||
|   comment: Option<String>, |   pub comment: Option<String>, | ||||||
|   creation_date: Option<u64>, |   pub creation_date: Option<u64>, | ||||||
|   created_by: Option<String>, |   pub created_by: Option<String>, | ||||||
|   source: Option<String>, |   pub source: Option<String>, | ||||||
|   info_hash: String, |   pub info_hash: String, | ||||||
|   torrent_size: u64, |   pub torrent_size: u64, | ||||||
|   content_size: u64, |   pub content_size: u64, | ||||||
|   private: bool, |   pub private: bool, | ||||||
|   tracker: Option<String>, |   pub tracker: Option<String>, | ||||||
|   announce_list: Vec<Vec<String>>, |   pub announce_list: Vec<Vec<String>>, | ||||||
|   update_url: Option<String>, |   pub update_url: Option<String>, | ||||||
|   dht_nodes: Vec<String>, |   pub dht_nodes: Vec<String>, | ||||||
|   piece_size: u64, |   pub piece_size: u64, | ||||||
|   piece_count: usize, |   pub piece_count: usize, | ||||||
|   file_count: usize, |   pub file_count: usize, | ||||||
|   files: Vec<String>, |   pub files: Vec<String>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl TorrentSummary { | impl TorrentSummary { | ||||||
| @ -37,14 +37,14 @@ impl TorrentSummary { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /// See `Info::infohash_lossy` for details on when this function is lossy.
 |   /// See `Info::infohash_lossy` for details on when this function is lossy.
 | ||||||
|   pub(crate) fn from_metainfo_lossy(metainfo: Metainfo) -> Result<Self> { |   pub fn from_metainfo_lossy(metainfo: Metainfo) -> Result<Self> { | ||||||
|     let bytes = metainfo.serialize()?; |     let bytes = metainfo.serialize()?; | ||||||
|     let size = Bytes(bytes.len().into_u64()); |     let size = Bytes(bytes.len().into_u64()); | ||||||
|     let infohash = metainfo.infohash_lossy()?; |     let infohash = metainfo.infohash_lossy()?; | ||||||
|     Ok(Self::new(metainfo, infohash, size)) |     Ok(Self::new(metainfo, infohash, size)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn from_input(input: &Input) -> Result<Self> { |   pub fn from_input(input: &Input) -> Result<Self> { | ||||||
|     let metainfo = Metainfo::from_input(input)?; |     let metainfo = Metainfo::from_input(input)?; | ||||||
|     let infohash = Infohash::from_input(input)?; |     let infohash = Infohash::from_input(input)?; | ||||||
|     let size = Bytes(input.data().len().into_u64()); |     let size = Bytes(input.data().len().into_u64()); | ||||||
| @ -52,7 +52,7 @@ impl TorrentSummary { | |||||||
|     Ok(Self::new(metainfo, infohash, size)) |     Ok(Self::new(metainfo, infohash, size)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn write(&self, env: &mut Env) -> Result<()> { |   pub fn write(&self, env: &mut Env) -> Result<()> { | ||||||
|     let table = self.table(); |     let table = self.table(); | ||||||
| 
 | 
 | ||||||
|     if env.out().is_term() { |     if env.out().is_term() { | ||||||
| @ -168,16 +168,16 @@ impl TorrentSummary { | |||||||
|     table |     table | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   pub(crate) fn write_json(&self, env: &mut Env) -> Result<()> { |   pub fn write_json(&self, env: &mut Env) -> Result<()> { | ||||||
|     let data = self.torrent_summary_data(); |     let data = self.torrent_summary_data(); | ||||||
|     let json = serde_json::to_string(&data).context(error::JsonSerialize)?; |     let json = serde_json::to_string(&data).context(error::JsonSerialize)?; | ||||||
|     outln!(env, "{}", json)?; |     outln!(env, "{}", json)?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   fn torrent_summary_data(&self) -> TorrentSummaryJson { |   pub fn torrent_summary_data(&self) -> TorrentSummaryJson { | ||||||
|     let (file_count, files) = match &self.metainfo.info.mode { |     let (file_count, files) = match &self.metainfo.info.mode { | ||||||
|       Mode::Single { .. } => (1, vec![self.metainfo.info.name.to_owned()]), |       Mode::Single { .. } => (1, vec![self.metainfo.info.name.clone()]), | ||||||
|       Mode::Multiple { files } => ( |       Mode::Multiple { files } => ( | ||||||
|         files.len(), |         files.len(), | ||||||
|         files |         files | ||||||
| @ -197,7 +197,7 @@ impl TorrentSummary { | |||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     TorrentSummaryJson { |     TorrentSummaryJson { | ||||||
|       name: self.metainfo.info.name.to_owned(), |       name: self.metainfo.info.name.clone(), | ||||||
|       comment: self.metainfo.comment.clone(), |       comment: self.metainfo.comment.clone(), | ||||||
|       creation_date: self.metainfo.creation_date, |       creation_date: self.metainfo.creation_date, | ||||||
|       created_by: self.metainfo.created_by.clone(), |       created_by: self.metainfo.created_by.clone(), | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user