Skip to content

Commit

Permalink
second (kind of working) draft for tar archives
Browse files Browse the repository at this point in the history
  • Loading branch information
taminob committed Jan 23, 2024
1 parent 3677d9f commit 8800a69
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 79 deletions.
84 changes: 45 additions & 39 deletions src/fs/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::fs::feature::git::GitCache;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::slice::Iter as SliceIter;

use log::*;

Expand Down Expand Up @@ -36,10 +37,10 @@ pub trait Directory: Sized {
fn join(&self, child: &Path) -> PathBuf;
}

struct ArchiveEntry {
pub struct ArchiveEntry {
// name: String,
path: String,
size: u64,
pub path: String,
pub size: u64,
// permissions
// owner
// file type
Expand All @@ -52,21 +53,24 @@ pub enum ArchiveFormat {
}

trait ArchiveReader {
fn read_dir(path: &PathBuf) -> io::Result<Vec<ArchiveEntry>>;
fn read_dir(path: &PathBuf) -> io::Result<Vec<Result<ArchiveEntry, Error>>>;
}

struct TarReader {}

impl ArchiveReader for TarReader {
fn read_dir(path: &PathBuf) -> io::Result<Vec<ArchiveEntry>> {
let result = Vec::new();
fn read_dir(path: &PathBuf) -> io::Result<Vec<Result<ArchiveEntry, Error>>> {
let mut result = Vec::new();
let file_content = fs::File::open(path)?;
tar::Archive::new(file_content).entries().map(|entries| {
for entry in entries {
if let Ok(e) = entry {
let path = e.path().expect("TODO").to_string_lossy().to_string();
let size = e.size();
result.push(ArchiveEntry { path, size });
match entry {
Ok(e) => {
let path = e.path().expect("TODO").to_string_lossy().to_string();
let size = e.size();
result.push(Ok(ArchiveEntry { path, size }));
}
Err(_) => result.push(Err(Error {})),
}
}
})?;
Expand All @@ -77,28 +81,40 @@ impl ArchiveReader for TarReader {
impl ArchiveFormat {
pub fn from_extension(extension: &str) -> Option<ArchiveFormat> {
match extension {
".tar" => Some(ArchiveFormat::Tar),
"tar" => Some(ArchiveFormat::Tar),
_ => None,
}
}
}

pub struct Error {}

pub struct Archive {
//pub file: &'a File<'dir>,
pub format: ArchiveFormat,

contents: Vec<ArchiveEntry>,
contents: Vec<Result<ArchiveEntry, Error>>,
pub path: PathBuf,
}

impl Directory for Archive {
fn read_dir(path: PathBuf) -> io::Result<Self> {
let extension = path
.extension()
.unwrap_or(std::ffi::OsStr::new(""))
.to_string_lossy();
pub struct ArchiveIterator<'archive> {
inner: SliceIter<'archive, Result<ArchiveEntry, Error>>,
}

impl<'archive> Iterator for ArchiveIterator<'archive> {
type Item = &'archive Result<ArchiveEntry, Error>;

fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

impl Archive {
pub fn from_path(path: PathBuf) -> io::Result<Self> {
let extension = File::extension(path.as_path()).unwrap_or(String::new());
debug!("Extension: {}", extension);
let format =
ArchiveFormat::from_extension(extension.as_ref()).unwrap_or(ArchiveFormat::Unknown);
ArchiveFormat::from_extension(extension.as_str()).unwrap_or(ArchiveFormat::Unknown);
let contents = match format {
ArchiveFormat::Tar => TarReader::read_dir(&path),
ArchiveFormat::Unknown => {
Expand All @@ -117,28 +133,18 @@ impl Directory for Archive {

/// Produce an iterator of IO results of trying to read all the files in
/// this directory.
fn files<'dir, 'ig>(
&'dir self,
dots: DotFilter,
git: Option<&'ig GitCache>,
git_ignoring: bool,
deref_links: bool,
total_size: bool,
) -> Files<'dir, 'ig> {
Files {
pub fn files<'archive>(&'archive self) -> ArchiveIterator<'archive> {
ArchiveIterator {
inner: self.contents.iter(),
dir: self,
dotfiles: dots.shows_dotfiles(),
dots: dots.dots(),
git,
git_ignoring,
deref_links,
total_size,
}
}

/// Whether this directory contains a file with the given path.
fn contains(&self, path: &Path) -> bool {}
/// Append a path onto the path specified by this directory.
fn join(&self, child: &Path) -> PathBuf {}
/// Whether this archive contains a file with the given path.
fn contains(&self, path: &Path) -> bool {
false
}
/// Append a path onto the path specified by this archive.
fn join(&self, child: &Path) -> PathBuf {
PathBuf::new()
}
}
5 changes: 2 additions & 3 deletions src/fs/dir.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::fs::archive::{Archive, ArchiveFormat};
use crate::fs::feature::git::GitCache;
use crate::fs::fields::GitStatus;
use std::fs;
Expand Down Expand Up @@ -224,7 +223,7 @@ impl Default for DotFilter {

impl DotFilter {
/// Whether this filter should show dotfiles in a listing.
pub fn shows_dotfiles(self) -> bool {
fn shows_dotfiles(self) -> bool {
match self {
Self::JustFiles => false,
Self::Dotfiles => true,
Expand All @@ -233,7 +232,7 @@ impl DotFilter {
}

/// Whether this filter should add dot directories to a listing.
pub fn dots(self) -> DotsNext {
fn dots(self) -> DotsNext {
match self {
Self::JustFiles => DotsNext::Files,
Self::Dotfiles => DotsNext::Files,
Expand Down
38 changes: 16 additions & 22 deletions src/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use log::*;
#[cfg(unix)]
use once_cell::sync::Lazy;

use crate::fs::archive::{Archive, ArchiveFormat};
use crate::fs::archive::Archive;
use crate::fs::dir::Dir;
use crate::fs::feature::xattr;
use crate::fs::feature::xattr::{Attribute, FileAttributes};
Expand Down Expand Up @@ -123,7 +123,7 @@ impl<'dir> File<'dir> {
{
let parent_dir = parent_dir.into();
let name = filename.into().unwrap_or_else(|| File::filename(&path));
let ext = File::ext(&path);
let ext = File::extension(&path);

debug!("Statting file {:?}", &path);
let metadata = std::fs::symlink_metadata(&path)?;
Expand Down Expand Up @@ -162,7 +162,7 @@ impl<'dir> File<'dir> {
name: &'static str,
total_size: bool,
) -> io::Result<File<'dir>> {
let ext = File::ext(&path);
let ext = File::extension(&path);

debug!("Statting file {:?}", &path);
let metadata = std::fs::symlink_metadata(&path)?;
Expand Down Expand Up @@ -229,7 +229,7 @@ impl<'dir> File<'dir> {
/// ASCII lowercasing is used because these extensions are only compared
/// against a pre-compiled list of extensions which are known to only exist
/// within ASCII, so it’s alright.
fn ext(path: &Path) -> Option<String> {
pub fn extension(path: &Path) -> Option<String> {
let name = path.file_name().map(|f| f.to_string_lossy().to_string())?;

name.rfind('.').map(|p| name[p + 1..].to_ascii_lowercase())
Expand Down Expand Up @@ -407,7 +407,7 @@ impl<'dir> File<'dir> {
// follow links.
match std::fs::metadata(&absolute_path) {
Ok(metadata) => {
let ext = File::ext(&path);
let ext = File::extension(&path);
let name = File::filename(&path);
let extended_attributes = OnceLock::new();
let absolute_path_cell = OnceLock::from(Some(absolute_path));
Expand All @@ -433,20 +433,8 @@ impl<'dir> File<'dir> {
}

/// Interpret file as archive
pub fn archive(&self) -> Option<Archive<'_, 'dir>> {
let extension = &self
.path
.extension()
.unwrap_or(std::ffi::OsStr::new(""))
.to_string_lossy();
if let Some(archive_format) = ArchiveFormat::from_extension(extension.as_ref()) {
Some(Archive {
file: &self,
format: archive_format,
})
} else {
None
}
pub fn archive(&self) -> Option<Archive> {
Archive::from_path(self.path.to_owned()).ok()
}

/// Assuming this file is a symlink, follows that link and any further
Expand Down Expand Up @@ -999,17 +987,23 @@ mod ext_test {

#[test]
fn extension() {
assert_eq!(Some("dat".to_string()), File::ext(Path::new("fester.dat")));
assert_eq!(
Some("dat".to_string()),
File::extension(Path::new("fester.dat"))
);
}

#[test]
fn dotfile() {
assert_eq!(Some("vimrc".to_string()), File::ext(Path::new(".vimrc")));
assert_eq!(
Some("vimrc".to_string()),
File::extension(Path::new(".vimrc"))
);
}

#[test]
fn no_extension() {
assert_eq!(None, File::ext(Path::new("jarlsberg")));
assert_eq!(None, File::extension(Path::new("jarlsberg")));
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ pub use self::dir::{Dir, DotFilter};
mod file;
pub use self::file::{File, FileTarget};

pub mod archive;
mod archive;
pub use self::archive::{Archive, ArchiveEntry};

pub mod dir_action;
pub mod feature;
pub mod fields;
Expand Down
72 changes: 59 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use ansiterm::{ANSIStrings, Style};

use crate::fs::feature::git::GitCache;
use crate::fs::filter::GitIgnore;
use crate::fs::{Dir, File};
use crate::fs::{Archive, Dir, File};
use crate::options::stdin::FilesInput;
use crate::options::{vars, Options, OptionsResult, Vars};
use crate::output::{details, escape, file_name, grid, grid_details, lines, Mode, View};
Expand Down Expand Up @@ -291,7 +291,7 @@ impl<'args> Exa<'args> {
}
}

if !self.options.dir_action.treat_dirs_as_files() {
if self.options.inspect_archives && !self.options.dir_action.treat_dirs_as_files() {
for f in &files {
if let Some(archive) = f.archive() {
archives.push(archive);
Expand All @@ -304,12 +304,15 @@ impl<'args> Exa<'args> {
// files to print as well. (It’s a double negative)

let no_files = files.is_empty();
let is_only_dir = dirs.len() == 1 && no_files;
let no_archives = archives.is_empty();
let is_only_archive = archives.len() == 1 && files.len() == 1 && dirs.is_empty();
let is_only_dir = dirs.len() == 1 && no_files && no_archives;

self.options.filter.filter_argument_files(&mut files);
self.print_files(None, files)?;

self.print_dirs(dirs, no_files, is_only_dir, exit_status)
self.print_archives(archives, no_files, is_only_archive)?;
self.print_dirs(dirs, no_files && no_archives, is_only_dir, exit_status)
}

fn print_dirs(
Expand All @@ -333,15 +336,7 @@ impl<'args> Exa<'args> {
}

if !is_only_dir {
let mut bits = Vec::new();
escape(
dir.path.display().to_string(),
&mut bits,
Style::default(),
Style::default(),
quote_style,
);
writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?;
self.print_dir_marker(dir.path.display().to_string(), quote_style)?;
}

let mut children = Vec::new();
Expand Down Expand Up @@ -509,6 +504,57 @@ impl<'args> Exa<'args> {
}
}
}

fn print_archives(
&mut self,
archives: Vec<Archive>,
mut is_first: bool,
is_only_archive: bool,
) -> Result<(), std::io::Error> {
let View {
file_style: file_name::Options { quote_style, .. },
..
} = self.options.view;

for archive in archives {
// Put a gap between directories, or between the list of files and
// the first directory.
if is_first {
is_first = false;
} else {
writeln!(&mut self.writer)?;
}

if !is_only_archive {
self.print_dir_marker(archive.path.display().to_string(), quote_style)?;
}

for entry in archive.files() {
match entry {
Ok(e) => writeln!(self.writer, "{}", e.path),
Err(_) => writeln!(self.writer, "Error - TODO"),
}?;
}
}
Ok(())
}

fn print_dir_marker(
&mut self,
dir_name: String,
quote_style: file_name::QuoteStyle,
) -> Result<(), std::io::Error> {
let mut bits = Vec::new();
escape(
dir_name,
&mut bits,
Style::default(),
Style::default(),
quote_style,
);
writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?;
Ok(())
}
}

mod exits {
Expand Down
2 changes: 1 addition & 1 deletion src/options/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,5 @@ pub static ALL_ARGS: Args = Args(&[
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &SMART_GROUP,

&GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,
&EXTENDED, &OCTAL, &SECURITY_CONTEXT, &STDIN, &FILE_FLAGS
&EXTENDED, &OCTAL, &SECURITY_CONTEXT, &STDIN, &FILE_FLAGS, &INSPECT_ARCHIVES
]);
Loading

0 comments on commit 8800a69

Please sign in to comment.