feat(json): starting to parse json as per asked in #768

This commit is contained in:
MartinFillon 2024-01-10 21:22:09 +01:00
parent 285cd3ed0e
commit 18cace298e
No known key found for this signature in database
GPG key ID: 16DC898F53F94853
6 changed files with 125 additions and 1 deletions

View file

@ -396,6 +396,8 @@ impl<'args> Exa<'args> {
..
} = self.options.view;
debug!("matching on mode {:?}, {:?}", mode, self.console_width);
match (mode, self.console_width) {
(Mode::Grid(ref opts), Some(console_width)) => {
let filter = &self.options.filter;
@ -490,6 +492,40 @@ impl<'args> Exa<'args> {
};
r.render(&mut self.writer)
}
(Mode::Json(ref opts), _) => match opts {
Some(o) => {
let filter = &self.options.filter;
let recurse = self.options.dir_action.recurse_options();
let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
let git = self.git.as_ref();
let git_repos = self.git_repos;
let r = details::Render {
dir,
files,
theme,
file_style,
opts: o,
recurse,
filter,
git_ignoring,
git,
git_repos,
};
r.render_as_json(&mut self.writer)
}
None => {
let filter = &self.options.filter;
let r = lines::Render {
files,
theme,
file_style,
filter,
};
r.render_as_json(&mut self.writer)
}
},
}
}
}

View file

@ -1,3 +1,4 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
use crate::options::parser::{Arg, Args, TakesValue, Values};
// exa options
@ -6,6 +7,7 @@ pub static HELP: Arg = Arg { short: Some(b'?'), long: "help", takes_value
// display options
pub static ONE_LINE: Arg = Arg { short: Some(b'1'), long: "oneline", takes_value: TakesValue::Forbidden };
pub static JSON: Arg = Arg { short: None, long: "json", takes_value: TakesValue::Forbidden };
pub static LONG: Arg = Arg { short: Some(b'l'), long: "long", takes_value: TakesValue::Forbidden };
pub static GRID: Arg = Arg { short: Some(b'G'), long: "grid", takes_value: TakesValue::Forbidden };
pub static ACROSS: Arg = Arg { short: Some(b'x'), long: "across", takes_value: TakesValue::Forbidden };
@ -88,7 +90,7 @@ pub static FILE_FLAGS: Arg = Arg { short: Some(b'O'), long: "flags",
pub static ALL_ARGS: Args = Args(&[
&VERSION, &HELP,
&ONE_LINE, &LONG, &GRID, &ACROSS, &RECURSE, &TREE, &CLASSIFY, &DEREF_LINKS,
&ONE_LINE, &JSON, &LONG, &GRID, &ACROSS, &RECURSE, &TREE, &CLASSIFY, &DEREF_LINKS,
&COLOR, &COLOUR, &COLOR_SCALE, &COLOUR_SCALE, &COLOR_SCALE_MODE, &COLOUR_SCALE_MODE,
&WIDTH, &NO_QUOTES, &ABSOLUTE,

View file

@ -44,6 +44,7 @@ impl Mode {
|| f.matches(&flags::ONE_LINE)
|| f.matches(&flags::GRID)
|| f.matches(&flags::TREE)
|| f.matches(&flags::JSON)
});
let Some(flag) = flag else {
@ -89,6 +90,17 @@ impl Mode {
return Ok(Self::Lines);
}
if flag.matches(&flags::JSON) && !flag.matches(&flags::ONE_LINE) {
let _ = matches.has(&flags::JSON)?;
let details = details::Options::deduce_long(matches, vars)?;
return Ok(Self::Json(Some(details)));
}
if flag.matches(&flags::JSON) && flag.matches(&flags::ONE_LINE) {
let _ = matches.has(&flags::JSON)?;
return Ok(Self::Json(None));
}
let grid = grid::Options::deduce(matches)?;
Ok(Self::Grid(grid))
}

View file

@ -222,6 +222,64 @@ impl<'a> Render<'a> {
Ok(())
}
pub fn render_as_json<W: Write>(mut self, w: &mut W) -> io::Result<()> {
let n_cpus = match num_cpus::get() as u32 {
0 => 1,
n => n,
};
let mut pool = Pool::new(n_cpus);
let mut rows = Vec::new();
if let Some(ref options) = self.opts.table {
let mut table = Table::new(options, self.git, self.theme, self.git_repos);
if self.opts.header {
let header = table.header_row();
table.add_widths(&header);
}
// This is weird, but I cant find a way around it:
// https://internals.rust-lang.org/t/should-option-mut-t-implement-copy/3715/6
let mut table = Some(table);
self.add_files_to_table(
&mut pool,
&mut table,
&mut rows,
&self.files,
TreeDepth::root(),
None,
);
write!(w, "{}", "{\"files\":[")?;
for (i, row) in self.iterate_with_table(table.unwrap(), rows).enumerate() {
write!(w, "\"{}\"", row.strings())?;
if (i + 1) < self.files.len() {
write!(w, ", ")?;
}
}
write!(w, "{}", "]}}\n")?;
} else {
self.add_files_to_table(
&mut pool,
&mut None,
&mut rows,
&self.files,
TreeDepth::root(),
None,
);
write!(w, "{}", "{\"files\":[")?;
for (i, row) in self.iterate(rows).enumerate() {
write!(w, "\"{}\"", row.strings())?;
if (i + 1) < self.files.len() {
write!(w, ", ")?;
}
}
write!(w, "{}", "]}}\n")?;
}
Ok(())
}
/// Whether to show the extended attribute hint
pub fn show_xattr_hint(&self, file: &File<'_>) -> bool {
// Do not show the hint '@' if the only extended attribute is the security

View file

@ -34,4 +34,19 @@ impl<'a> Render<'a> {
.with_mount_details(false)
.paint()
}
pub fn render_as_json<W: Write>(mut self, w: &mut W) -> io::Result<()> {
self.filter.sort_files(&mut self.files);
write!(w, "{}", "{\"files\":[")?;
for (i, file) in self.files.iter().enumerate() {
let name_cell = self.render_file(file);
write!(w, "\"{}\"", ANSIStrings(&name_cell))?;
if (i + 1) < self.files.len() {
write!(w, "{}", ",")?;
}
}
write!(w, "{}", "]}\n")?;
Ok(())
}
}

View file

@ -35,6 +35,7 @@ pub enum Mode {
Details(details::Options),
GridDetails(grid_details::Options),
Lines,
Json(Option<details::Options>),
}
/// The width of the terminal requested by the user.