Renaming a file to the parent directory name

I'm new to rust and looking for feedback. The goal of this small program is to find files created in a child directory and rename the file so it is the same name as the directory with the original extension and not overwriting any file that already exists. I'm trying to use this to learn and would really like to know anything I can do better and why the suggestion is better.

extern crate notify;

use notify::DebouncedEvent::{Create, Error};
use notify::{watcher, RecursiveMode, Watcher};
use std::env;
use std::error::Error as GenericError;
use std::fmt;
use std::fs::rename;
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;

#[derive(Debug)]
struct FileWatcherError {
    details: String,
}

impl FileWatcherError {
    fn new(msg: &str) -> FileWatcherError {
        FileWatcherError {
            details: msg.to_string(),
        }
    }
}

impl fmt::Display for FileWatcherError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.details)
    }
}

impl GenericError for FileWatcherError {
    fn description(&self) -> &str {
        &self.details
    }
}

fn get_new_file_name(fpath: &str) -> String {
    let path_parts: Vec<&str> = fpath.rsplitn(3, "/").collect();
    let file_parts: Vec<&str> = path_parts[0].rsplitn(2, ".").collect();
    match file_parts.len() {
        1 => [path_parts[2], path_parts[1], path_parts[1]].join("/"),
        _ => [
            path_parts[2],
            path_parts[1],
            &[path_parts[1], file_parts[0]].join("."),
        ]
        .join("/"),
    }
}

fn rename_file(fpath: &str) -> Result<(), FileWatcherError> {
    let error: String;
    let file = get_new_file_name(&fpath);

    match file == fpath {
        true => return Ok(()),
        false => {
            if !Path::new(&fpath).exists() {
                error = format!("Can't rename {}, it doesn't exist!", fpath);
            } else if Path::new(&file).exists() {
                error = format!("Can't rename {}, destination, {}, exists!", fpath, file);
            } else {
                match rename(fpath, file.to_string()) {
                    Ok(_) => {
                        println!("{} renamed to {}", fpath, file);
                        return Ok(())
                    },
                    Err(e) => {
                        error = e.to_string();
                    }
                }
            }
        }
    }

    println!("{}", error);
    Err(FileWatcherError::new(&error))
}

fn check_args() -> Result<String, FileWatcherError> {
    // Get the args we were called with
    let args: Vec<String> = env::args().collect();

    match args.len() {
        1 => Ok("./jive".to_string()),
        2 => match Path::new(&args[1]).is_dir() {
            true => Ok(args[1].to_string()),
            _ => {
                let error = format!("{} does not appear to be a valid path!", &args[1]);
                Err(FileWatcherError::new(&error))
            }
        },
        _ => {
            let error = format!(
                "Only 0 or 1 arguments allowed, {} given, exiting!",
                args.len() - 1
            );
            Err(FileWatcherError::new(&error))
        }
    }
}

fn main() {
    let file_path = match check_args() {
        Ok(fp) => fp,
        Err(error) => {
            println!("Error: {}", error);
            return ();
        }
    };

    // Create a channel to receive the events.
    let (tx, rx) = channel();

    // Create a watcher object, delivering debounced events.
    // The notification back-end is selected based on the platform.
    let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();

    // Add a path to be watched. All files and directories at that path and
    // below will be monitored for changes.
    watcher.watch(file_path, RecursiveMode::Recursive).unwrap();

    loop {
        let _: Result<(), FileWatcherError> = match rx.recv() {
            Ok(event) => match event {
                Create(path) => rename_file(path.to_str().unwrap()),
                Error(err, path) => {
                    let error = format!("Error: {:?},path: {:?}", err, path);
                    println!("{}", error);
                    Err(FileWatcherError::new(&error))
                }
                _ => Ok(()),
            },
            Err(e) => {
                let error = format!("watch error: {:?}", e);
                println!("{}", error);
                Err(FileWatcherError::new(&error))
            }
        };
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.