Explorar el Código

feat(json): made json available for every output type

MartinFillon hace 2 años
padre
commit
f3e7c8fb7f
Se han modificado 9 ficheros con 89 adiciones y 60 borrados
  1. 21 39
      src/main.rs
  2. 13 13
      src/options/view.rs
  3. 1 1
      src/output/cell.rs
  4. 12 4
      src/output/details.rs
  5. 16 0
      src/output/grid.rs
  6. 14 0
      src/output/grid_details.rs
  7. 1 1
      src/output/lines.rs
  8. 7 1
      src/output/mod.rs
  9. 4 1
      src/output/table.rs

+ 21 - 39
src/main.rs

@@ -393,6 +393,7 @@ impl<'args> Exa<'args> {
         let View {
             ref mode,
             ref file_style,
+            ref output_type,
             ..
         } = self.options.view;
 
@@ -409,7 +410,10 @@ impl<'args> Exa<'args> {
                     console_width,
                     filter,
                 };
-                r.render(&mut self.writer)
+                match output_type {
+                    output::OutputType::Legacy => r.render(&mut self.writer),
+                    output::OutputType::Json => r.render_json(&mut self.writer),
+                }
             }
 
             (Mode::Grid(_), None) | (Mode::Lines, _) => {
@@ -420,7 +424,10 @@ impl<'args> Exa<'args> {
                     file_style,
                     filter,
                 };
-                r.render(&mut self.writer)
+                match output_type {
+                    output::OutputType::Legacy => r.render(&mut self.writer),
+                    output::OutputType::Json => r.render_json(&mut self.writer),
+                }
             }
 
             (Mode::Details(ref opts), _) => {
@@ -442,7 +449,10 @@ impl<'args> Exa<'args> {
                     git,
                     git_repos,
                 };
-                r.render(&mut self.writer)
+                match output_type {
+                    output::OutputType::Legacy => r.render(&mut self.writer),
+                    output::OutputType::Json => r.render_json(&mut self.writer),
+                }
             }
 
             (Mode::GridDetails(ref opts), Some(console_width)) => {
@@ -467,7 +477,10 @@ impl<'args> Exa<'args> {
                     console_width,
                     git_repos,
                 };
-                r.render(&mut self.writer)
+                match output_type {
+                    output::OutputType::Legacy => r.render(&mut self.writer),
+                    output::OutputType::Json => r.render_json(&mut self.writer),
+                }
             }
 
             (Mode::GridDetails(ref opts), None) => {
@@ -490,42 +503,11 @@ impl<'args> Exa<'args> {
                     git,
                     git_repos,
                 };
-                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)
+                match output_type {
+                    output::OutputType::Legacy => r.render(&mut self.writer),
+                    output::OutputType::Json => r.render_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)
-                }
-            },
+            }
         }
     }
 }

+ 13 - 13
src/options/view.rs

@@ -10,7 +10,7 @@ use crate::output::table::{
     Columns, FlagsFormat, GroupFormat, Options as TableOptions, SizeFormat, TimeTypes, UserFormat,
 };
 use crate::output::time::TimeFormat;
-use crate::output::{details, grid, Mode, TerminalWidth, View};
+use crate::output::{details, grid, Mode, OutputType, TerminalWidth, View};
 
 impl View {
     pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
@@ -19,16 +19,28 @@ impl View {
         let total_size = matches.has(&flags::TOTAL_SIZE)?;
         let width = TerminalWidth::deduce(matches, vars)?;
         let file_style = FileStyle::deduce(matches, vars, width.actual_terminal_width().is_some())?;
+        let output_type = OutputType::deduce(matches)?;
         Ok(Self {
             mode,
             width,
             file_style,
             deref_links,
             total_size,
+            output_type,
         })
     }
 }
 
+impl OutputType {
+    fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
+        if matches.has(&flags::JSON)? {
+            Ok(Self::Json)
+        } else {
+            Ok(Self::Legacy)
+        }
+    }
+}
+
 impl Mode {
     /// Determine which viewing mode to use based on the user’s options.
     ///
@@ -44,7 +56,6 @@ impl Mode {
                 || f.matches(&flags::ONE_LINE)
                 || f.matches(&flags::GRID)
                 || f.matches(&flags::TREE)
-                || f.matches(&flags::JSON)
         });
 
         let Some(flag) = flag else {
@@ -90,17 +101,6 @@ 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))
     }

+ 1 - 1
src/output/cell.rs

