intermodal/src/sort_spec.rs
Casey Rodarmor 97018031c1
Introduce "sort specs" to allow fine-grained sorting of files in torrents
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
2020-04-07 19:01:33 -07:00

96 lines
1.9 KiB
Rust

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()
);
}
}