From be993fcaf97d5b1e4832a6eac3e871d1b272c313 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Wed, 7 Apr 2021 00:12:18 +0200 Subject: [PATCH] Stuff! --- manager/Cargo.lock | 164 +++++++++++++++++++++++++++++++++-------- manager/Cargo.toml | 5 +- manager/src/builder.rs | 77 +++++++++++-------- manager/src/linker.rs | 22 +++--- manager/src/main.rs | 60 ++++++++++++--- 5 files changed, 244 insertions(+), 84 deletions(-) diff --git a/manager/Cargo.lock b/manager/Cargo.lock index ef00b7b..b9d5eb5 100644 --- a/manager/Cargo.lock +++ b/manager/Cargo.lock @@ -48,6 +48,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.2.1" @@ -115,17 +121,21 @@ dependencies = [ ] [[package]] -name = "df" -version = "0.1.0" +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "derive_more" +version = "0.99.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b1b72f1263f214c0f823371768776c4f5841b942c9883aa8e5ec584fd0ba6" dependencies = [ - "async-recursion", - "futures", - "handlebars", - "log", - "pretty_env_logger", - "serde", - "structopt", - "tokio", + "convert_case", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -137,6 +147,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "dotfiles" +version = "0.1.0" +dependencies = [ + "async-recursion", + "futures", + "log", + "pretty_env_logger", + "serde", + "structopt", + "templar", + "tokio", + "xdg", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "env_logger" version = "0.7.1" @@ -257,20 +288,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "handlebars" -version = "3.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580b6f551b29a3a02436318aed09ba1c58eea177dc49e39beac627ad356730a5" -dependencies = [ - "log", - "pest", - "pest_derive", - "quick-error 2.0.0", - "serde", - "serde_json", -] - [[package]] name = "heck" version = "0.3.2" @@ -295,7 +312,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3", + "quick-error", ] [[package]] @@ -325,6 +342,12 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714" +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + [[package]] name = "lock_api" version = "0.4.3" @@ -386,6 +409,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -408,6 +440,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "ordered-float" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "766f840da25490628d8e63e529cd21c014f6600c6b8517add12a6fa6167a6218" +dependencies = [ + "num-traits", +] + [[package]] name = "parking_lot" version = "0.11.1" @@ -549,12 +590,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" - [[package]] name = "quote" version = "1.0.9" @@ -633,6 +668,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + [[package]] name = "sha-1" version = "0.8.2" @@ -707,6 +754,36 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "templar" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4a3b2a09395dd7f898d4334565af031f82c1cab88a29ab35ebc2b53843f994" +dependencies = [ + "base64", + "derive_more", + "lazy_static", + "parking_lot", + "pest", + "pest_derive", + "serde", + "serde_json", + "serde_yaml", + "templar_macros", + "unstructured", +] + +[[package]] +name = "templar_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e33911b223167a20560729d8e0ba5f82b2ed81516d28f1864e6c062bdcee44c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -786,6 +863,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "unstructured" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae5a28ab9d8cd21bb5300077b36a70809ff6a4ce3fc3915767c59fbdf35b7bc" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "vec_map" version = "0.8.2" @@ -828,3 +915,18 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xdg" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/manager/Cargo.toml b/manager/Cargo.toml index fc2017a..3c6164e 100644 --- a/manager/Cargo.toml +++ b/manager/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "df" +name = "dotfiles" version = "0.1.0" authors = ["Joakim Hulthe "] edition = "2018" @@ -10,7 +10,8 @@ pretty_env_logger = "0.4.0" log = "0.4.14" futures = "0.3.13" async-recursion = "0.3.2" -handlebars = "3.5.4" +xdg = "2.2.0" +templar = "0.5.0" [dependencies.serde] version = "1.0.125" diff --git a/manager/src/builder.rs b/manager/src/builder.rs index a0d7175..21bbcd9 100644 --- a/manager/src/builder.rs +++ b/manager/src/builder.rs @@ -1,11 +1,11 @@ -use crate::{ColorMode, Opt}; +use crate::{ColorMode, Config}; use async_recursion::async_recursion; use futures::future::join_all; -use handlebars::{Context, Handlebars}; use serde::Serialize; use std::ffi::OsStr; use std::io::{self, ErrorKind}; use std::path::PathBuf; +use templar::{Context, InnerData, StandardContext, Templar}; use tokio::fs::{copy, create_dir, read_dir, read_to_string, write}; use tokio::try_join; @@ -16,27 +16,36 @@ struct TemplateContext<'a> { darkmode: bool, } -pub async fn build_tree(opt: &Opt) -> io::Result<()> { +const TEMPLATE_EXTENSION: &str = "tpl"; + +pub async fn build_tree(cfg: &Config) -> io::Result<()> { + let tpl = Templar::global(); + let hostname = read_to_string("/etc/hostname").await?; + let darkmode = cfg.color == ColorMode::Dark; + let ctx = StandardContext::new(); + ctx.set( + InnerData::new(TemplateContext { + hostname: hostname.trim(), + darkmode, + lightmode: !darkmode, + }) + .expect("serialize template context"), + ) + .expect("set template context"); - let darkmode = opt.color.map(|m| m == ColorMode::Dark).unwrap_or(true); - - let hbs = Handlebars::new(); - - let ctx = Context::wraps(TemplateContext { - hostname: hostname.trim(), - darkmode, - lightmode: !darkmode, - }) - .expect("template context"); - - dir(opt, &ctx, &hbs, PathBuf::new()).await + dir(cfg, &ctx, &tpl, PathBuf::new()).await } #[async_recursion] -async fn dir(opt: &Opt, ctx: &Context, hbs: &Handlebars<'_>, relative: PathBuf) -> io::Result<()> { - let template_path = opt.template_dir.join(&relative); - let build_path = opt.build_dir.join(&relative); +async fn dir( + cfg: &Config, + ctx: &StandardContext, + tpl: &Templar, + relative: PathBuf, +) -> io::Result<()> { + let template_path = cfg.template_dir.join(&relative); + let build_path = cfg.build_dir.join(&relative); info!("traversing {:?}", template_path); @@ -56,9 +65,9 @@ async fn dir(opt: &Opt, ctx: &Context, hbs: &Handlebars<'_>, relative: PathBuf) let new_relative = relative.join(entry.file_name()); if meta.is_dir() { - dir_tasks.push(dir(opt, ctx, hbs, new_relative)); + dir_tasks.push(dir(cfg, ctx, tpl, new_relative)); } else if meta.is_file() { - file_tasks.push(file(opt, ctx, hbs, new_relative)); + file_tasks.push(file(cfg, ctx, tpl, new_relative)); } } @@ -81,22 +90,28 @@ async fn dir(opt: &Opt, ctx: &Context, hbs: &Handlebars<'_>, relative: PathBuf) Ok(()) } -async fn file(opt: &Opt, ctx: &Context, hbs: &Handlebars<'_>, relative: PathBuf) -> io::Result<()> { - let template_path = opt.template_dir.join(&relative); - let mut new_path = opt.build_dir.join(&relative); +async fn file( + cfg: &Config, + ctx: &StandardContext, + tpl: &Templar, + relative: PathBuf, +) -> io::Result<()> { + let template_path = cfg.template_dir.join(&relative); + let mut new_path = cfg.build_dir.join(&relative); - // if it is a handlebars file - if template_path.extension() == Some(OsStr::new("hbs")) { - info!("rendering {:?}", template_path); - let file_data = read_to_string(&template_path).await?; + info!("rendering {:?}", template_path); + let file_data = read_to_string(&template_path).await?; + if template_path.extension() == Some(OsStr::new(TEMPLATE_EXTENSION)) { // perform templating // TODO: error handling - let rendered = hbs - .render_template_with_context(&file_data, &ctx) - .expect("template error"); + let rendered = tpl + .parse_template(&file_data) + .expect("failed to parse template") + .render(ctx) + .expect("failed to render template"); - // remove .hbs + // remove template file extension new_path.set_extension(""); // write the rendered file diff --git a/manager/src/linker.rs b/manager/src/linker.rs index 640bd72..e9b3762 100644 --- a/manager/src/linker.rs +++ b/manager/src/linker.rs @@ -1,4 +1,4 @@ -use crate::Opt; +use crate::Config; use async_recursion::async_recursion; use futures::future::join_all; use std::io::{self, ErrorKind}; @@ -6,14 +6,14 @@ use std::path::PathBuf; use tokio::fs::{create_dir, read_dir, remove_file, symlink}; use tokio::try_join; -pub async fn link_tree(opt: &Opt) -> io::Result<()> { - dir(opt, PathBuf::new()).await +pub async fn link_tree(cfg: &Config) -> io::Result<()> { + dir(cfg, PathBuf::new()).await } #[async_recursion] -async fn dir(opt: &Opt, relative: PathBuf) -> io::Result<()> { - let build_path = opt.build_dir.join(&relative); - let link_path = opt.link_dir.join(&relative); +async fn dir(cfg: &Config, relative: PathBuf) -> io::Result<()> { + let build_path = cfg.build_dir.join(&relative); + let link_path = cfg.link_dir.join(&relative); info!("traversing {:?}", build_path); @@ -33,9 +33,9 @@ async fn dir(opt: &Opt, relative: PathBuf) -> io::Result<()> { let new_relative = relative.join(entry.file_name()); if meta.is_dir() { - dir_tasks.push(dir(opt, new_relative)); + dir_tasks.push(dir(cfg, new_relative)); } else if meta.is_file() { - file_tasks.push(file(opt, new_relative)); + file_tasks.push(file(cfg, new_relative)); } } @@ -58,9 +58,9 @@ async fn dir(opt: &Opt, relative: PathBuf) -> io::Result<()> { Ok(()) } -async fn file(opt: &Opt, relative: PathBuf) -> io::Result<()> { - let build_path = opt.build_dir.join(&relative); - let link_path = opt.link_dir.join(&relative); +async fn file(cfg: &Config, relative: PathBuf) -> io::Result<()> { + let build_path = cfg.build_dir.join(&relative); + let link_path = cfg.link_dir.join(&relative); match remove_file(&link_path).await { Ok(_) => { diff --git a/manager/src/main.rs b/manager/src/main.rs index 8b72b89..dd11ddb 100644 --- a/manager/src/main.rs +++ b/manager/src/main.rs @@ -6,21 +6,39 @@ mod linker; use builder::build_tree; use linker::link_tree; +use log::LevelFilter; +use std::env; use std::path::PathBuf; use structopt::StructOpt; use tokio::io; #[derive(StructOpt)] -pub struct Opt { - template_dir: PathBuf, - build_dir: PathBuf, - link_dir: PathBuf, +struct Opt { + #[structopt(short, long)] + template_dir: Option, + + #[structopt(short, long)] + build_dir: Option, + + #[structopt(short, long)] + link_dir: Option, #[structopt(subcommand)] color: Option, + + #[structopt(short, parse(from_occurrences))] + verbosity: u8, } -#[derive(StructOpt, PartialEq, Eq, Clone, Copy)] +#[derive(Debug)] +pub struct Config { + template_dir: PathBuf, + build_dir: PathBuf, + link_dir: PathBuf, + color: ColorMode, +} + +#[derive(StructOpt, PartialEq, Eq, Clone, Copy, Debug)] pub enum ColorMode { Dark, Light, @@ -28,15 +46,39 @@ pub enum ColorMode { #[tokio::main] async fn main() -> io::Result<()> { - pretty_env_logger::init(); - let opt = Opt::from_args(); + let filter_level = match opt.verbosity { + 0 => LevelFilter::Warn, + 1 => LevelFilter::Info, + 2 => LevelFilter::Debug, + _ => LevelFilter::Trace, + }; + + pretty_env_logger::formatted_builder() + .filter_level(filter_level) + .init(); + + let xdg_dirs = xdg::BaseDirectories::with_prefix("dotfiles").unwrap(); + + let cfg = Config { + template_dir: opt + .template_dir + .unwrap_or_else(|| xdg_dirs.create_config_directory("tree").expect("xdg")), + build_dir: opt + .build_dir + .unwrap_or_else(|| xdg_dirs.create_cache_directory("").expect("xdg")), + link_dir: opt + .link_dir + .unwrap_or_else(|| env::var("HOME").expect("$HOME").into()), + color: opt.color.unwrap_or(ColorMode::Dark), + }; + info!("building tree"); - build_tree(&opt).await?; + build_tree(&cfg).await?; info!("linking tree"); - link_tree(&opt).await?; + link_tree(&cfg).await?; Ok(()) }