intermodal/src/torrent_summary.rs
Casey Rodarmor a6bf752791
Use invariant instead of unwrap and expect
Add the `Invariant` trait, which provides `Invariant::invariant` and
`Invariant::invariant_unwrap` methods, and use them instead of unwrap
and expect.

I think these methods are a bit clearer than `unwrap` and `expect`,
since they more clearly document intent, i.e. that the thing passed to
`invariant` should be a description of an invariant that should always
be true, and should provide better error messages.

Replace uses of `unwrap` and `expect` with `invariant`.

type: reform
fixes:
- https://github.com/casey/intermodal/issues/167
2020-04-18 14:41:27 -07:00

143 lines
3.2 KiB
Rust

use crate::common::*;
pub(crate) struct TorrentSummary {
infohash: Infohash,
metainfo: Metainfo,
size: Bytes,
}
impl TorrentSummary {
fn new(metainfo: Metainfo, infohash: Infohash, size: Bytes) -> Self {
Self {
infohash,
metainfo,
size,
}
}
pub(crate) fn from_metainfo(metainfo: Metainfo) -> Result<Self> {
let bytes = metainfo.serialize()?;
let size = Bytes(bytes.len().into_u64());
let infohash = metainfo.infohash()?;
Ok(Self::new(metainfo, infohash, size))
}
pub(crate) fn from_input(input: &Input) -> Result<Self> {
let metainfo = Metainfo::from_input(input)?;
Ok(Self::from_metainfo(metainfo)?)
}
pub(crate) fn write(&self, env: &mut Env) -> Result<()> {
let table = self.table();
if env.out().is_term() {
let style = env.out().style();
table
.write_human_readable(env.out_mut(), style)
.context(error::Stdout)?;
} else {
table
.write_tab_delimited(env.out_mut())
.context(error::Stdout)?;
}
Ok(())
}
fn table(&self) -> Table {
let mut table = Table::new();
table.row("Name", &self.metainfo.info.name);
if let Some(comment) = &self.metainfo.comment {
table.row("Comment", comment);
}
if let Some(creation_date) = self.metainfo.creation_date {
#[allow(clippy::as_conversions)]
table.row(
"Creation Date",
Utc.timestamp(
creation_date
.min(i64::max_value() as u64)
.try_into()
.invariant_unwrap("min with i64 is always valid i64"),
0,
),
);
}
if let Some(created_by) = &self.metainfo.created_by {
table.row("Created By", created_by);
}
if let Some(source) = &self.metainfo.info.source {
table.row("Source", source);
}
table.row("Info Hash", self.infohash);
table.size("Torrent Size", self.size);
table.size("Content Size", self.metainfo.content_size());
table.row(
"Private",
if self.metainfo.info.private.unwrap_or(false) {
"yes"
} else {
"no"
},
);
if let Some(announce) = &self.metainfo.announce {
table.row("Tracker", announce);
}
if let Some(tiers) = &self.metainfo.announce_list {
let mut value = Vec::new();
for (i, tier) in tiers.iter().enumerate() {
value.push((format!("Tier {}", i + 1), tier.clone()));
}
table.tiers("Announce List", value);
}
if let Some(nodes) = &self.metainfo.nodes {
table.list(
"DHT Nodes",
nodes
.iter()
.map(ToString::to_string)
.collect::<Vec<String>>(),
);
}
table.size("Piece Size", self.metainfo.info.piece_length);
table.row("Piece Count", self.metainfo.info.pieces.count());
match &self.metainfo.info.mode {
Mode::Single { .. } => {
table.row("File Count", 1);
table.row("Files", &self.metainfo.info.name);
}
Mode::Multiple { files } => {
table.row("File Count", files.len());
table.directory(
"Files",
&self.metainfo.info.name,
files
.iter()
.map(|file_info| file_info.path.clone())
.collect(),
);
}
};
table
}
}