Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect wizard #23

Merged
merged 31 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c74756e
reading entries in output directory
mxwt00 Dec 7, 2023
263f5a0
removing everything inside output directory
mxwt00 Dec 7, 2023
b039b46
added question if the user really wants to delete the entries
mxwt00 Dec 7, 2023
60bcf27
Connect wizard: lsblk integration
mxwt00 Dec 13, 2023
77e60a6
added select list from dialoguer
mxwt00 Dec 13, 2023
88a3131
refactored lsblk device list
mxwt00 Dec 13, 2023
efd62ae
connect wizard connect function complete
mxwt00 Dec 13, 2023
6962ba6
added todo
mxwt00 Dec 13, 2023
a826b81
Mounting with sys_mount
mxwt00 Dec 15, 2023
5fe3169
switching from crate sys_mount to nix
mxwt00 Dec 22, 2023
b874224
wip: switching from crate sys_mount to nix: unmounting not working
mxwt00 Dec 22, 2023
1a9e082
Revert "wip: switching from crate sys_mount to nix: unmounting not wo…
mxwt00 Dec 22, 2023
8be2f11
Revert "switching from crate sys_mount to nix"
mxwt00 Dec 22, 2023
6f867ca
auto unmounting previously mounted devices at .hydrophonitor
mxwt00 Dec 22, 2023
f2ab0f2
added disconnect command
mxwt00 Dec 22, 2023
bca30e1
refactored error messages of disconnect command
mxwt00 Dec 22, 2023
55a1f91
modified connect wizard and clean command to match new cargo workspace
mxwt00 Dec 23, 2023
c4e80bb
moved parts of the connect logic into lib
mxwt00 Jan 16, 2024
e385ccc
refactored clean command: clean lib no longer requires manual user in…
mxwt00 Jan 16, 2024
a210188
changed mount locations to /var/lib/hydrophonitor/ device and temp_de…
mxwt00 Jan 16, 2024
224ba02
removed unnecessary check after mounting for output directory
mxwt00 Jan 16, 2024
109974c
added check for invalid directories before cleaning
mxwt00 Jan 16, 2024
1eb51e6
integrated connect wizard into other subcommands. This made the conne…
mxwt00 Jan 17, 2024
f8f969f
added comment
mxwt00 Jan 17, 2024
c11cb28
refactored error handling in lib/clean.rs
mxwt00 Jan 17, 2024
a146d23
refactoring
mxwt00 Jan 17, 2024
13c9075
modified PermissionDenied message
mxwt00 Jan 17, 2024
02c59dc
changed error handling to use with_context and passing errors through…
mxwt00 Jan 18, 2024
23876dd
changed yes/no dialog in src/connect.rs to use Confirm of dialoguer
mxwt00 Jan 18, 2024
b73022e
changed yes/no dialog in cli/clean.rs to use Confirm of dialoguer
mxwt00 Jan 18, 2024
a8ec26e
changed error handling in function create_dir_if_not_existing of file…
mxwt00 Jan 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 370 additions & 3 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ members = [
anyhow = "1.0.70"
clap = { version = "4.1.11", features = ["derive"] }
clap-verbosity-flag = "2.1.0"
dialoguer = "0.11.0"
dirs = "5.0.1"
env_logger = "0.10.1"
hound = "3.5.0"
indicatif = "0.17.3"
lazy_static = "1.4.0"
log = "0.4"
sys-mount = "2.1.0"
walkdir = "2.3.3"
4 changes: 4 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ hydrophonitor-lib = { path = "../lib" }
anyhow.workspace = true
clap.workspace = true
clap-verbosity-flag.workspace = true
dialoguer.workspace = true
dirs.workspace = true
env_logger.workspace = true
hound.workspace = true
indicatif.workspace = true
lazy_static.workspace = true
log.workspace = true
sys-mount.workspace = true
walkdir.workspace = true
54 changes: 50 additions & 4 deletions cli/src/clean.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,64 @@
use std::path::PathBuf;

use anyhow::{anyhow, Context, Result};
use clap::Parser;
use dialoguer::Confirm;
use log::info;

use hydrophonitor_lib::{clean as clean_lib, connect};

use crate::connect::connect;

#[derive(Parser, Debug)]
#[clap(about = "This command removes all deployment data from the given device's /output path")]
pub struct Clean {
///Path to USB mass storage or SD card where data will be deleted from.
#[clap(short, long, required = true)]
pub device: PathBuf,
#[clap(short, long)]
device: Option<PathBuf>,
}

impl Clean {
pub fn clean(&self) {
info!("Cleaning device at {:?}", self.device);
pub fn clean(&mut self) -> Result<()> {
//create path to output folder
let _mount;
let mut output_dir: PathBuf;
match &self.device {
Some(device) => output_dir = device.clone(),
None => {
_mount = connect().with_context(|| "connecting to device failed:")?;
output_dir = connect::MOUNT_PATH.clone();
}
}
output_dir.push("output");

// Show deployments and ask for confirmation
match clean_lib::get_deployments_of_device(&output_dir) {
Some(deployments) => {
info!("Cleaning device at {:?}", self.device);

if !deployments.is_empty() {
dbg!(deployments);
satukoskinen marked this conversation as resolved.
Show resolved Hide resolved

if !Confirm::new()
.with_prompt("Do you really want to delete these deployments? (y/n)")
.default(true)
.interact()? {
println!("Aborting!");
return Ok(());
}
} else {
println!("The directory is already empty!");
return Ok(());
}
}
None => {
return Err(anyhow!("{output_dir:?} is not a valid device! please select a hydrophonitor device with output folder!"));
satukoskinen marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Clean device
clean_lib::clear_directory(&output_dir).with_context(|| "Error cleaning directory")?;
println!("Successfully cleaned directory!");
Ok(())
}
}
41 changes: 41 additions & 0 deletions cli/src/connect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use anyhow::{Context, Result};
satukoskinen marked this conversation as resolved.
Show resolved Hide resolved
use dialoguer::{Confirm, Select};
use sys_mount::{Mount, UnmountDrop};

use hydrophonitor_lib::connect as connect_lib;

//Runs the connect wizard to select and mount the hydrophonitor device. It returns a mount object that defines the lifetime of the mount.
pub fn connect() -> Result<UnmountDrop<Mount>> {
let devices = connect_lib::get_device_list().with_context(|| "Failed to get device list")?;
let suitable_device = connect_lib::find_suitable_device(&devices).with_context(|| "Getting device failed")?;
let selected_device = match suitable_device {
Some(dev) => {
let connect_to_device = Confirm::new()
.with_prompt(format!("Found potential Hydrophonitor device '{dev}'. Do you want to connect to this device?"))
.default(true)
.interact()?;
if connect_to_device {
dev
} else {
manual_connect(&devices)
}
}
None => {
println!("No device found matching the Hydrophonitor disk.");
manual_connect(&devices)
}
};

let mount = connect_lib::mount_device(selected_device).with_context(|| "Mounting device failed")?;
println!("successfully connected to device {selected_device}!");
Ok(mount)
}

fn manual_connect(devices: &[String]) -> &String {
let selection = Select::new()
.with_prompt("Please choose a device from the list:")
.items(devices)
.default(0)
.interact();
&devices[selection.unwrap_or_default()]
}
6 changes: 4 additions & 2 deletions cli/src/import.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::path::PathBuf;

use anyhow::Result;
use clap::Parser;

use hydrophonitor_lib::import::import;
Expand Down Expand Up @@ -28,7 +29,8 @@ pub struct Import {

impl Import {
//TODO old logic; has to be changed to match new commands
pub fn import(&mut self) {
import(&mut self.device, self.output.clone())
pub fn import(&mut self) -> Result<()> {
import(&mut self.device, self.output.clone());
Ok(())
}
}
7 changes: 6 additions & 1 deletion cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::import::Import;

mod import;
mod clean;
mod connect;

#[derive(Subcommand)]
#[clap(about = "A tool to record audio on Linux using the command line.")]
Expand All @@ -32,9 +33,13 @@ fn main() {

env_logger::builder().filter_level(verbose.log_level_filter()).init();

match commands {
let result = match commands {
Commands::Import(mut import) => import.import(),
Commands::Clean(mut clean) => clean.clean(),
};

if let Err(err) = result {
println!("{err:?}")
}
}

3 changes: 3 additions & 0 deletions lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ edition = "2021"

[dependencies]
anyhow.workspace = true
dirs.workspace = true
hound.workspace = true
indicatif.workspace = true
lazy_static.workspace = true
log.workspace = true
sys-mount.wokspace = true
walkdir.workspace = true
28 changes: 28 additions & 0 deletions lib/src/clean.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::fs;
use std::path::PathBuf;

use anyhow::{Context, Error};
use log::warn;

pub fn get_deployments_of_device(output_dir: &PathBuf) -> Option<Vec<String>> {
match fs::read_dir(output_dir) {
Ok(dir) => {
let mut entries = Vec::new();
// Iterate over the entries in the directory
for entry in dir {
entries.push(entry.unwrap().file_name().into_string().unwrap());
}
Some(entries)
}
Err(e) => {
warn!("Error opening the directory {:?}: {}", output_dir, e);
None
}
}
}

pub fn clear_directory(output_dir: &PathBuf) -> Result<(), Error> {
fs::remove_dir_all(output_dir).with_context(|| format!("Removing everything in directory {:?} failed", &output_dir))?;
fs::create_dir(output_dir).with_context(|| format!("Creating new empty output directory in {:?} failed", &output_dir))?;
Ok(())
}
78 changes: 78 additions & 0 deletions lib/src/connect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::fs;
use std::io::ErrorKind;
use std::path::PathBuf;
use std::process::Command;

use anyhow::{anyhow, Context, Result};
use lazy_static::lazy_static;
use log::debug;
use sys_mount::{Mount, unmount, UnmountDrop, UnmountFlags};

lazy_static! {
pub static ref MOUNT_PATH: PathBuf = PathBuf::from("/var/lib/hydrophonitor/device");
static ref TEMP_MOUNT_PATH: PathBuf = PathBuf::from("/var/lib/hydrophonitor/temp_device");
}

//gets all available devices with lsblk
pub fn get_device_list() -> Result<Vec<String>> {
let output = Command::new("lsblk").arg("-l").output().with_context(|| "Failed to run lsblk!")?;
let output = String::from_utf8_lossy(&output.stdout);
let devices: Vec<&str> = output.lines().collect();
let mut devices_cropped: Vec<String> = Vec::new();
for device in devices.iter() {
let mut device_columns = device.split_whitespace();
let cropped_device = device_columns.next().unwrap_or_default().to_string();
let device_type = device_columns.nth(4);
if device_type.unwrap_or_default() == "part" {
devices_cropped.push(cropped_device);
}
}
Ok(devices_cropped)
}

pub fn find_suitable_device(devices: &[String]) -> Result<Option<&String>> {
create_dir_if_not_existing(&TEMP_MOUNT_PATH).with_context(|| format!("Failed to create dir {:?}", &*TEMP_MOUNT_PATH))?;

//Checking all devices for an output directory
for device in devices.iter() {
let device_path = format!("/dev/{device}");
match Mount::builder()
.mount_autodrop(&device_path, &*TEMP_MOUNT_PATH, UnmountFlags::DETACH) {
Ok(_) =>
{
let read_dir_result = fs::read_dir(format!("{}/output", TEMP_MOUNT_PATH.to_str().unwrap()));
if read_dir_result.is_ok() {
return Ok(Some(device));
}
}
Err(e) => {
if e.kind() == ErrorKind::PermissionDenied {
return Err(anyhow!("Please execute the command with sudo rights or specify a device path with access rights!"));
}
debug!("Mount of device {device_path} failed: {e}")
}
}
}
Ok(None)
}

pub fn mount_device(device: &String) -> Result<UnmountDrop<Mount>> {
if unmount(&*MOUNT_PATH, UnmountFlags::empty()).is_ok() { debug!("unmounting previously mounted device at {:?}", &*MOUNT_PATH) }
create_dir_if_not_existing(&MOUNT_PATH).with_context(|| format!("Failed to create dir {:?}", &*MOUNT_PATH))?;

let device_path = format!("/dev/{device}");
Mount::builder().mount_autodrop(device_path, &*MOUNT_PATH, UnmountFlags::DETACH).with_context(|| "Mount failed")
}

fn create_dir_if_not_existing(dir_path: &PathBuf) -> Result<()> {
match fs::create_dir_all(dir_path) {
Ok(_) => Ok(()),
Err(e) => {
if e.kind() != ErrorKind::PermissionDenied {
Err(anyhow!("Please execute the command with sudo rights or specify a device path with access rights!"))
} else {
Err(anyhow!(e))
}
}
}
}
2 changes: 2 additions & 0 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod import;
pub mod clean;
pub mod connect;