@@ -110,7 +110,7 @@ impl TextCell {
         let new_contents = self
             .contents
             .iter()
-            .map(|x| x.clone())
+            .cloned()
             .filter(|w| !w.is_empty() && !w.trim().is_empty())
             .collect::<Vec<_>>();
 

+ 12 - 4
src/output/details.rs

@@ -228,7 +228,11 @@ impl<'a> Render<'a> {
         row: &TextCell,
         header: &Option<TextCell>,
     ) -> io::Result<()> {
-        writeln!(w, "{{")?;
+        if self.opts.header {
+            writeln!(w, "{{")?;
+        } else {
+            writeln!(w, "[")?;
+        }
         let mut j: usize = 0;
         for (i, cell) in row.contents.iter().enumerate() {
             if cell.is_empty() || cell.trim().is_empty() {
@@ -246,10 +250,14 @@ impl<'a> Render<'a> {
                 writeln!(w, ", ")?;
             }
         }
-        write!(w, "}}")
+        if self.opts.header {
+            writeln!(w, "}}")
+        } else {
+            writeln!(w, "]")
+        }
     }
 
-    pub fn render_as_json<W: Write>(self, w: &mut W) -> io::Result<()> {
+    pub fn render_json<W: Write>(self, w: &mut W) -> io::Result<()> {
         let n_cpus = match num_cpus::get() as u32 {
             0 => 1,
             n => n,
@@ -280,7 +288,7 @@ impl<'a> Render<'a> {
 
             writeln!(w, "{{")?;
             let mut row_iter = self.iterate_with_table(table.unwrap(), rows);
-            let header: _ = if self.opts.header {
+            let header = if self.opts.header {
                 let header = row_iter.next().unwrap().clean_content();
                 Some(header)
             } else {

+ 16 - 0
src/output/grid.rs

@@ -58,4 +58,20 @@ impl<'a> Render<'a> {
 
         write!(w, "{grid}")
     }
+
+    // As the goal of json output is to be piped we ignore grid options on it
+    // and treat it as just printing *quite* the same as lines
+    pub fn render_json<W: Write>(mut self, w: &mut W) -> io::Result<()> {
+        self.filter.sort_files(&mut self.files);
+        writeln!(w, "{{\"files\":[")?;
+        for (i, file) in self.files.iter().enumerate() {
+            let name_cell = self.file_style.for_file(file, self.theme).paint();
+            write!(w, "\"{}\"", name_cell.strings())?;
+            if (i + 1) < self.files.len() {
+                write!(w, ",")?;
+            }
+        }
+        writeln!(w, "]}}")?;
+        Ok(())
+    }
 }

+ 14 - 0
src/output/grid_details.rs

@@ -109,6 +109,20 @@ impl<'a> Render<'a> {
     // because grid-details has no tree view.
 
     pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
+        if let Some((grid, width)) = self.find_fitting_grid() {
+            write!(w, "{}", grid.fit_into_columns(width))
+        } else {
+            self.give_up().render(w)
+        }
+    }
+
+    pub fn render_json<W: Write>(self, w: &mut W) -> io::Result<()> {
+        // As json is made to be piped we use the Details render method
+
+        self.give_up().render_json(w)
+    }
+
+    pub fn find_fitting_grid(&mut self) -> Option<(grid::Grid, grid::Width)> {
         let options = self
             .details
             .table

+ 1 - 1
src/output/lines.rs

@@ -35,7 +35,7 @@ impl<'a> Render<'a> {
             .paint()
     }
 
-    pub fn render_as_json<W: Write>(mut self, w: &mut W) -> io::Result<()> {
+    pub fn render_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() {

+ 7 - 1
src/output/mod.rs

@@ -25,6 +25,7 @@ pub struct View {
     pub file_style: file_name::Options,
     pub deref_links: bool,
     pub total_size: bool,
+    pub output_type: OutputType,
 }
 
 /// The **mode** is the “type” of output.
@@ -35,7 +36,12 @@ pub enum Mode {
     Details(details::Options),
     GridDetails(grid_details::Options),
     Lines,
-    Json(Option<details::Options>),
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub enum OutputType {
+    Legacy,
+    Json,
 }
 
 /// The width of the terminal requested by the user.

+ 4 - 1
src/output/table.rs

@@ -510,7 +510,10 @@ impl<'a> Table<'a> {
         color_scale_info: Option<ColorScaleInformation>,
     ) -> TextCell {
         match column {
-            Column::Permissions => self.permissions_plus(file, xattrs).render(self.theme).concat_content(),
+            Column::Permissions => self
+                .permissions_plus(file, xattrs)
+                .render(self.theme)
+                .concat_content(),
             Column::FileSize => file
                 .size()
                 .render(