commit 7cf6adc479debdff9554bb7409f768646602dbfc Author: Xory Date: Sun Sep 8 15:50:34 2024 +0300 init: initial code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9129af3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cynewave" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.210", features = [ "derive" ] } +toml = "0.8.19" diff --git a/README.md b/README.md new file mode 100644 index 0000000..61375f3 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Cynewave +Cynewave is a WIP fast and configurable backup system written in Rust. diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b3d604f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,81 @@ +use serde::Deserialize; +use toml; +use std::{fs, io, path::Path}; +mod logger; +use logger::{log, LogLevel}; + + +#[derive(Deserialize, Debug, PartialEq)] +pub struct StageConfig { + pub src_files: Vec, + pub target_dir: String, + pub recursive: bool +} + + +#[derive(Deserialize, Debug, PartialEq)] +pub struct Config { + pub stage_1: StageConfig +} + +impl Config { + pub fn new(config_toml: String) -> Config { + toml::from_str(&config_toml).expect("Invalid Config") + } +} + +pub fn stage1(config: &Config) -> io::Result<()> { + for file in &config.stage_1.src_files { + let filename = match file.split('/').last() { + Some(filename) => filename, + None => { eprintln!("E: Failed to derive filename from {file}"); break } + }; + + let target_path = format!("{}/{filename}", &config.stage_1.target_dir); + + if Path::new(file).is_file() { + if let Err(err) = fs::copy(file, target_path) { + log("./log.txt".to_string(), err.to_string(), LogLevel::Error)?; + }; + } else if Path::new(file).is_dir() && config.stage_1.recursive { + if let Err(err) = fs::create_dir(target_path) { + log("./log.txt".to_string(), err.to_string(), LogLevel::Error)?; + } + for entry in fs::read_dir(file)? { + let target_dir = format!("{}/{}", &config.stage_1.target_dir, filename); + let file = entry?; + if let Err(err) = fs::copy(Path::new(&file.path()), format!("{}/{}", target_dir, file.file_name().to_str().unwrap())) { + log("./log.txt".to_string(), err.to_string(), LogLevel::Error)?; + }; + } + } else { + log("./log.txt".to_string(), "Copy conditions not matched for file {file}, skipping.".to_string(), LogLevel::Warning)?; + } + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn config_parser_test() { + let toml_config = "[stage_1] + src_files = [ './testfiles/src/file1.txt', './testfiles/src/file2.txt', './testfiles/src/a' ] + target_dir = './testfiles/dir' + recursive = true"; + + let parsed_config = Config::new(toml_config.to_string()); + assert_eq!( + Config { + stage_1: StageConfig { + src_files: vec![ "./testfiles/src/file1.txt".to_string(), "./testfiles/src/file2.txt".to_string(), "./testfiles/src/a".to_string() ], + target_dir: "./testfiles/dir".to_string(), + recursive: true + } + }, + parsed_config + ); + } +} diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..c198d90 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,29 @@ +use std::io::{Result, Write}; +use std::fs::OpenOptions; + + +pub enum LogLevel { + Error, + Warning, + Info +} + +pub fn log(logfile: String, text: String, level: LogLevel) -> Result<()> { + let prefix = match level { + LogLevel::Error => "E".to_string(), + LogLevel::Warning => "W".to_string(), + LogLevel::Info => "I".to_string() + }; + let msg = format!("{prefix}: {text}"); + println!("{msg}"); + let mut logfile = OpenOptions::new() + .read(false) + .write(false) + .append(true) + .open(logfile)?; + logfile.write(msg.as_bytes())?; + logfile.write(b" +")?; + Ok(()) +} + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..58864b9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,13 @@ +use cynewave::{Config, stage1}; +mod logger; +use logger::{log, LogLevel}; +use std::fs; + +fn main() { + let config_file: String = fs::read_to_string("./testconfig.toml").expect("Failed to read config"); + let config: Config = Config::new(config_file); + dbg!(&config); + log("./log.txt".to_string(), "Beginning stage 1 backup.".to_string(), LogLevel::Info).unwrap(); + stage1(&config).unwrap(); + +} diff --git a/testconfig.toml b/testconfig.toml new file mode 100644 index 0000000..b2e6aa0 --- /dev/null +++ b/testconfig.toml @@ -0,0 +1,5 @@ +[stage_1] +src_files = [ "./testfiles/src/file1.txt", "./testfiles/src/file2.txt", "./testfiles/src/a" ] +target_dir = "./testfiles/dir" +recursive = false + diff --git a/testfiles/src/a/file3.txt b/testfiles/src/a/file3.txt new file mode 100644 index 0000000..e69de29 diff --git a/testfiles/src/file1.txt b/testfiles/src/file1.txt new file mode 100644 index 0000000..7898192 --- /dev/null +++ b/testfiles/src/file1.txt @@ -0,0 +1 @@ +a diff --git a/testfiles/src/file2.txt b/testfiles/src/file2.txt new file mode 100644 index 0000000..6178079 --- /dev/null +++ b/testfiles/src/file2.txt @@ -0,0 +1 @@ +b