/* * Copyright 2025 Bryson Steck * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ mod common; mod config; mod refractr; use crate::refractr::Refractr; use clap::Parser; use std::path::PathBuf; use std::process; #[cfg(target_family = "windows")] use username; #[cfg(target_family = "unix")] use users; #[derive(Parser)] #[command(name = "refractr")] #[command(version = option_env!("CARGO_PKG_VERSION"))] #[command(about = "An automated pull/push utility for mirroring Git repositories")] #[command(long_about = None)] struct Args { #[arg(short, long, help = "Specify a config file", default_value = get_config_default())] config: Vec, #[arg(short, long, help = "Specify the level of verbosity", action = clap::ArgAction::Count)] verbose: u8, #[arg( short = 'e', long, help = "Output a full, commented config file and exit" )] create: bool, #[arg( short = 's', long, help = "Exit on push and unknown host errors instead of ignoring them" )] strict: bool, } fn get_config_default() -> &'static str { #[cfg(target_os = "windows")] return "C:\\ProgramData\\refractr\\config.toml"; #[cfg(target_os = "linux")] return "/etc/refractr/config.toml"; #[cfg(any( target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "macos" ))] return "/usr/local/etc/refractr/config.toml"; } #[quit::main] fn main() -> Result<(), String> { let args = Args::parse(); let refractr = Refractr { docker: match option_env!("REFRACTR_DOCKER") { Some(v) => match v { "true" => true, _ => false, }, None => false, }, pid: process::id(), strict: args.strict, unix: cfg!(unix), verbose: args.verbose, }; // warn to avoid root/admin // unix-like #[cfg(target_family = "unix")] if users::get_current_uid() == 0 { common::warning(format!("this program should not ran as root")); } // windows #[cfg(target_family = "windows")] match username::get_user_name() { Ok(user) => { if user.contains("Administrator") || user.contains("SYSTEM") { common::warning(format!("this program should not ran as {}", user)); } }, Err(_) => common::warning(format!("failed to get process username")), } common::verbose( refractr.verbose, 1, format!( "refractr started with level {} verbosity enabled", refractr.verbose.to_string() ), ); common::verbose(refractr.verbose, 3, format!("Process ID: {}", refractr.pid)); common::verbose( refractr.verbose, 3, format!("Running in Docker: {}", refractr.docker), ); common::verbose( refractr.verbose, 3, format!("System is UNIX(-like): {}", refractr.unix), ); common::verbose(refractr.verbose, 2, format!("Checking for create flag")); if args.create { common::verbose(refractr.verbose, 3, format!("Printing sample config")); let example = include_str!("example/config.toml"); println!("{}", example); return Ok(()); } let mut cfgs = vec![]; match config::read_config(args.config, &refractr) { Ok(c) => cfgs = c, Err(e) => common::error(format!("{}", e), common::ExitCode::ConfigError), }; if refractr.verbose >= 2 { // no need to loop over configs if verbose is not at the correct level for i in &cfgs { common::verbose(refractr.verbose, 2, format!("{}", i)); } } common::verbose( refractr.verbose, 1, format!("Config file(s) read successfully"), ); match refractr.run(cfgs) { Ok(_) => (), Err(e) => common::error(format!("{}", e.msg), e.code), }; Ok(()) }