Improve torrent display formatting
- Use colors - Use cut-friendly formatting when not writing to terminal - Show sizes as number of bytes when not writing to terminal type: changed
This commit is contained in:
parent
5c5dac1fe5
commit
35a0e8f9b7
|
@ -56,7 +56,6 @@ pub(crate) use crate::{
|
||||||
pub(crate) use std::{
|
pub(crate) use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
io::Cursor,
|
io::Cursor,
|
||||||
iter,
|
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
|
@ -68,4 +67,4 @@ pub(crate) use crate::testing;
|
||||||
|
|
||||||
// test structs and enums
|
// test structs and enums
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) use crate::{capture::Capture, test_env::TestEnv};
|
pub(crate) use crate::{capture::Capture, test_env::TestEnv, test_env_builder::TestEnvBuilder};
|
||||||
|
|
48
src/env.rs
48
src/env.rs
|
@ -6,6 +6,8 @@ pub(crate) struct Env {
|
||||||
pub(crate) err: Box<dyn Write>,
|
pub(crate) err: Box<dyn Write>,
|
||||||
pub(crate) out: Box<dyn Write>,
|
pub(crate) out: Box<dyn Write>,
|
||||||
err_style: Style,
|
err_style: Style,
|
||||||
|
out_style: Style,
|
||||||
|
out_is_term: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Env {
|
impl Env {
|
||||||
|
@ -15,16 +17,32 @@ impl Env {
|
||||||
Err(error) => panic!("Failed to get current directory: {}", error),
|
Err(error) => panic!("Failed to get current directory: {}", error),
|
||||||
};
|
};
|
||||||
|
|
||||||
let err_style = if env::var_os("NO_COLOR").is_some()
|
let no_color = env::var_os("NO_COLOR").is_some()
|
||||||
|| env::var_os("TERM").as_deref() == Some(OsStr::new("dumb"))
|
|| env::var_os("TERM").as_deref() == Some(OsStr::new("dumb"));
|
||||||
|| !atty::is(atty::Stream::Stderr)
|
|
||||||
{
|
let err_style = if no_color || !atty::is(atty::Stream::Stderr) {
|
||||||
Style::inactive()
|
Style::inactive()
|
||||||
} else {
|
} else {
|
||||||
Style::active()
|
Style::active()
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::new(dir, io::stdout(), io::stderr(), err_style, env::args())
|
let out_style = if no_color || !atty::is(atty::Stream::Stdout) {
|
||||||
|
Style::inactive()
|
||||||
|
} else {
|
||||||
|
Style::active()
|
||||||
|
};
|
||||||
|
|
||||||
|
let out_is_term = atty::is(atty::Stream::Stdout);
|
||||||
|
|
||||||
|
Self::new(
|
||||||
|
dir,
|
||||||
|
io::stdout(),
|
||||||
|
out_style,
|
||||||
|
out_is_term,
|
||||||
|
io::stderr(),
|
||||||
|
err_style,
|
||||||
|
env::args(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn run(&mut self) -> Result<(), Error> {
|
pub(crate) fn run(&mut self) -> Result<(), Error> {
|
||||||
|
@ -50,7 +68,15 @@ impl Env {
|
||||||
opt.run(self)
|
opt.run(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new<D, O, E, S, I>(dir: D, out: O, err: E, err_style: Style, args: I) -> Self
|
pub(crate) fn new<D, O, E, S, I>(
|
||||||
|
dir: D,
|
||||||
|
out: O,
|
||||||
|
out_style: Style,
|
||||||
|
out_is_term: bool,
|
||||||
|
err: E,
|
||||||
|
err_style: Style,
|
||||||
|
args: I,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
D: AsRef<Path> + 'static,
|
D: AsRef<Path> + 'static,
|
||||||
O: Write + 'static,
|
O: Write + 'static,
|
||||||
|
@ -63,6 +89,8 @@ impl Env {
|
||||||
dir: Box::new(dir),
|
dir: Box::new(dir),
|
||||||
err: Box::new(err),
|
err: Box::new(err),
|
||||||
out: Box::new(out),
|
out: Box::new(out),
|
||||||
|
out_style,
|
||||||
|
out_is_term,
|
||||||
err_style,
|
err_style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,6 +144,14 @@ impl Env {
|
||||||
pub(crate) fn resolve(&self, path: impl AsRef<Path>) -> PathBuf {
|
pub(crate) fn resolve(&self, path: impl AsRef<Path>) -> PathBuf {
|
||||||
self.dir().join(path).clean()
|
self.dir().join(path).clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn out_is_term(&self) -> bool {
|
||||||
|
self.out_is_term
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn out_style(&self) -> Style {
|
||||||
|
self.out_style
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -42,6 +42,9 @@ mod testing;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_env;
|
mod test_env;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_env_builder;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod capture;
|
mod capture;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ impl Metainfo {
|
||||||
Self::deserialize(path, &bytes)
|
Self::deserialize(path, &bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
pub(crate) fn dump(&self, path: impl AsRef<Path>) -> Result<(), Error> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = serde_bencode::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
let bytes = serde_bencode::ser::to_bytes(&self).context(error::MetainfoSerialize)?;
|
||||||
|
|
|
@ -27,4 +27,12 @@ impl Style {
|
||||||
ansi_term::Style::new()
|
ansi_term::Style::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn blue(self) -> ansi_term::Style {
|
||||||
|
if self.active {
|
||||||
|
ansi_term::Style::new().fg(ansi_term::Color::Blue)
|
||||||
|
} else {
|
||||||
|
ansi_term::Style::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
85
src/table.rs
85
src/table.rs
|
@ -13,6 +13,10 @@ impl Table {
|
||||||
self.rows.push((name, Value::Scalar(value.to_string())));
|
self.rows.push((name, Value::Scalar(value.to_string())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn size(&mut self, name: &'static str, bytes: Bytes) {
|
||||||
|
self.rows.push((name, Value::Size(bytes)));
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn tiers(
|
pub(crate) fn tiers(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
@ -47,7 +51,7 @@ impl Table {
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write_human_readable(&self, out: &mut dyn Write) -> io::Result<()> {
|
pub(crate) fn write_human_readable(&self, out: &mut dyn Write, style: Style) -> io::Result<()> {
|
||||||
fn padding(out: &mut dyn Write, n: usize) -> io::Result<()> {
|
fn padding(out: &mut dyn Write, n: usize) -> io::Result<()> {
|
||||||
write!(out, "{:width$}", "", width = n)
|
write!(out, "{:width$}", "", width = n)
|
||||||
}
|
}
|
||||||
|
@ -55,10 +59,17 @@ impl Table {
|
||||||
let name_width = self.name_width();
|
let name_width = self.name_width();
|
||||||
|
|
||||||
for (name, value) in self.rows() {
|
for (name, value) in self.rows() {
|
||||||
write!(out, "{:>width$}", name, width = name_width)?;
|
write!(
|
||||||
|
out,
|
||||||
|
"{:width$}{}",
|
||||||
|
"",
|
||||||
|
style.blue().paint(*name),
|
||||||
|
width = name_width - UnicodeWidthStr::width(*name),
|
||||||
|
)?;
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
Value::Scalar(value) => writeln!(out, " {}", value)?,
|
Value::Scalar(scalar) => writeln!(out, " {}", scalar)?,
|
||||||
|
Value::Size(bytes) => writeln!(out, " {}", bytes)?,
|
||||||
Value::Tiers(tiers) => {
|
Value::Tiers(tiers) => {
|
||||||
let tier_name_width = tiers
|
let tier_name_width = tiers
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -92,31 +103,81 @@ impl Table {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn write_tab_delimited(&self, out: &mut dyn Write) -> io::Result<()> {
|
||||||
|
for (name, value) in self.rows() {
|
||||||
|
write!(out, "{}\t", name)?;
|
||||||
|
match value {
|
||||||
|
Value::Scalar(scalar) => writeln!(out, "{}", scalar)?,
|
||||||
|
Value::Size(Bytes(value)) => writeln!(out, "{}", value)?,
|
||||||
|
Value::Tiers(tiers) => {
|
||||||
|
for (i, value) in tiers.iter().flat_map(|(_name, values)| values).enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(out, "\t")?;
|
||||||
|
}
|
||||||
|
write!(out, "{}", value)?;
|
||||||
|
}
|
||||||
|
writeln!(out)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Value {
|
enum Value {
|
||||||
Scalar(String),
|
Scalar(String),
|
||||||
Tiers(Vec<(String, Vec<String>)>),
|
Tiers(Vec<(String, Vec<String>)>),
|
||||||
|
Size(Bytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn human_readable(table: Table, want: &str) {
|
fn human_readable(table: &Table, want: &str) {
|
||||||
let mut cursor = Cursor::new(Vec::new());
|
let mut cursor = Cursor::new(Vec::new());
|
||||||
table.write_human_readable(&mut cursor).unwrap();
|
table
|
||||||
|
.write_human_readable(&mut cursor, Style::inactive())
|
||||||
|
.unwrap();
|
||||||
|
let have = String::from_utf8(cursor.into_inner()).unwrap();
|
||||||
|
if have != want {
|
||||||
|
panic!("have != want:\nHAVE:\n{}\nWANT:\n{}", have, want);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tab_delimited(table: &Table, want: &str) {
|
||||||
|
let mut cursor = Cursor::new(Vec::new());
|
||||||
|
table.write_tab_delimited(&mut cursor).unwrap();
|
||||||
let have = String::from_utf8(cursor.into_inner()).unwrap();
|
let have = String::from_utf8(cursor.into_inner()).unwrap();
|
||||||
if have != want {
|
if have != want {
|
||||||
panic!("have != want:\nHAVE:\n{}\nWANT:\n{}", have, want);
|
panic!("have != want:\nHAVE:\n{}\nWANT:\n{}", have, want);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn color() {
|
||||||
|
let mut table = Table::new();
|
||||||
|
table.row("Here", "bar");
|
||||||
|
table.row("There", "baz");
|
||||||
|
let mut cursor = Cursor::new(Vec::new());
|
||||||
|
table
|
||||||
|
.write_human_readable(&mut cursor, Style::active())
|
||||||
|
.unwrap();
|
||||||
|
let have = String::from_utf8(cursor.into_inner()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
have,
|
||||||
|
" \u{1b}[34mHere\u{1b}[0m bar\n\u{1b}[34mThere\u{1b}[0m baz\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_row() {
|
fn single_row() {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.row("Foo", "bar");
|
table.row("Foo", "bar");
|
||||||
human_readable(table, "Foo bar\n");
|
human_readable(&table, "Foo bar\n");
|
||||||
|
tab_delimited(&table, "Foo\tbar\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -124,7 +185,8 @@ mod tests {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.row("Foo", "bar");
|
table.row("Foo", "bar");
|
||||||
table.row("X", "y");
|
table.row("X", "y");
|
||||||
human_readable(table, "Foo bar\n X y\n");
|
human_readable(&table, "Foo bar\n X y\n");
|
||||||
|
tab_delimited(&table, "Foo\tbar\nX\ty\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -132,7 +194,7 @@ mod tests {
|
||||||
let mut table = Table::new();
|
let mut table = Table::new();
|
||||||
table.tiers("Foo", vec![("Bar", &["a", "b"]), ("Baz", &["x", "y"])]);
|
table.tiers("Foo", vec![("Bar", &["a", "b"]), ("Baz", &["x", "y"])]);
|
||||||
human_readable(
|
human_readable(
|
||||||
table,
|
&table,
|
||||||
"\
|
"\
|
||||||
Foo Bar: a
|
Foo Bar: a
|
||||||
b
|
b
|
||||||
|
@ -140,6 +202,7 @@ Foo Bar: a
|
||||||
y
|
y
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
tab_delimited(&table, "Foo\ta\tb\tx\ty\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -157,7 +220,7 @@ Foo Bar: a
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
human_readable(
|
human_readable(
|
||||||
table,
|
&table,
|
||||||
" First Some: the
|
" First Some: the
|
||||||
thing
|
thing
|
||||||
Other: about
|
Other: about
|
||||||
|
@ -168,5 +231,9 @@ Second Row: the
|
||||||
that
|
that
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
|
tab_delimited(
|
||||||
|
&table,
|
||||||
|
"First\tthe\tthing\tabout\tthat\nSecond\tthe\tthing\tabout\tthat\n",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,7 @@ pub(crate) struct TestEnv {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestEnv {
|
impl TestEnv {
|
||||||
pub(crate) fn new(iter: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
pub(crate) fn new(env: Env, err: Capture, out: Capture) -> TestEnv {
|
||||||
let err = Capture::new();
|
|
||||||
let out = Capture::new();
|
|
||||||
|
|
||||||
let env = Env::new(
|
|
||||||
tempfile::tempdir().unwrap(),
|
|
||||||
out.clone(),
|
|
||||||
err.clone(),
|
|
||||||
Style::inactive(),
|
|
||||||
iter::once(String::from("imdl")).chain(iter.into_iter().map(|item| item.into())),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self { err, env, out }
|
Self { err, env, out }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
62
src/test_env_builder.rs
Normal file
62
src/test_env_builder.rs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
pub(crate) struct TestEnvBuilder {
|
||||||
|
args: Vec<String>,
|
||||||
|
out_is_term: bool,
|
||||||
|
use_color: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestEnvBuilder {
|
||||||
|
pub(crate) fn new() -> TestEnvBuilder {
|
||||||
|
TestEnvBuilder {
|
||||||
|
args: Vec::new(),
|
||||||
|
out_is_term: false,
|
||||||
|
use_color: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn out_is_term(mut self) -> Self {
|
||||||
|
self.out_is_term = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn arg(mut self, arg: impl Into<String>) -> Self {
|
||||||
|
self.args.push(arg.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
||||||
|
for arg in args {
|
||||||
|
self.args.push(arg.into());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn arg_slice(mut self, args: &[&str]) -> Self {
|
||||||
|
for arg in args.iter().cloned() {
|
||||||
|
self.args.push(arg.to_owned());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build(self) -> TestEnv {
|
||||||
|
let err = Capture::new();
|
||||||
|
let out = Capture::new();
|
||||||
|
|
||||||
|
let env = Env::new(
|
||||||
|
tempfile::tempdir().unwrap(),
|
||||||
|
out.clone(),
|
||||||
|
if self.use_color && self.out_is_term {
|
||||||
|
Style::active()
|
||||||
|
} else {
|
||||||
|
Style::inactive()
|
||||||
|
},
|
||||||
|
self.out_is_term,
|
||||||
|
err.clone(),
|
||||||
|
Style::inactive(),
|
||||||
|
self.args,
|
||||||
|
);
|
||||||
|
|
||||||
|
TestEnv::new(env, err, out)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
pub(crate) fn env(iter: impl IntoIterator<Item = impl Into<String>>) -> TestEnv {
|
pub(crate) fn env(iter: impl IntoIterator<Item = impl Into<String>>) -> TestEnv {
|
||||||
TestEnv::new(iter)
|
TestEnvBuilder::new().arg("imdl").args(iter).build()
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,13 +872,20 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn output() {
|
fn output() {
|
||||||
let mut env = environment(&[
|
let mut env = TestEnvBuilder::new()
|
||||||
"--input",
|
.arg_slice(&[
|
||||||
"foo",
|
"imdl",
|
||||||
"--announce",
|
"torrent",
|
||||||
"http://bar",
|
"create",
|
||||||
"--no-creation-date",
|
"--input",
|
||||||
]);
|
"foo",
|
||||||
|
"--announce",
|
||||||
|
"http://bar",
|
||||||
|
"--no-creation-date",
|
||||||
|
])
|
||||||
|
.out_is_term()
|
||||||
|
.build();
|
||||||
|
|
||||||
let dir = env.resolve("foo");
|
let dir = env.resolve("foo");
|
||||||
fs::create_dir(&dir).unwrap();
|
fs::create_dir(&dir).unwrap();
|
||||||
fs::write(dir.join("a"), "abc").unwrap();
|
fs::write(dir.join("a"), "abc").unwrap();
|
||||||
|
|
|
@ -33,12 +33,6 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn output() {
|
fn output() {
|
||||||
let mut env = testing::env(
|
|
||||||
["torrent", "show", "--input", "foo.torrent"]
|
|
||||||
.iter()
|
|
||||||
.cloned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let metainfo = Metainfo {
|
let metainfo = Metainfo {
|
||||||
announce: "announce".into(),
|
announce: "announce".into(),
|
||||||
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
|
announce_list: Some(vec![vec!["announce".into(), "b".into()], vec!["c".into()]]),
|
||||||
|
@ -61,14 +55,20 @@ mod tests {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = env.resolve("foo.torrent");
|
{
|
||||||
|
let mut env = TestEnvBuilder::new()
|
||||||
|
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
|
||||||
|
.out_is_term()
|
||||||
|
.build();
|
||||||
|
|
||||||
metainfo.dump(path).unwrap();
|
let path = env.resolve("foo.torrent");
|
||||||
|
|
||||||
env.run().unwrap();
|
metainfo.dump(path).unwrap();
|
||||||
|
|
||||||
let have = env.out();
|
env.run().unwrap();
|
||||||
let want = " Name foo
|
|
||||||
|
let have = env.out();
|
||||||
|
let want = " Name foo
|
||||||
Comment comment
|
Comment comment
|
||||||
Created 1970-01-01 00:00:01 UTC
|
Created 1970-01-01 00:00:01 UTC
|
||||||
Source source
|
Source source
|
||||||
|
@ -76,8 +76,7 @@ mod tests {
|
||||||
Torrent Size 252 bytes
|
Torrent Size 252 bytes
|
||||||
Content Size 20 bytes
|
Content Size 20 bytes
|
||||||
Private yes
|
Private yes
|
||||||
Trackers Main: announce
|
Trackers Tier 1: announce
|
||||||
Tier 1: announce
|
|
||||||
b
|
b
|
||||||
Tier 2: c
|
Tier 2: c
|
||||||
Piece Size 16 KiB
|
Piece Size 16 KiB
|
||||||
|
@ -85,6 +84,37 @@ Content Size 20 bytes
|
||||||
File Count 1
|
File Count 1
|
||||||
";
|
";
|
||||||
|
|
||||||
assert_eq!(have, want);
|
assert_eq!(have, want);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut env = TestEnvBuilder::new()
|
||||||
|
.arg_slice(&["imdl", "torrent", "show", "--input", "foo.torrent"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let path = env.resolve("foo.torrent");
|
||||||
|
|
||||||
|
metainfo.dump(path).unwrap();
|
||||||
|
|
||||||
|
env.run().unwrap();
|
||||||
|
|
||||||
|
let have = env.out();
|
||||||
|
let want = "\
|
||||||
|
Name\tfoo
|
||||||
|
Comment\tcomment
|
||||||
|
Created\t1970-01-01 00:00:01 UTC
|
||||||
|
Source\tsource
|
||||||
|
Info Hash\tb7595205a46491b3e8686e10b28efe7144d066cc
|
||||||
|
Torrent Size\t252
|
||||||
|
Content Size\t20
|
||||||
|
Private\tyes
|
||||||
|
Trackers\tannounce\tb\tc
|
||||||
|
Piece Size\t16384
|
||||||
|
Piece Count\t1
|
||||||
|
File Count\t1
|
||||||
|
";
|
||||||
|
|
||||||
|
assert_eq!(have, want);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,9 +45,16 @@ impl TorrentSummary {
|
||||||
pub(crate) fn write(&self, env: &mut Env) -> Result<(), Error> {
|
pub(crate) fn write(&self, env: &mut Env) -> Result<(), Error> {
|
||||||
let table = self.table();
|
let table = self.table();
|
||||||
|
|
||||||
table
|
if env.out_is_term() {
|
||||||
.write_human_readable(&mut env.out)
|
let out_style = env.out_style();
|
||||||
.context(error::Stdout)?;
|
table
|
||||||
|
.write_human_readable(&mut env.out, out_style)
|
||||||
|
.context(error::Stdout)?;
|
||||||
|
} else {
|
||||||
|
table
|
||||||
|
.write_tab_delimited(&mut env.out)
|
||||||
|
.context(error::Stdout)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -81,9 +88,9 @@ impl TorrentSummary {
|
||||||
|
|
||||||
table.row("Info Hash", self.infohash);
|
table.row("Info Hash", self.infohash);
|
||||||
|
|
||||||
table.row("Torrent Size", self.size);
|
table.size("Torrent Size", self.size);
|
||||||
|
|
||||||
table.row("Content Size", self.metainfo.info.mode.total_size());
|
table.size("Content Size", self.metainfo.info.mode.total_size());
|
||||||
|
|
||||||
table.row(
|
table.row(
|
||||||
"Private",
|
"Private",
|
||||||
|
@ -97,7 +104,13 @@ impl TorrentSummary {
|
||||||
match &self.metainfo.announce_list {
|
match &self.metainfo.announce_list {
|
||||||
Some(tiers) => {
|
Some(tiers) => {
|
||||||
let mut value = Vec::new();
|
let mut value = Vec::new();
|
||||||
value.push(("Main".to_owned(), vec![self.metainfo.announce.clone()]));
|
|
||||||
|
if !tiers
|
||||||
|
.iter()
|
||||||
|
.any(|tier| tier.contains(&self.metainfo.announce))
|
||||||
|
{
|
||||||
|
value.push(("Main".to_owned(), vec![self.metainfo.announce.clone()]));
|
||||||
|
}
|
||||||
|
|
||||||
for (i, tier) in tiers.iter().enumerate() {
|
for (i, tier) in tiers.iter().enumerate() {
|
||||||
value.push((format!("Tier {}", i + 1), tier.clone()));
|
value.push((format!("Tier {}", i + 1), tier.clone()));
|
||||||
|
@ -108,7 +121,7 @@ impl TorrentSummary {
|
||||||
None => table.row("Tracker", &self.metainfo.announce),
|
None => table.row("Tracker", &self.metainfo.announce),
|
||||||
}
|
}
|
||||||
|
|
||||||
table.row("Piece Size", Bytes::from(self.metainfo.info.piece_length));
|
table.size("Piece Size", Bytes::from(self.metainfo.info.piece_length));
|
||||||
|
|
||||||
table.row("Piece Count", self.metainfo.info.pieces.len() / 20);
|
table.row("Piece Count", self.metainfo.info.pieces.len() / 20);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user