1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::process::Command;

use sysinfo::{DiskExt, System, SystemExt};

use crate::{Result, Table, Tool, ToolInfo, Value, ValueType};

pub struct ListDisks;

impl Tool for ListDisks {
    fn info(&self) -> ToolInfo<'static> {
        ToolInfo {
            identifier: "list_disks",
            description: "List all block devices.",
            group: "disks",
            inputs: vec![ValueType::Empty],
        }
    }

    fn run(&self, argument: &Value) -> Result<Value> {
        argument.as_empty()?;

        let mut sys = System::new_all();
        sys.refresh_all();

        let mut disk_table = Table::new(vec![
            "name".to_string(),
            "kind".to_string(),
            "file system".to_string(),
            "mount point".to_string(),
            "total space".to_string(),
            "available space".to_string(),
            "is removable".to_string(),
        ]);

        for disk in sys.disks() {
            let name = disk.name().to_string_lossy().to_string();
            let kind = disk.kind();
            let file_system = String::from_utf8_lossy(disk.file_system()).to_string();
            let mount_point = disk.mount_point().to_str().unwrap().to_string();
            let total_space = disk.total_space() as i64;
            let available_space = disk.available_space() as i64;
            let is_removable = disk.is_removable();

            let row = vec![
                Value::String(name),
                Value::String(format!("{kind:?}")),
                Value::String(file_system),
                Value::String(mount_point),
                Value::Integer(total_space),
                Value::Integer(available_space),
                Value::Boolean(is_removable),
            ];

            disk_table.insert(row)?;
        }

        Ok(Value::Table(disk_table))
    }
}

pub struct Partition;

impl Tool for Partition {
    fn info(&self) -> ToolInfo<'static> {
        ToolInfo {
            identifier: "partition",
            description: "Partition a disk, clearing its content.",
            group: "disks",
            inputs: vec![ValueType::Map],
        }
    }

    fn run(&self, argument: &Value) -> Result<Value> {
        let argument = argument.as_map()?;
        let path = argument
            .get_value("path")?
            .unwrap_or(Value::Empty)
            .as_string()?
            .clone();
        let label = argument
            .get_value("label")?
            .unwrap_or(Value::Empty)
            .as_string()?
            .clone();
        let name = argument
            .get_value("name")?
            .unwrap_or(Value::Empty)
            .as_string()?
            .clone();
        let filesystem = argument
            .get_value("filesystem")?
            .unwrap_or(Value::Empty)
            .as_string()?
            .clone();
        let range = argument
            .get_value("range")?
            .unwrap_or(Value::Empty)
            .as_list()?
            .clone();

        if range.len() != 2 {
            return Err(crate::Error::ExpectedFixedLenList {
                expected_len: 2,
                actual: Value::List(range),
            });
        }

        let range_start = range[0].as_string()?;
        let range_end = range[1].as_string()?;

        let script = format!(
            "sudo parted {path} mklabel {label} mkpart {name} {filesystem} {range_start} {range_end}"
        );

        Command::new("fish")
            .arg("-c")
            .arg(&script)
            .spawn()?
            .wait()?;

        Ok(Value::Empty)
    }
}