conf-embed 0.1.3

An experimental Rust crate for embedding user configuration (or anything really) in the executable of the program.
Documentation
/*
This file is responsible for handling config IO.
Since the config is stored in the binary of the program,
a few hacks have to be made to get it to work.
*/

// use crate::helpers::SliceBetween;
use std::env;
use std::fs::{self, File};
use std::io::{self, Write};

/* impl SliceBetween for String {
    fn slice_between(&self, start: &str, end: &str) -> Option<&str> {
        Some(&self[self.find(start)? + start.len()..self.find(end)? + end.len()])
    }
} */

// The reference string cannot be its own constant since it could change the data section,
// and it cannot be used with the concat! macro.

const CONFIG: &'static str = "----CONFIG-REF-START----00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";

// Just to be safe (making sure it doesn't go into the data section) I am returning a non-static string
pub fn get_config() -> io::Result<&'static str> {
    let mut len = None;
    for (i, c) in CONFIG[24 /* length of reference */..].chars().enumerate() {
        if c == '0' {
            if len.is_none() {
                len = Some(i);
            }
        } else if len.is_some() {
            len = None;
        }
    }

    if let Some(i) = len {
        return Ok(&CONFIG[24..24 + i]);
    }

    Err(io::Error::new(
        io::ErrorKind::Other,
        "Could not locate config.",
    ))
}

pub fn new_config(config: &str) -> io::Result<()> {
    // let mut new = String::new();
    // This SHOULDN'T be a const to avoid potentially mixing it with the original reference
    if config.len() > 2024 {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            "Config is too large. It must be no more than 2024 bytes.",
        ));
    }

    let conf_text = "----CONFIG-REF-START----";

    if config.contains(conf_text) {
        return Err(io::Error::new(
            io::ErrorKind::Other,
            format!("Config contains invalid string '{}'.", conf_text),
        ));
    }

    let exe_loc = env::current_exe()?;
    let current = fs::read(&exe_loc)?;
    let mut new = Vec::new();
    let mut index = None;
    for i in 0..current.len() {
        // 24 is the length of the ----CONFIG-REF-START---- text
        if current[i..i + 24] == *conf_text.as_bytes() {
            index = Some(i);
            break;
        }
    }

    if let Some(i) = index {
        // println!("i: {}", i);
        new.extend_from_slice(&current[0..i]);
        let new_out = conf_text.to_owned()
            + config
            + &"0".repeat(2024 /* Number of bytes avaliable to the config */ - config.len());
        // println!("New out len: {}", new_out.len());
        new.extend_from_slice(new_out.as_bytes());
        new.extend_from_slice(&current[i + 2048..]);

        /* if new.len() != current.len() {
            panic!()
        } */

        fs::rename(&exe_loc, exe_loc.to_str().unwrap().to_owned() + ".old")?;
        File::create(exe_loc)?.write_all(&new)?;

        return Ok(());
    }

    Err(io::Error::new(
        io::ErrorKind::Other,
        "Could not locate config.",
    ))
}