rura 1.6.0

Interactive TUI pipeline editor built for rapid iteration
use anyhow::Result;
use itertools::Itertools;
use log::debug;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::PathBuf;

pub trait FileSaver {
    fn save(&self, path: PathBuf, content: Vec<u8>) -> Result<()>;
    fn save_script(&self, path: PathBuf, content: Vec<u8>) -> Result<()>;
}

pub struct FileSavers;

impl FileSavers {
    pub fn new(shell: &str) -> Box<dyn FileSaver> {
        #[cfg(unix)]
        return Box::new(UnixFileSaver {
            shell: shell.into(),
        });
        #[cfg(windows)]
        return Box::new(WindowsFileSaver {});
    }
}

#[cfg(unix)]
struct UnixFileSaver {
    shell: String,
}

#[cfg(unix)]
impl FileSaver for UnixFileSaver {
    fn save(&self, path: PathBuf, content: Vec<u8>) -> Result<()> {
        UnixFileSaver::save(path, content, 0o644)
    }

    fn save_script(&self, path: PathBuf, content: Vec<u8>) -> Result<()> {
        let shebang = format!("#!/usr/bin/env {}\n\n", self.shell);
        let full_content = shebang
            .bytes()
            .into_iter()
            .chain(content.into_iter())
            .collect_vec();
        UnixFileSaver::save(path, full_content, 0o755)
    }
}

#[cfg(unix)]
impl UnixFileSaver {
    fn save(path: PathBuf, content: Vec<u8>, mode: u32) -> Result<()> {
        use std::os::unix::fs::OpenOptionsExt;

        let mut file = OpenOptions::new()
            .create_new(true)
            .write(true)
            .mode(mode)
            .open(&path)?;

        debug!("Saving with len: {:?}", content.len());

        file.write_all(&content)?;

        debug!("Successfully saved file: {:?}", path);

        Ok(())
    }
}

#[cfg(windows)]
struct WindowsFileSaver {}

#[cfg(windows)]
impl FileSaver for WindowsFileSaver {
    fn save(&self, path: PathBuf, content: Vec<u8>) -> Result<()> {
        let mut file = OpenOptions::new()
            .create_new(true)
            .write(true)
            .open(&path)?;

        debug!("Saving with len: {:?}", content.len());

        file.write_all(&content)?;

        debug!("Successfully saved file: {:?}", path);

        Ok(())
    }

    fn save_script(&self, path: PathBuf, content: Vec<u8>) -> Result<()> {
        self.save(path, content)
    }
}