Skip to main content

btrfs_cli/filesystem/
df.rs

1use super::UnitMode;
2use crate::{
3    Format, RunContext, Runnable,
4    util::{fmt_size, open_path, print_json},
5};
6use anyhow::{Context, Result};
7use btrfs_uapi::space::space_info;
8use clap::Parser;
9use cols::Cols;
10use serde::Serialize;
11use std::{os::unix::io::AsFd, path::PathBuf};
12
13/// Show space usage information for a mounted filesystem
14#[derive(Parser, Debug)]
15pub struct FilesystemDfCommand {
16    #[clap(flatten)]
17    pub units: UnitMode,
18
19    pub path: PathBuf,
20}
21
22#[derive(Serialize)]
23struct SpaceEntryJson {
24    #[serde(rename = "type")]
25    bg_type: String,
26    profile: String,
27    total: u64,
28    used: u64,
29}
30
31impl Runnable for FilesystemDfCommand {
32    fn supported_formats(&self) -> &[Format] {
33        &[Format::Text, Format::Json, Format::Modern]
34    }
35
36    fn run(&self, ctx: &RunContext) -> Result<()> {
37        let mode = self.units.resolve();
38        let file = open_path(&self.path)?;
39        let entries = space_info(file.as_fd()).with_context(|| {
40            format!("failed to get space info for '{}'", self.path.display())
41        })?;
42
43        match ctx.format {
44            Format::Modern => {
45                let rows: Vec<DfRow> = entries
46                    .iter()
47                    .map(|e| DfRow {
48                        bg_type: e.flags.type_name().to_string(),
49                        profile: e.flags.profile_name().to_string(),
50                        total: fmt_size(e.total_bytes, &mode),
51                        used: fmt_size(e.used_bytes, &mode),
52                    })
53                    .collect();
54                let mut out = std::io::stdout().lock();
55                let _ = DfRow::print_table(&rows, &mut out);
56            }
57            Format::Text => {
58                for entry in &entries {
59                    println!(
60                        "{}, {}: total={}, used={}",
61                        entry.flags.type_name(),
62                        entry.flags.profile_name(),
63                        fmt_size(entry.total_bytes, &mode),
64                        fmt_size(entry.used_bytes, &mode),
65                    );
66                }
67            }
68            Format::Json => {
69                let json: Vec<SpaceEntryJson> = entries
70                    .iter()
71                    .map(|e| SpaceEntryJson {
72                        bg_type: e.flags.type_name().to_string(),
73                        profile: e.flags.profile_name().to_string(),
74                        total: e.total_bytes,
75                        used: e.used_bytes,
76                    })
77                    .collect();
78                print_json("filesystem-df", &json)?;
79            }
80        }
81
82        Ok(())
83    }
84}
85
86#[derive(Cols)]
87struct DfRow {
88    #[column(header = "TYPE")]
89    bg_type: String,
90    #[column(header = "PROFILE")]
91    profile: String,
92    #[column(header = "TOTAL", right)]
93    total: String,
94    #[column(header = "USED", right)]
95    used: String,
96}