make functions part of Refractr object

This commit is contained in:
Bryson Steck 2025-03-05 20:45:35 -07:00
parent 3b7d30f492
commit 4791e1dd84
Signed by: brysonsteck
SSH key fingerprint: SHA256:XpKABw/nP4z8UVaH+weLaBnEOD86+cVwif+QjuYLGT4
4 changed files with 213 additions and 208 deletions

View file

@ -1,10 +1,4 @@
pub struct Refractr {
pub verbose: u8,
pub pid: u32,
pub unix: bool
}
pub fn verbose(level: u8, msg_lvl: u8, msg: String) { pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
if level < msg_lvl { return }; if level < msg_lvl { return };
let mut prefix = String::new(); let mut prefix = String::new();

View file

@ -1,4 +1,5 @@
use crate::common; use crate::common;
use crate::refractr::Refractr;
use core::fmt; use core::fmt;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
@ -106,7 +107,7 @@ pub struct Schedule {
pub interval: Option<i32>, pub interval: Option<i32>,
} }
pub fn read_config(paths: Vec<PathBuf>, refractr: &common::Refractr) -> Vec<ConfigFile> { pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Vec<ConfigFile> {
let mut config_files: Vec<ConfigFile> = vec![]; let mut config_files: Vec<ConfigFile> = vec![];
for path in paths { for path in paths {
common::verbose(refractr.verbose, 1, format!("Reading config file: \"{}\"", String::from(path.to_string_lossy()))); common::verbose(refractr.verbose, 1, format!("Reading config file: \"{}\"", String::from(path.to_string_lossy())));

View file

@ -6,6 +6,7 @@ use clap::Parser;
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
use users; use users;
use crate::refractr::Refractr;
#[derive(Parser)] #[derive(Parser)]
#[command(name = "refractor")] #[command(name = "refractor")]
@ -33,7 +34,7 @@ fn get_config_default() -> &'static str {
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let args = Args::parse(); let args = Args::parse();
let refractr = common::Refractr { let refractr = Refractr {
verbose: args.verbose, verbose: args.verbose,
pid: process::id(), pid: process::id(),
unix: cfg!(unix) unix: cfg!(unix)
@ -65,7 +66,7 @@ fn main() -> std::io::Result<()> {
} }
common::verbose(refractr.verbose, 1, format!("Config file(s) read successfully")); common::verbose(refractr.verbose, 1, format!("Config file(s) read successfully"));
refractr::run(refractr, cfgs) refractr.run(cfgs)
} }
} }

View file

@ -1,8 +1,8 @@
use git2::build::CheckoutBuilder; use git2::build::CheckoutBuilder;
use git2::{BranchType, Cred, PushOptions, RemoteCallbacks, Repository}; use git2::{Cred, PushOptions, RemoteCallbacks, Repository};
use sha2::{Sha256, Digest}; use sha2::{Sha256, Digest};
use crate::common::{self, Refractr}; use crate::common;
use crate::config::{Config, ConfigFile}; use crate::config::{Config, ConfigFile};
use std::fs; use std::fs;
use std::env; use std::env;
@ -14,6 +14,12 @@ use hex;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
pub struct Refractr {
pub verbose: u8,
pub pid: u32,
pub unix: bool
}
struct OpenedRepository { struct OpenedRepository {
repo: Repository, repo: Repository,
path: String, path: String,
@ -21,14 +27,15 @@ struct OpenedRepository {
cfg: Config, cfg: Config,
} }
fn set_up_work_dir(work_dir: PathBuf) -> String { impl Refractr {
fn set_up_work_dir(&self, work_dir: PathBuf) -> String {
if let Err(e) = fs::create_dir_all(&work_dir) { if let Err(e) = fs::create_dir_all(&work_dir) {
panic!("refractr: could not create working directory: {}: {}", work_dir.to_string_lossy(), e) panic!("refractr: could not create working directory: {}: {}", work_dir.to_string_lossy(), e)
} }
work_dir.to_string_lossy().to_string() work_dir.to_string_lossy().to_string()
} }
fn get_branches(repo: &Repository, branches: &Option<Vec<String>>, refs: bool) -> Vec<String> { fn get_branches(&self, repo: &Repository, branches: &Option<Vec<String>>, refs: bool) -> Vec<String> {
match branches { match branches {
Some(repo_branches) => { Some(repo_branches) => {
if refs { if refs {
@ -63,11 +70,11 @@ fn get_branches(repo: &Repository, branches: &Option<Vec<String>>, refs: bool) -
} }
} }
fn fast_forward(refractr: &Refractr, repo_dir: &str, branches: &Option<Vec<String>>) -> Result<(), Error> { fn fast_forward(&self, repo_dir: &str, branches: &Option<Vec<String>>) -> Result<(), Error> {
let repo = Repository::open(repo_dir)?; let repo = Repository::open(repo_dir)?;
let branch_list: Vec<String> = get_branches(&repo, branches, false); let branch_list: Vec<String> = self.get_branches(&repo, branches, false);
common::verbose(refractr.verbose, 2, format!("Pulling origin")); common::verbose(self.verbose, 2, format!("Pulling origin"));
repo.find_remote("origin")?.fetch(&branch_list, None, None)?; repo.find_remote("origin")?.fetch(&branch_list, None, None)?;
let fetch_head = repo.find_reference("FETCH_HEAD")?; let fetch_head = repo.find_reference("FETCH_HEAD")?;
@ -87,14 +94,14 @@ fn fast_forward(refractr: &Refractr, repo_dir: &str, branches: &Option<Vec<Strin
} }
fn make_remotes<'a> (refractr: &Refractr, repo: &'a Repository, cfg: &ConfigFile) -> Vec<String> { fn make_remotes<'a> (&self, repo: &'a Repository, cfg: &ConfigFile) -> Vec<String> {
// create remotes for each "to" repo // create remotes for each "to" repo
let mut remote_list = Vec::new(); let mut remote_list = Vec::new();
for to in &cfg.config.to { for to in &cfg.config.to {
let mut hasher = Sha256::new(); let mut hasher = Sha256::new();
hasher.update(to); hasher.update(to);
let remote_id = format!("refractr-{}", &hex::encode(hasher.finalize())[..8]); let remote_id = format!("refractr-{}", &hex::encode(hasher.finalize())[..8]);
common::verbose(refractr.verbose, 2, format!("Attempting to create remote {} for url {}", remote_id, to)); common::verbose(self.verbose, 2, format!("Attempting to create remote {} for url {}", remote_id, to));
match repo.remote(remote_id.as_str(), to) { match repo.remote(remote_id.as_str(), to) {
Ok(_) => remote_list.push(remote_id), Ok(_) => remote_list.push(remote_id),
Err(e) => { Err(e) => {
@ -111,17 +118,17 @@ fn make_remotes<'a> (refractr: &Refractr, repo: &'a Repository, cfg: &ConfigFile
remote_list remote_list
} }
fn push_remotes(refractr: &Refractr, cfg: &Config, repo: &Repository, remote_list: &Vec<String>) { fn push_remotes(&self, cfg: &Config, repo: &Repository, remote_list: &Vec<String>) {
for id in remote_list { for id in remote_list {
let mut remote = repo.find_remote(&id).unwrap(); let mut remote = repo.find_remote(&id).unwrap();
common::verbose(refractr.verbose, 1, format!("Pushing to remote: {}", remote.url().unwrap())); common::verbose(self.verbose, 1, format!("Pushing to remote: {}", remote.url().unwrap()));
let mut callbacks = RemoteCallbacks::new(); let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|_,_,_| Cred::ssh_key("git", None, &Path::new(&cfg.git.ssh_identity_file), None)); callbacks.credentials(|_,_,_| Cred::ssh_key("git", None, &Path::new(&cfg.git.ssh_identity_file), None));
let mut push_options = PushOptions::new(); let mut push_options = PushOptions::new();
push_options.remote_callbacks(callbacks); push_options.remote_callbacks(callbacks);
let mut refs = Vec::new(); let mut refs = Vec::new();
let strings = get_branches(&repo, &cfg.branches, true); let strings = self.get_branches(&repo, &cfg.branches, true);
for branch in &strings { for branch in &strings {
refs.push(branch.as_str()); refs.push(branch.as_str());
} }
@ -135,7 +142,7 @@ fn push_remotes(refractr: &Refractr, cfg: &Config, repo: &Repository, remote_lis
} }
} }
fn looper(refractr: Refractr, repos: Vec<OpenedRepository>) { fn looper(&self, repos: Vec<OpenedRepository>) {
let mut current_ints = Vec::new(); let mut current_ints = Vec::new();
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
@ -149,7 +156,7 @@ fn looper(refractr: Refractr, repos: Vec<OpenedRepository>) {
r.store(false, Ordering::SeqCst); r.store(false, Ordering::SeqCst);
}).expect("Failed to set ^C handler"); }).expect("Failed to set ^C handler");
common::verbose(refractr.verbose, 1, format!("Starting scheduled loop")); common::verbose(self.verbose, 1, format!("Starting scheduled loop"));
let min = *current_ints.iter().min().unwrap(); let min = *current_ints.iter().min().unwrap();
let mut do_break = false; let mut do_break = false;
while !do_break { while !do_break {
@ -157,18 +164,18 @@ fn looper(refractr: Refractr, repos: Vec<OpenedRepository>) {
let sleep_int = time::Duration::from_secs(min); let sleep_int = time::Duration::from_secs(min);
let now = time::Instant::now(); let now = time::Instant::now();
common::verbose(refractr.verbose, 2, format!("Sleeping for {} seconds", sleep_int.as_secs())); common::verbose(self.verbose, 2, format!("Sleeping for {} seconds", sleep_int.as_secs()));
while running.load(Ordering::SeqCst) { while running.load(Ordering::SeqCst) {
thread::sleep(time::Duration::from_secs(1)); thread::sleep(time::Duration::from_secs(1));
if now.elapsed().as_secs() >= sleep_int.as_secs() { if now.elapsed().as_secs() >= sleep_int.as_secs() {
common::verbose(refractr.verbose, 3, format!("Thread has awoken!")); common::verbose(self.verbose, 3, format!("Thread has awoken!"));
for i in 0..count { for i in 0..count {
current_ints[i] -= now.elapsed().as_secs(); current_ints[i] -= now.elapsed().as_secs();
if i <= 0 { if i <= 0 {
current_ints[i] = original_ints[i].clone(); current_ints[i] = original_ints[i].clone();
common::verbose(refractr.verbose, 2, format!("Interval for {} has arrived, pulling", repos[i].cfg.from)); common::verbose(self.verbose, 2, format!("Interval for {} has arrived, pulling", repos[i].cfg.from));
let _ = fast_forward(&refractr, &repos[i].path, &repos[i].cfg.branches); let _ = self.fast_forward(&repos[i].path, &repos[i].cfg.branches);
push_remotes(&refractr, &repos[i].cfg, &repos[i].repo, &repos[i].remotes); self.push_remotes(&repos[i].cfg, &repos[i].repo, &repos[i].remotes);
} }
} }
do_break = false; do_break = false;
@ -176,18 +183,18 @@ fn looper(refractr: Refractr, repos: Vec<OpenedRepository>) {
} }
} }
} }
common::verbose(refractr.verbose, 1, format!("Exited looper due to ^C")); common::verbose(self.verbose, 1, format!("Exited looper due to ^C"));
} }
pub fn run(refractr: Refractr, cfgs: Vec<ConfigFile>) -> std::io::Result<()> { pub fn run(&self, cfgs: Vec<ConfigFile>) -> std::io::Result<()> {
common::verbose(refractr.verbose, 3, format!("Starting main refractr loop")); common::verbose(self.verbose, 3, format!("Starting main refractr loop"));
let mut loop_repos = Vec::new(); let mut loop_repos = Vec::new();
for cfg in cfgs { for cfg in cfgs {
// set up the working directory // set up the working directory
common::verbose(refractr.verbose, 3, format!("Loaded config: {}", cfg.path)); common::verbose(self.verbose, 3, format!("Loaded config: {}", cfg.path));
let path_str = set_up_work_dir(match &cfg.config.work_dir { let path_str = self.set_up_work_dir(match &cfg.config.work_dir {
None => { None => {
if cfg!(windows) { if cfg!(windows) {
PathBuf::from(format!("\"{}\\refractr\"", match env::var("TEMP") { PathBuf::from(format!("\"{}\\refractr\"", match env::var("TEMP") {
@ -201,20 +208,20 @@ pub fn run(refractr: Refractr, cfgs: Vec<ConfigFile>) -> std::io::Result<()> {
Some(path) => PathBuf::from(path) Some(path) => PathBuf::from(path)
}); });
common::verbose(refractr.verbose, 2, format!("Created working directory: {}", &path_str)); common::verbose(self.verbose, 2, format!("Created working directory: {}", &path_str));
let repo_name = match &cfg.config.from.split("/").last() { let repo_name = match &cfg.config.from.split("/").last() {
Some(split) => split.to_string(), Some(split) => split.to_string(),
None => panic!("refractr: failed to parse repository name") None => panic!("refractr: failed to parse repository name")
}; };
// make initial clone // make initial clone
common::verbose(refractr.verbose, 1, format!("Cloning repository: {}", &cfg.config.from)); common::verbose(self.verbose, 1, format!("Cloning repository: {}", &cfg.config.from));
let repo_dir = format!("{}/{}", &path_str, repo_name); let repo_dir = format!("{}/{}", &path_str, repo_name);
let repo = match Repository::clone(&cfg.config.from, Path::new(&repo_dir)) { let repo = match Repository::clone(&cfg.config.from, Path::new(&repo_dir)) {
Ok(repo) => repo, Ok(repo) => repo,
Err(_) => { Err(_) => {
eprintln!("refractr: warning: found existing repo at {}, attempting to use", repo_dir); eprintln!("refractr: warning: found existing repo at {}, attempting to use", repo_dir);
match fast_forward(&refractr, &repo_dir, &cfg.config.branches) { match self.fast_forward(&repo_dir, &cfg.config.branches) {
Ok(_) => if let Ok(repo) = Repository::open(Path::new(&repo_dir)) { Ok(_) => if let Ok(repo) = Repository::open(Path::new(&repo_dir)) {
repo repo
} else { } else {
@ -227,8 +234,8 @@ pub fn run(refractr: Refractr, cfgs: Vec<ConfigFile>) -> std::io::Result<()> {
let repo_fresh = Repository::open(Path::new(&repo_dir)).unwrap(); let repo_fresh = Repository::open(Path::new(&repo_dir)).unwrap();
let remotes = make_remotes(&refractr, &repo_fresh, &cfg); let remotes = self.make_remotes(&repo_fresh, &cfg);
push_remotes(&refractr, &cfg.config, &repo, &remotes); self.push_remotes(&cfg.config, &repo, &remotes);
if cfg.config.schedule.enabled { if cfg.config.schedule.enabled {
loop_repos.push(OpenedRepository { loop_repos.push(OpenedRepository {
repo, repo,
@ -240,12 +247,14 @@ pub fn run(refractr: Refractr, cfgs: Vec<ConfigFile>) -> std::io::Result<()> {
} }
if loop_repos.len() >= 1 { if loop_repos.len() >= 1 {
common::verbose(refractr.verbose, 2, format!("{} configs have schedules enabled, setting up looper", loop_repos.len())); common::verbose(self.verbose, 2, format!("{} configs have schedules enabled, setting up looper", loop_repos.len()));
looper(refractr, loop_repos); self.looper(loop_repos);
} else { } else {
common::verbose(refractr.verbose, 2, format!("No scheduled configs found, exiting refractr")); common::verbose(self.verbose, 2, format!("No scheduled configs found, exiting refractr"));
} }
Ok(()) Ok(())
} }
}