Skip to content

Commit

Permalink
Introduce "sort specs" to allow fine-grained sorting of files in torr…
Browse files Browse the repository at this point in the history
…ents

Sort specs are of the form `KEY:ORDER`, and allow sorting files in a
torrent by multiple criteria. Multiple sort specs can be passed with
`--sort-by` upon torrent creation.

type: added
  • Loading branch information
casey committed Apr 8, 2020
1 parent 362a81d commit 9701803
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 153 deletions.
23 changes: 10 additions & 13 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ pub(crate) use serde_with::rust::unwrap_or_skip;
pub(crate) use sha1::Sha1;
pub(crate) use snafu::{ResultExt, Snafu};
pub(crate) use static_assertions::const_assert;
pub(crate) use structopt::{
clap::{AppSettings, ArgSettings},
StructOpt,
};
pub(crate) use structopt::{clap::AppSettings, StructOpt};
pub(crate) use strum::VariantNames;
pub(crate) use strum_macros::{EnumString, EnumVariantNames, IntoStaticStr};
pub(crate) use unicode_width::UnicodeWidthStr;
Expand All @@ -61,15 +58,15 @@ pub(crate) use crate::{
// structs and enums
pub(crate) use crate::{
arguments::Arguments, bytes::Bytes, env::Env, error::Error, file_error::FileError,
file_info::FileInfo, file_order::FileOrder, file_path::FilePath, file_status::FileStatus,
files::Files, hasher::Hasher, host_port::HostPort, host_port_parse_error::HostPortParseError,
info::Info, infohash::Infohash, input::Input, input_target::InputTarget, lint::Lint,
linter::Linter, magnet_link::MagnetLink, md5_digest::Md5Digest, metainfo::Metainfo,
metainfo_error::MetainfoError, mode::Mode, options::Options, output_stream::OutputStream,
output_target::OutputTarget, piece_length_picker::PieceLengthPicker, piece_list::PieceList,
platform::Platform, sha1_digest::Sha1Digest, status::Status, style::Style,
subcommand::Subcommand, table::Table, torrent_summary::TorrentSummary, use_color::UseColor,
verifier::Verifier, walker::Walker,
file_info::FileInfo, file_path::FilePath, file_status::FileStatus, files::Files, hasher::Hasher,
host_port::HostPort, host_port_parse_error::HostPortParseError, info::Info, infohash::Infohash,
input::Input, input_target::InputTarget, lint::Lint, linter::Linter, magnet_link::MagnetLink,
md5_digest::Md5Digest, metainfo::Metainfo, metainfo_error::MetainfoError, mode::Mode,
options::Options, output_stream::OutputStream, output_target::OutputTarget,
piece_length_picker::PieceLengthPicker, piece_list::PieceList, platform::Platform,
sha1_digest::Sha1Digest, sort_key::SortKey, sort_order::SortOrder, sort_spec::SortSpec,
status::Status, style::Style, subcommand::Subcommand, table::Table,
torrent_summary::TorrentSummary, use_color::UseColor, verifier::Verifier, walker::Walker,
};

// type aliases
Expand Down
110 changes: 0 additions & 110 deletions src/file_order.rs

This file was deleted.

4 changes: 3 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ mod env;
mod error;
mod file_error;
mod file_info;
mod file_order;
mod file_path;
mod file_status;
mod files;
Expand Down Expand Up @@ -87,6 +86,9 @@ mod platform_interface;
mod print;
mod reckoner;
mod sha1_digest;
mod sort_key;
mod sort_order;
mod sort_spec;
mod status;
mod step;
mod style;
Expand Down
14 changes: 14 additions & 0 deletions src/sort_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::common::*;

#[derive(Clone, Copy, Debug, PartialEq, IntoStaticStr, EnumString)]
#[strum(serialize_all = "kebab-case")]
pub(crate) enum SortKey {
Path,
Size,
}

impl SortKey {
pub(crate) fn name(self) -> &'static str {
self.into()
}
}
20 changes: 20 additions & 0 deletions src/sort_order.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use crate::common::*;

#[derive(Clone, Copy, Debug, PartialEq, IntoStaticStr, EnumString)]
#[strum(serialize_all = "kebab-case")]
pub(crate) enum SortOrder {
Ascending,
Descending,
}

impl SortOrder {
pub(crate) fn name(self) -> &'static str {
self.into()
}
}

impl Default for SortOrder {
fn default() -> Self {
Self::Ascending
}
}
95 changes: 95 additions & 0 deletions src/sort_spec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::common::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct SortSpec {
key: SortKey,
order: SortOrder,
}

impl SortSpec {
pub(crate) fn compare(specs: &[SortSpec], a: &FileInfo, b: &FileInfo) -> Ordering {
let mut specs = specs.to_vec();

specs.push(SortSpec::default());

Self::compare_specs(&specs, a, b)
}

fn compare_specs(specs: &[SortSpec], a: &FileInfo, b: &FileInfo) -> Ordering {
specs.iter().fold(Ordering::Equal, |ordering, spec| {
ordering.then_with(|| spec.compare_file_info(a, b))
})
}

fn compare_file_info(self, a: &FileInfo, b: &FileInfo) -> Ordering {
let ordering = match self.key {
SortKey::Path => a.path.cmp(&b.path),
SortKey::Size => a.length.cmp(&b.length),
};

match self.order {
SortOrder::Ascending => ordering,
SortOrder::Descending => ordering.reverse(),
}
}
}

impl Default for SortSpec {
fn default() -> Self {
Self {
key: SortKey::Path,
order: SortOrder::default(),
}
}
}

impl FromStr for SortSpec {
type Err = strum::ParseError;

fn from_str(text: &str) -> Result<Self, Self::Err> {
if let Some(index) = text.find(':') {
Ok(SortSpec {
key: text[..index].parse()?,
order: text[index + 1..].parse()?,
})
} else {
Ok(SortSpec {
key: text.parse()?,
order: SortOrder::default(),
})
}
}
}

impl Display for SortSpec {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}:{}", self.key.name(), self.order.name())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn default() {
assert_eq!(
SortSpec::default(),
SortSpec {
key: SortKey::Path,
order: SortOrder::Ascending
}
);
}

#[test]
fn parse() {
assert_eq!(
SortSpec {
key: SortKey::Path,
order: SortOrder::Ascending
},
"path:ascending".parse().unwrap()
);
}
}
Loading

0 comments on commit 9701803

Please sign in to comment.