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