init: initial code
This commit is contained in:
commit
7cf6adc479
11 changed files with 142 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/Cargo.lock
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
@ -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"
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Cynewave
|
||||
Cynewave is a WIP fast and configurable backup system written in Rust.
|
0
log.txt
Normal file
0
log.txt
Normal file
81
src/lib.rs
Normal file
81
src/lib.rs
Normal file
|
@ -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<String>,
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
29
src/logger.rs
Normal file
29
src/logger.rs
Normal file
|
@ -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(())
|
||||
}
|
||||
|
13
src/main.rs
Normal file
13
src/main.rs
Normal file
|
@ -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();
|
||||
|
||||
}
|
5
testconfig.toml
Normal file
5
testconfig.toml
Normal file
|
@ -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
|
||||
|
0
testfiles/src/a/file3.txt
Normal file
0
testfiles/src/a/file3.txt
Normal file
1
testfiles/src/file1.txt
Normal file
1
testfiles/src/file1.txt
Normal file
|
@ -0,0 +1 @@
|
|||
a
|
1
testfiles/src/file2.txt
Normal file
1
testfiles/src/file2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
b
|
Loading…
Reference in a new issue