Split up a couple large functions

This commit is contained in:
Igor Katson 2024-02-28 20:27:45 +00:00
parent 340d54eafa
commit 5488e1d40f
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
2 changed files with 88 additions and 71 deletions

View file

@ -25,6 +25,7 @@ use buffers::{ByteBuf, ByteBufT, ByteString};
use clone_to_owned::CloneToOwned; use clone_to_owned::CloneToOwned;
use dht::{Dht, DhtBuilder, DhtConfig, Id20, PersistentDht, PersistentDhtConfig}; use dht::{Dht, DhtBuilder, DhtConfig, Id20, PersistentDht, PersistentDhtConfig};
use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, TryFutureExt}; use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, TryFutureExt};
use itertools::Itertools;
use librqbit_core::{ use librqbit_core::{
directories::get_configuration_directory, directories::get_configuration_directory,
magnet::Magnet, magnet::Magnet,
@ -181,7 +182,7 @@ async fn torrent_from_url(url: &str) -> anyhow::Result<TorrentMetaV1Owned> {
.await .await
.context("error downloading torrent metadata")?; .context("error downloading torrent metadata")?;
if !response.status().is_success() { if !response.status().is_success() {
anyhow::bail!("GET {} returned {}", url, response.status()) bail!("GET {} returned {}", url, response.status())
} }
let b = response let b = response
.bytes() .bytes()
@ -190,7 +191,7 @@ async fn torrent_from_url(url: &str) -> anyhow::Result<TorrentMetaV1Owned> {
torrent_from_bytes(&b).context("error decoding torrent") torrent_from_bytes(&b).context("error decoding torrent")
} }
fn compute_only_files<ByteBuf: AsRef<[u8]>>( fn compute_only_files_regex<ByteBuf: AsRef<[u8]>>(
torrent: &TorrentMetaV1Info<ByteBuf>, torrent: &TorrentMetaV1Info<ByteBuf>,
filename_re: &str, filename_re: &str,
) -> anyhow::Result<Vec<usize>> { ) -> anyhow::Result<Vec<usize>> {
@ -205,11 +206,46 @@ fn compute_only_files<ByteBuf: AsRef<[u8]>>(
} }
} }
if only_files.is_empty() { if only_files.is_empty() {
anyhow::bail!("none of the filenames match the given regex") bail!("none of the filenames match the given regex")
} }
Ok(only_files) Ok(only_files)
} }
fn compute_only_files(
info: &TorrentMetaV1Info<ByteString>,
only_files: Option<Vec<usize>>,
only_files_regex: Option<String>,
list_only: bool,
) -> anyhow::Result<Option<Vec<usize>>> {
match (only_files, only_files_regex) {
(Some(_), Some(_)) => {
bail!("only_files and only_files_regex are mutually exclusive");
}
(Some(only_files), None) => {
let total_files = info.iter_file_lengths()?.count();
for id in only_files.iter().copied() {
if id >= total_files {
bail!("file id {} is out of range", id);
}
}
Ok(Some(only_files))
}
(None, Some(filename_re)) => {
let only_files = compute_only_files_regex(info, &filename_re)?;
for (idx, (filename, _)) in info.iter_filenames_and_lengths()?.enumerate() {
if !only_files.contains(&idx) {
continue;
}
if !list_only {
info!(?filename, "will download");
}
}
Ok(Some(only_files))
}
(None, None) => Ok(None),
}
}
/// Options for adding new torrents to the session. /// Options for adding new torrents to the session.
#[serde_as] #[serde_as]
#[derive(Default, Clone, Serialize, Deserialize)] #[derive(Default, Clone, Serialize, Deserialize)]
@ -797,14 +833,14 @@ impl Session {
{ {
ReadMetainfoResult::Found { info, rx, seen } => (info, rx, seen), ReadMetainfoResult::Found { info, rx, seen } => (info, rx, seen),
ReadMetainfoResult::ChannelClosed { .. } => { ReadMetainfoResult::ChannelClosed { .. } => {
anyhow::bail!("DHT died, no way to discover torrent metainfo") bail!("DHT died, no way to discover torrent metainfo")
} }
}; };
debug!(?info, "received result from DHT"); debug!(?info, "received result from DHT");
( (
info_hash, info_hash,
info, info,
magnet.trackers, magnet.trackers.into_iter().unique().collect(),
Some(peer_rx), Some(peer_rx),
initial_peers, initial_peers,
) )
@ -830,6 +866,7 @@ impl Session {
let trackers = torrent let trackers = torrent
.iter_announce() .iter_announce()
.unique()
.filter_map(|tracker| match std::str::from_utf8(tracker.as_ref()) { .filter_map(|tracker| match std::str::from_utf8(tracker.as_ref()) {
Ok(url) => Some(url.to_owned()), Ok(url) => Some(url.to_owned()),
Err(_) => { Err(_) => {
@ -877,6 +914,33 @@ impl Session {
.boxed() .boxed()
} }
fn get_default_subfolder_for_torrent(
&self,
info: &TorrentMetaV1Info<ByteString>,
) -> anyhow::Result<Option<PathBuf>> {
let files = info
.iter_filenames_and_lengths()?
.map(|(f, l)| Ok((f.to_pathbuf()?, l)))
.collect::<anyhow::Result<Vec<(PathBuf, u64)>>>()?;
if files.len() < 2 {
return Ok(None);
}
if let Some(name) = &info.name {
let s =
std::str::from_utf8(name.as_slice()).context("invalid UTF-8 in torrent name")?;
return Ok(Some(PathBuf::from(s)));
};
// Let the subfolder name be the longest filename
let longest = files
.iter()
.max_by_key(|(_, l)| l)
.unwrap()
.0
.file_stem()
.context("can't determine longest filename")?;
Ok::<_, anyhow::Error>(Some(PathBuf::from(longest)))
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
async fn main_torrent_info( async fn main_torrent_info(
&self, &self,
@ -889,67 +953,18 @@ impl Session {
) -> anyhow::Result<AddTorrentResponse> { ) -> anyhow::Result<AddTorrentResponse> {
debug!("Torrent info: {:#?}", &info); debug!("Torrent info: {:#?}", &info);
let get_only_files = let only_files = compute_only_files(
|only_files: Option<Vec<usize>>, only_files_regex: Option<String>, list_only: bool| { &info,
match (only_files, only_files_regex) { opts.only_files,
(Some(_), Some(_)) => { opts.only_files_regex,
bail!("only_files and only_files_regex are mutually exclusive"); opts.list_only,
} )?;
(Some(only_files), None) => {
let total_files = info.iter_file_lengths()?.count();
for id in only_files.iter().copied() {
if id >= total_files {
anyhow::bail!("file id {} is out of range", id);
}
}
Ok(Some(only_files))
}
(None, Some(filename_re)) => {
let only_files = compute_only_files(&info, &filename_re)?;
for (idx, (filename, _)) in info.iter_filenames_and_lengths()?.enumerate() {
if !only_files.contains(&idx) {
continue;
}
if !list_only {
info!(?filename, "will download");
}
}
Ok(Some(only_files))
}
(None, None) => Ok(None),
}
};
let only_files = get_only_files(opts.only_files, opts.only_files_regex, opts.list_only)?;
let get_default_subfolder = || {
let files = info
.iter_filenames_and_lengths()?
.map(|(f, l)| Ok((f.to_pathbuf()?, l)))
.collect::<anyhow::Result<Vec<(PathBuf, u64)>>>()?;
if files.len() < 2 {
return Ok(None);
}
if let Some(name) = &info.name {
let s = std::str::from_utf8(name.as_slice())
.context("invalid UTF-8 in torrent name")?;
return Ok(Some(PathBuf::from(s)));
};
// Let the subfolder name be the longest filename
let longest = files
.iter()
.max_by_key(|(_, l)| l)
.unwrap()
.0
.file_stem()
.context("can't determine longest filename")?;
Ok::<_, anyhow::Error>(Some(PathBuf::from(longest)))
};
let output_folder = match (opts.output_folder, opts.sub_folder) { let output_folder = match (opts.output_folder, opts.sub_folder) {
(None, None) => self (None, None) => self.output_folder.join(
.output_folder self.get_default_subfolder_for_torrent(&info)?
.join(get_default_subfolder()?.unwrap_or_default()), .unwrap_or_default(),
),
(Some(o), None) => PathBuf::from(o), (Some(o), None) => PathBuf::from(o),
(Some(_), Some(_)) => bail!("you can't provide both output_folder and sub_folder"), (Some(_), Some(_)) => bail!("you can't provide both output_folder and sub_folder"),
(None, Some(s)) => self.output_folder.join(s), (None, Some(s)) => self.output_folder.join(s),
@ -1043,10 +1058,9 @@ impl Session {
.context("error pausing torrent"); .context("error pausing torrent");
match (paused, delete_files) { match (paused, delete_files) {
(Err(e), true) => Err(e).context("torrent deleted, but could not delete files"), (Err(e), true) => return Err(e).context("torrent deleted, but could not delete files"),
(Err(e), false) => { (Err(e), false) => {
warn!(error=?e, "could not delete torrent files"); warn!(error=?e, "error deleting torrent cleanly");
Ok(())
} }
(Ok(Some(paused)), true) => { (Ok(Some(paused)), true) => {
drop(paused.files); drop(paused.files);
@ -1055,10 +1069,10 @@ impl Session {
warn!(?file, error=?e, "could not delete file"); warn!(?file, error=?e, "could not delete file");
} }
} }
Ok(())
} }
_ => Ok(()), _ => {}
} };
Ok(())
} }
// Get a peer stream from both DHT and trackers. // Get a peer stream from both DHT and trackers.

View file

@ -49,7 +49,10 @@ pub struct TorrentMetaV1<BufType> {
impl<BufType> TorrentMetaV1<BufType> { impl<BufType> TorrentMetaV1<BufType> {
pub fn iter_announce(&self) -> impl Iterator<Item = &BufType> { pub fn iter_announce(&self) -> impl Iterator<Item = &BufType> {
once(&self.announce).chain(self.announce_list.iter().flatten()) if self.announce_list.iter().flatten().next().is_some() {
return itertools::Either::Left(self.announce_list.iter().flatten());
}
itertools::Either::Right(once(&self.announce))
} }
} }