Skip to content

Commit

Permalink
New collector DirectoryEntries
Browse files Browse the repository at this point in the history
  • Loading branch information
Enselic authored and sharkdp committed Mar 4, 2022
1 parent b1627d9 commit adfd3d8
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ format_plaintext = []
sys-info = { version = "0.9", optional = true }
git-version = { version = "0.3", optional = true }
shell-escape = "0.1"

[dev-dependencies]
pretty_assertions = "1.1.0"
tempfile = "3.3.0"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ generates bug report information that [looks like this](example-report.md).
- [x] Command line (including all arguments)
- [x] Environment variables (e.g. `SHELL`, `PATH`, …)
- [x] File contents (e.g. config files)
- [x] Directory contents
- [x] Command output (e.g. `bash --version`)
- [x] Compile time information (profile, target, architecture, cpu features, etc.)
- [ ] Current working directory
Expand Down
3 changes: 3 additions & 0 deletions src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use super::Result;
use crate::helper::StringExt;
use crate::report::{Code, ReportEntry};

mod directory_entries;
pub use directory_entries::DirectoryEntries;

/// Error that appeared while collecting bug report information.
#[derive(Debug)]
pub enum CollectionError {
Expand Down
88 changes: 88 additions & 0 deletions src/collector/directory_entries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use std::fs::{self, DirEntry};
use std::io::ErrorKind;
use std::path::PathBuf;

use crate::{report::ReportEntry, Collector, CrateInfo, Result};

use super::CollectionError;

/// List information about entries in a directory.
///
/// Limitations:
/// * Is not recursive
/// * Does not handle symbolic links
/// * Only sizes of files are printed and not e.g. time of last modification
///
/// # Example
///
/// ```md
/// #### File and dir
///
/// - file.txt, 14 bytes
/// - some_dir
///
/// ```
pub struct DirectoryEntries {
title: String,
path: PathBuf,
}

impl DirectoryEntries {
pub fn new(title: impl Into<String>, path: impl Into<PathBuf>) -> Self {
Self {
title: title.into(),
path: path.into(),
}
}
}

impl Collector for DirectoryEntries {
fn description(&self) -> &str {
&self.title
}

fn collect(&mut self, _: &CrateInfo) -> Result<ReportEntry> {
let path_str = &self.path.to_string_lossy();

let mut entries = fs::read_dir(&self.path)
.map_err(|e| read_dir_error_to_report_entry(e, path_str))?
.map(|e| match e {
Ok(dir_entry) => dir_entry_to_report_entry(dir_entry),
Err(e) => format!("Error: {}", e),
})
.collect::<Vec<_>>();

// For stable ordering
entries.sort();

if entries.is_empty() {
Ok(ReportEntry::Text(format!("'{}' is empty", path_str)))
} else {
Ok(ReportEntry::List(
entries.into_iter().map(ReportEntry::Text).collect(),
))
}
}
}

fn read_dir_error_to_report_entry(error: std::io::Error, path_str: &str) -> CollectionError {
CollectionError::CouldNotRetrieve(if error.kind() == ErrorKind::NotFound {
format!("'{}' not found", path_str)
} else {
format!("'{}' not read: {}", path_str, error)
})
}

fn dir_entry_to_report_entry(dir_entry: DirEntry) -> String {
let mut text = String::new();

text.push_str(&dir_entry.file_name().to_string_lossy());

if let Ok(metadata) = dir_entry.metadata() {
if metadata.is_file() {
text.push_str(&format!(", {} bytes", metadata.len()));
}
}

text
}
90 changes: 90 additions & 0 deletions tests/test_collector_directory_entries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#![cfg(feature = "format_markdown")]

use std::path::PathBuf;

use pretty_assertions::assert_eq;
use tempfile::tempdir;

use bugreport::{bugreport, collector::DirectoryEntries, format::Markdown};

#[test]
fn dir_not_found() {
let actual = bugreport!()
.info(DirectoryEntries::new("No dir", "this-dir-does-not-exist"))
.format::<Markdown>();

let expected = "#### No dir
'this-dir-does-not-exist' not found
";

assert_eq!(expected, actual);
}

#[test]
fn dir_is_empty() -> Result<(), std::io::Error> {
let empty_dir = tempdir()?;
let empty_dir_path = empty_dir.path();

let actual = bugreport!()
.info(DirectoryEntries::new("Empty dir", empty_dir_path))
.format::<Markdown>();

let expected = format!(
"#### Empty dir
'{}' is empty
",
empty_dir_path.to_string_lossy()
);

assert_eq!(expected, actual);

Ok(())
}

#[test]
fn dir_exists() -> Result<(), std::io::Error> {
let dir = tempdir()?;
let dir_path = dir.path();

// Put a file in the dir
let mut some_file = PathBuf::from(dir_path);
some_file.push("file.txt");
std::fs::write(some_file, "This is a file")?;

// Put a dir in the dir
let mut some_dir = PathBuf::from(dir_path);
some_dir.push("some_dir");
std::fs::create_dir(some_dir)?;

let actual = bugreport!()
.info(DirectoryEntries::new("File and dir", dir_path))
.format::<Markdown>();

let expected = "#### File and dir
- file.txt, 14 bytes
- some_dir
";

assert_eq!(expected, actual);

Ok(())
}

#[test]
fn new() {
DirectoryEntries::new("a", "/a");
DirectoryEntries::new(String::from("b"), PathBuf::from("/b"));
DirectoryEntries::new(&String::from("c"), &PathBuf::from("/c"));
new_with_title_from_local_variable();
}

fn new_with_title_from_local_variable() -> DirectoryEntries {
let local_variable = String::from("pretend this is dynamically constructed");
DirectoryEntries::new(&local_variable, "/path")
}

0 comments on commit adfd3d8

Please sign in to comment.