Add a logging solution, and remove ANSI crate.
parent
2c95c43124
commit
61b9a338a5
|
@ -10,13 +10,14 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ansi_term = "0.12.1"
|
|
||||||
clap = { version = "3.0.0-rc.9", features = ["derive"] }
|
clap = { version = "3.0.0-rc.9", features = ["derive"] }
|
||||||
git2 = "0.13.25"
|
git2 = "0.13.25"
|
||||||
|
log = "0.4.14"
|
||||||
notify = "4.0.16"
|
notify = "4.0.16"
|
||||||
regex = "1.5.4"
|
regex = "1.5.4"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0.132"
|
serde_derive = "1.0.132"
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
yaml-rust = "0.4.4"
|
simplelog = { version = "^0.11.1", features = ["paris"] }
|
||||||
url = { version = "2.2.2", features = ["serde"] }
|
url = { version = "2.2.2", features = ["serde"] }
|
||||||
|
yaml-rust = "0.4.4"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{path, path::ResPathBuf};
|
use super::{path, path::ResPathBuf};
|
||||||
use ansi_term::Colour::{Green, Yellow};
|
use paris::formatter::colorize_string;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use simplelog::{info, paris};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
env::VarError,
|
env::VarError,
|
||||||
|
@ -143,8 +144,10 @@ pub fn load(candidates: &Vec<ResPathBuf>) -> Result<PathConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(config: &PathConfig) -> Result<PathConfig> {
|
pub fn reload(config: &PathConfig) -> Result<PathConfig> {
|
||||||
// TODO(jrpotter): Let's add a proper logging solution.
|
info!(
|
||||||
println!("Configuration reloaded.");
|
"<green>{}</> configuration reloaded.",
|
||||||
|
config.1.local.display()
|
||||||
|
);
|
||||||
load(&vec![config.0.clone()])
|
load(&vec![config.0.clone()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +159,7 @@ fn prompt_local(path: Option<&Path>) -> Result<PathBuf> {
|
||||||
let default = path.map_or("$HOME/.homesync".to_owned(), |p| p.display().to_string());
|
let default = path.map_or("$HOME/.homesync".to_owned(), |p| p.display().to_string());
|
||||||
print!(
|
print!(
|
||||||
"Local git repository <{}> (enter to continue): ",
|
"Local git repository <{}> (enter to continue): ",
|
||||||
Yellow.paint(&default)
|
colorize_string(format!("<yellow>{}</>", &default)),
|
||||||
);
|
);
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
let mut local = String::new();
|
let mut local = String::new();
|
||||||
|
@ -176,7 +179,7 @@ fn prompt_remote(url: Option<&Url>) -> Result<Url> {
|
||||||
});
|
});
|
||||||
print!(
|
print!(
|
||||||
"Remote git repository <{}> (enter to continue): ",
|
"Remote git repository <{}> (enter to continue): ",
|
||||||
Yellow.paint(&default)
|
colorize_string(format!("<yellow>{}</>", &default)),
|
||||||
);
|
);
|
||||||
io::stdout().flush()?;
|
io::stdout().flush()?;
|
||||||
let mut remote = String::new();
|
let mut remote = String::new();
|
||||||
|
@ -192,7 +195,7 @@ fn prompt_remote(url: Option<&Url>) -> Result<Url> {
|
||||||
pub fn write(path: &ResPathBuf, loaded: Option<Config>) -> Result<PathConfig> {
|
pub fn write(path: &ResPathBuf, loaded: Option<Config>) -> Result<PathConfig> {
|
||||||
println!(
|
println!(
|
||||||
"Generating config at {}...\n",
|
"Generating config at {}...\n",
|
||||||
Green.paint(path.unresolved().display().to_string())
|
colorize_string(format!("<green>{}</>", path.unresolved().display())),
|
||||||
);
|
);
|
||||||
let local = prompt_local(match &loaded {
|
let local = prompt_local(match &loaded {
|
||||||
Some(c) => Some(c.local.as_ref()),
|
Some(c) => Some(c.local.as_ref()),
|
||||||
|
@ -221,7 +224,7 @@ pub fn write(path: &ResPathBuf, loaded: Option<Config>) -> Result<PathConfig> {
|
||||||
pub fn list_packages(config: PathConfig) {
|
pub fn list_packages(config: PathConfig) {
|
||||||
println!(
|
println!(
|
||||||
"Listing packages in {}...\n",
|
"Listing packages in {}...\n",
|
||||||
Green.paint(config.0.unresolved().display().to_string())
|
colorize_string(format!("<green>{}</>", config.0.unresolved().display())),
|
||||||
);
|
);
|
||||||
// Alphabetical ordered ensured by B-tree implementation.
|
// Alphabetical ordered ensured by B-tree implementation.
|
||||||
for (k, _) in config.1.packages {
|
for (k, _) in config.1.packages {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::{config, config::PathConfig, path, path::ResPathBuf};
|
use super::{config, config::PathConfig, path, path::ResPathBuf};
|
||||||
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
|
||||||
|
use simplelog::{error, paris, trace, warn};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -9,7 +10,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(jrpotter): Add logging.
|
|
||||||
// TODO(jrpotter): Add pid file to only allow one daemon at a time.
|
// TODO(jrpotter): Add pid file to only allow one daemon at a time.
|
||||||
// TODO(jrpotter): Sync files to local git repository.
|
// TODO(jrpotter): Sync files to local git repository.
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ fn resolve_pending(tx: &Sender<DebouncedEvent>, pending: &HashSet<PathBuf>) -> V
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
to_remove.push(path.clone());
|
to_remove.push(path.clone());
|
||||||
eprintln!(
|
error!(
|
||||||
"Encountered unexpected error {} when processing path {}",
|
"Encountered unexpected error {} when processing path {}",
|
||||||
e,
|
e,
|
||||||
path.display()
|
path.display()
|
||||||
|
@ -96,7 +96,7 @@ impl<'a> WatchState<'a> {
|
||||||
self.watching.insert(path);
|
self.watching.insert(path);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
error!(
|
||||||
"Encountered unexpected error {} when watching path {}",
|
"Encountered unexpected error {} when watching path {}",
|
||||||
e,
|
e,
|
||||||
path.unresolved().display()
|
path.unresolved().display()
|
||||||
|
@ -113,7 +113,7 @@ impl<'a> WatchState<'a> {
|
||||||
match self.watcher.unwatch(&path) {
|
match self.watcher.unwatch(&path) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
error!(
|
||||||
"Encountered unexpected error {} when unwatching path {}",
|
"Encountered unexpected error {} when unwatching path {}",
|
||||||
e,
|
e,
|
||||||
path.unresolved().display()
|
path.unresolved().display()
|
||||||
|
@ -159,47 +159,51 @@ pub fn launch(mut config: PathConfig, freq_secs: u64) -> Result<(), Box<dyn Erro
|
||||||
// Received paths should always be the fully resolved ones so safe to
|
// Received paths should always be the fully resolved ones so safe to
|
||||||
// compare against our current config path.
|
// compare against our current config path.
|
||||||
match watch_rx.recv() {
|
match watch_rx.recv() {
|
||||||
Ok(DebouncedEvent::NoticeWrite(_)) => {
|
Ok(DebouncedEvent::NoticeWrite(p)) => {
|
||||||
// Intentionally ignore in favor of stronger signals.
|
trace!("NoticeWrite {}", p.display());
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::NoticeRemove(_)) => {
|
Ok(DebouncedEvent::NoticeRemove(p)) => {
|
||||||
// Intentionally ignore in favor of stronger signals.
|
trace!("NoticeRemove {}", p.display());
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Create(p)) => {
|
Ok(DebouncedEvent::Create(p)) => {
|
||||||
if config.0 == p {
|
if config.0 == p {
|
||||||
config = config::reload(&config)?;
|
config = config::reload(&config)?;
|
||||||
state.update(&config);
|
state.update(&config);
|
||||||
}
|
}
|
||||||
println!("Create {}", p.display());
|
trace!("Create {}", p.display());
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Write(p)) => {
|
Ok(DebouncedEvent::Write(p)) => {
|
||||||
if config.0 == p {
|
if config.0 == p {
|
||||||
config = config::reload(&config)?;
|
config = config::reload(&config)?;
|
||||||
state.update(&config);
|
state.update(&config);
|
||||||
}
|
}
|
||||||
println!("Write {}", p.display());
|
trace!("Write {}", p.display());
|
||||||
}
|
}
|
||||||
// Do not try reloading our primary config in any of the following
|
// Do not try reloading our primary config in any of the following
|
||||||
// cases since it may lead to undesired behavior. If our config has
|
// cases since it may lead to undesired behavior. If our config has
|
||||||
// e.g. been removed, let's just keep using what we have in memory
|
// e.g. been removed, let's just keep using what we have in memory
|
||||||
// in the chance it may be added back.
|
// in the chance it may be added back.
|
||||||
Ok(DebouncedEvent::Chmod(p)) => {
|
Ok(DebouncedEvent::Chmod(p)) => {
|
||||||
println!("Chmod {}", p.display());
|
trace!("Chmod {}", p.display());
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Remove(p)) => {
|
Ok(DebouncedEvent::Remove(p)) => {
|
||||||
println!("Remove {}", p.display());
|
trace!("Remove {}", p.display());
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Rename(src, dst)) => {
|
Ok(DebouncedEvent::Rename(src, dst)) => {
|
||||||
println!("Rename {} {}", src.display(), dst.display())
|
trace!("Rename {} {}", src.display(), dst.display())
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Rescan) => {
|
Ok(DebouncedEvent::Rescan) => {
|
||||||
println!("Rescanning");
|
trace!("Rescanning");
|
||||||
}
|
}
|
||||||
Ok(DebouncedEvent::Error(e, _maybe_path)) => {
|
Ok(DebouncedEvent::Error(e, path)) => {
|
||||||
println!("Error {}", e);
|
warn!(
|
||||||
|
"Error {} at {}",
|
||||||
|
e,
|
||||||
|
path.unwrap_or_else(|| PathBuf::from("N/A")).display()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("watch error: {:?}", e);
|
error!("Watch error: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::{config::PathConfig, path};
|
use super::{config::PathConfig, path};
|
||||||
use git2::Repository;
|
use git2::Repository;
|
||||||
|
use simplelog::{info, paris};
|
||||||
use std::{env::VarError, error, fmt, fs, io, path::PathBuf, result};
|
use std::{env::VarError, error, fmt, fs, io, path::PathBuf, result};
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
|
@ -143,7 +144,10 @@ pub fn init(config: &PathConfig) -> Result<git2::Repository> {
|
||||||
// issues that we need to resolve anyways (e.g. setting remote, pulling,
|
// issues that we need to resolve anyways (e.g. setting remote, pulling,
|
||||||
// managing possible merge conflicts, etc.).
|
// managing possible merge conflicts, etc.).
|
||||||
None => {
|
None => {
|
||||||
println!("Creating new homesync repository.");
|
info!(
|
||||||
|
"Creating new homesync repository at <green>{}</>.",
|
||||||
|
config.1.local.display()
|
||||||
|
);
|
||||||
let repo = Repository::init(&expanded)?;
|
let repo = Repository::init(&expanded)?;
|
||||||
fs::File::create(sentinel)?;
|
fs::File::create(sentinel)?;
|
||||||
Ok(repo)
|
Ok(repo)
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub fn run_init(candidates: Vec<ResPathBuf>) -> Result<(), Box<dyn Error>> {
|
||||||
// git library we chose to use employs async/await so let's wrap around a
|
// git library we chose to use employs async/await so let's wrap around a
|
||||||
// channel.
|
// channel.
|
||||||
git::init(&config)?;
|
git::init(&config)?;
|
||||||
println!("Finished initialization.");
|
println!("\nFinished initialization.");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -1,8 +1,20 @@
|
||||||
use clap::{App, AppSettings, Arg};
|
use clap::{App, AppSettings, Arg};
|
||||||
use homesync::path::ResPathBuf;
|
use homesync::path::ResPathBuf;
|
||||||
|
use simplelog;
|
||||||
|
use simplelog::{error, paris};
|
||||||
use std::{error::Error, io, path::PathBuf};
|
use std::{error::Error, io, path::PathBuf};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Only one logger should ever be initialized and it should be done at the
|
||||||
|
// beginning of the program. Otherwise logs are ignored.
|
||||||
|
simplelog::TermLogger::init(
|
||||||
|
simplelog::LevelFilter::Info,
|
||||||
|
simplelog::Config::default(),
|
||||||
|
simplelog::TerminalMode::Mixed,
|
||||||
|
simplelog::ColorChoice::Auto,
|
||||||
|
)
|
||||||
|
.expect("Could not initialize logger library.");
|
||||||
|
|
||||||
let matches = App::new("homesync")
|
let matches = App::new("homesync")
|
||||||
.about("Cross desktop configuration sync tool.")
|
.about("Cross desktop configuration sync tool.")
|
||||||
.version("0.1.0")
|
.version("0.1.0")
|
||||||
|
@ -43,7 +55,7 @@ fn main() {
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
if let Err(e) = dispatch(matches) {
|
if let Err(e) = dispatch(matches) {
|
||||||
eprintln!("{}", e);
|
error!("{}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +80,7 @@ fn dispatch(matches: clap::ArgMatches) -> Result<(), Box<dyn Error>> {
|
||||||
if freq_secs > 0 {
|
if freq_secs > 0 {
|
||||||
homesync::run_daemon(config, freq_secs)?;
|
homesync::run_daemon(config, freq_secs)?;
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Invalid frequency. Expected a positive integer.");
|
error!("Invalid frequency. Expected a positive integer.");
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue