summaryrefslogtreecommitdiffstats
path: root/src/removable_devices.rs
blob: 6f793f845da15bdd51c7f0b900c5e4f0649139ff (plain)
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
use anyhow::{anyhow, Result};

use crate::{constant_strings_paths::GIO, impl_selectable_content};

#[derive(Debug, Clone, Default)]
pub struct RemovableDevices {
    pub content: Vec<Removable>,
    pub index: usize,
}

impl RemovableDevices {
    pub fn from_gio() -> Option<Self> {
        let Ok(output) = std::process::Command::new(GIO)
            .args(["mount", "-li"])
            .output()
        else {
            return None;
        };
        let Ok(stdout) = String::from_utf8(output.stdout) else {
            return None;
        };
        log::info!("gio {stdout}");

        let content: Vec<_> = stdout
            .lines()
            .filter(|line| line.contains("activation_root"))
            .map(|line| line.to_owned())
            .map(|line| Removable::from_gio(line))
            .filter_map(|removable| removable.ok())
            .collect();

        if content.is_empty() {
            None
        } else {
            Some(Self { content, index: 0 })
        }
    }

    pub fn current(&mut self) -> Option<&mut Removable> {
        if self.content.is_empty() {
            None
        } else {
            Some(&mut self.content[self.index])
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct Removable {
    pub device_name: String,
    pub path: String,
    pub is_mounted: bool,
}

impl Removable {
    fn from_gio(line: String) -> Result<Self> {
        let line = line.replace("activation_root=mtp://", "");
        let device_name = line;
        let uid = users::get_current_uid();
        let path = format!("/run/user/{uid}/gvfs/mtp:host={device_name}");
        let pb_path = std::path::Path::new(&path);
        let is_mounted = pb_path.exists() && pb_path.read_dir()?.next().is_some();
        log::info!("gio {device_name} - is_mounted {is_mounted}");
        Ok(Self {
            device_name,
            path,
            is_mounted,
        })
    }

    pub fn mount(&mut self) -> Result<()> {
        if self.is_mounted {
            return Err(anyhow!("Already mounted {name}", name = self.device_name));
        }
        self.is_mounted = std::process::Command::new(GIO)
            .args(vec![
                "mount",
                &format!("mtp://{name}", name = self.device_name),
            ])
            .spawn()?
            .wait()?
            .success();
        Ok(())
    }

    pub fn umount(&mut self) -> Result<()> {
        if !self.is_mounted {
            return Err(anyhow!("Not mounted {name}", name = self.device_name));
        }
        self.is_mounted = std::process::Command::new(GIO)
            .args(vec![
                "mount",
                &format!("mtp://{name}", name = self.device_name),
                "-u",
            ])
            .spawn()?
            .wait()?
            .success();
        Ok(())
    }
}

impl_selectable_content!(Removable, RemovableDevices);