dysk_cli/
csv.rs

1use {
2    crate::{
3        Args, col::Col,
4    },
5    lfs_core::*,
6    std::{
7        fmt::Display,
8        io::Write,
9    },
10};
11
12/// Utility to write in CSV
13struct Csv<W: Write> {
14    separator: char,
15    w: W,
16}
17
18impl<W: Write> Csv<W> {
19    pub fn new(separator: char, w: W) -> Self {
20        Self { separator, w }
21    }
22    pub fn cell<D: Display>(&mut self, content: D) -> Result<(), std::io::Error> {
23        let s = content.to_string();
24        let needs_quotes = s.contains(self.separator) || s.contains('"') || s.contains('\n');
25        if needs_quotes {
26            write!(self.w, "\"")?;
27            for c in s.chars() {
28                if c == '"' {
29                    write!(self.w, "\"\"")?;
30                } else {
31                    write!(self.w, "{}", c)?;
32                }
33            }
34            write!(self.w, "\"")?;
35        } else {
36            write!(self.w, "{}", s)?;
37        }
38        write!(self.w, "{}", self.separator)
39    }
40    pub fn cell_opt<D: Display>(&mut self, content: Option<D>) -> Result<(), std::io::Error> {
41        if let Some(c) = content {
42            self.cell(c)
43        } else {
44            write!(self.w, "{}", self.separator)
45        }
46    }
47    pub fn end_line(&mut self) -> Result<(), std::io::Error> {
48        writeln!(self.w)
49    }
50}
51
52pub fn print(mounts: &[&Mount], args: &Args) -> Result<(), std::io::Error> {
53    let units = args.units;
54    let mut csv = Csv::new(args.csv_separator, std::io::stdout());
55    for col in args.cols.cols() {
56        csv.cell(col.title())?;
57    }
58    csv.end_line()?;
59    for mount in mounts {
60        for col in args.cols.cols() {
61            match col {
62                Col::Id => csv.cell_opt(mount.info.id),
63                Col::Dev => csv.cell(&mount.info.dev),
64                Col::Filesystem => csv.cell(&mount.info.fs),
65                Col::Label => csv.cell_opt(mount.fs_label.as_ref()),
66                Col::Type => csv.cell(&mount.info.fs_type),
67                Col::Remote => csv.cell(if mount.info.is_remote() { "yes" } else { "no" }),
68                Col::Disk => csv.cell_opt(mount.disk.as_ref().map(|d| d.disk_type())),
69                Col::Used => csv.cell_opt(mount.stats().map(|s| units.fmt(s.used()))),
70                Col::Use => csv.cell_opt(mount.stats().map(|s| s.use_share())),
71                Col::UsePercent => csv.cell_opt(mount.stats().map(|s| format!("{:.0}%", 100.0 * s.use_share()))),
72                Col::Free => csv.cell_opt(mount.stats().map(|s| units.fmt(s.available()))),
73                Col::FreePercent => csv.cell_opt(mount.stats().map(|s| format!("{:.0}%", 100.0 * (1.0 - s.use_share())))),
74                Col::Size => csv.cell_opt(mount.stats().map(|s| units.fmt(s.size()))),
75                Col::InodesUsed => csv.cell_opt(mount.inodes().map(|i| i.used())),
76                Col::InodesUse => csv.cell_opt(mount.inodes().map(|i| i.use_share())),
77                Col::InodesUsePercent => csv.cell_opt(mount.inodes().map(|i| format!("{:.0}%", 100.0 * i.use_share()))),
78                Col::InodesFree => csv.cell_opt(mount.inodes().map(|i| i.favail)),
79                Col::InodesCount => csv.cell_opt(mount.inodes().map(|i| i.files)),
80                Col::MountPoint => csv.cell(mount.info.mount_point.to_string_lossy()),
81                Col::Uuid => csv.cell(mount.uuid.as_ref().map_or("", |v| v)),
82                Col::PartUuid => csv.cell(mount.part_uuid.as_ref().map_or("", |v| v)),
83            }?;
84        }
85        csv.end_line()?;
86    }
87    Ok(())
88}
89
90#[test]
91fn test_csv() {
92    use std::io::Cursor;
93    let mut w = Cursor::new(Vec::new());
94    let mut csv = Csv::new(';', &mut w);
95    csv.cell("1;2;3").unwrap();
96    csv.cell("\"").unwrap();
97    csv.cell("").unwrap();
98    csv.end_line().unwrap();
99    csv.cell(3).unwrap();
100    let s = String::from_utf8(w.into_inner()).unwrap();
101    assert_eq!(
102        s,
103r#""1;2;3";"""";;
1043;"#,
105    );
106}