include!("check_features.rs");
pub mod args;
mod blueprint;
pub mod error;
pub mod reference;
use std::{
collections::HashMap,
path::{
Path,
PathBuf,
},
};
use anyhow::Result;
use args::ManualFormat;
use blueprint::Blueprint;
use git2::FetchOptions;
#[tokio::main]
async fn main() -> Result<()> {
let cmd = crate::args::ClapArgumentLoader::load()?;
match cmd.command {
| crate::args::Command::Manual { path, format } => {
let out_path = PathBuf::from(path);
std::fs::create_dir_all(&out_path)?;
match format {
| ManualFormat::Manpages => {
reference::build_manpages(&out_path)?;
},
| ManualFormat::Markdown => {
reference::build_markdown(&out_path)?;
},
}
Ok(())
},
| crate::args::Command::Autocomplete { path, shell } => {
let out_path = PathBuf::from(path);
std::fs::create_dir_all(&out_path)?;
reference::build_shell_completion(&out_path, &shell)?;
Ok(())
},
| crate::args::Command::Generate(c) => {
match c {
| crate::args::GenerateCommand::Git {
out,
repo,
branch,
folder,
vars,
interactive,
force,
} => {
let out_path_root = Path::new(&out);
let temp_dir = Path::join(&std::env::temp_dir(), uuid::Uuid::new_v4().to_string());
let root_dir = Path::join(&temp_dir, &folder);
let mut fo = FetchOptions::new();
fo.depth(1);
git2::build::RepoBuilder::new().branch(&branch).fetch_options(fo).clone(&repo, &temp_dir)?;
if force {
let _ = std::fs::remove_dir_all(&out);
}
if std::fs::create_dir_all(&out).is_err() {
return Err(anyhow::anyhow!(
"failed to create output directory - might already exist"
));
}
let blueprint = serde_yaml::from_str::<Blueprint>(
&std::fs::read_to_string(Path::join(&temp_dir, &folder).join(".ranger.yaml")).unwrap(),
)
.unwrap();
let render_result = render(&blueprint, &vars, &root_dir, out_path_root, interactive).await;
std::fs::remove_dir_all(temp_dir)?;
match render_result {
| Ok(_) => Ok(()),
| Err(e) => {
std::fs::remove_dir_all(out_path_root)?;
Err(e)
},
}
},
| crate::args::GenerateCommand::Local {
out,
folder,
vars,
interactive,
force,
} => {
let out_path_root = Path::new(&out);
let folder = Path::new(&folder);
let mut fo = FetchOptions::new();
fo.depth(1);
if force {
let _ = std::fs::remove_dir_all(&out);
}
if std::fs::create_dir_all(&out).is_err() {
return Err(anyhow::anyhow!(
"failed to create output directory - might already exist"
));
}
let blueprint = serde_yaml::from_str::<Blueprint>(
&std::fs::read_to_string(Path::join(folder, ".ranger.yaml")).unwrap(),
)
.unwrap();
match render(&blueprint, &vars, &folder, out_path_root, interactive).await {
| Ok(_) => Ok(()),
| Err(e) => {
std::fs::remove_dir_all(out_path_root)?;
Err(e)
},
}
},
}
},
}
}
async fn render(
bp: &Blueprint,
value_overrides: &HashMap<String, String>,
root_dir: &Path,
out_path_root: &Path,
interactive: bool,
) -> Result<(), anyhow::Error> {
let backend = if interactive {
&complate::render::Backend::CLI
} else {
&complate::render::Backend::Headless
};
let values = if let Some(variables) = &bp.template.variables {
complate::render::populate_variables(
variables,
value_overrides,
&complate::render::ShellTrust::Ultimate,
backend,
Some("vars".to_owned()),
)
.await?
} else {
HashMap::<_, _>::new()
};
let hb = complate::render::make_handlebars(
&values,
&bp.template.helpers,
&complate::render::ShellTrust::Ultimate,
true,
)
.await?;
for w in walkdir::WalkDir::new(root_dir) {
let entry = w?;
let path = entry.path();
let rel_path =
hb.0.render_template(&path.strip_prefix(&root_dir)?.to_str().unwrap(), &hb.1)
.map_err(|e| anyhow::anyhow!(e))?;
if rel_path == ".ranger.yaml" {
continue;
}
let out_path = Path::join(out_path_root, rel_path);
if path.is_dir() {
std::fs::create_dir_all(out_path)?;
} else {
let content = std::fs::read_to_string(path)?;
let rendered = hb.0.render_template(&content, &hb.1).map_err(|e| anyhow::anyhow!(e))?;
std::fs::write(out_path, rendered)?;
}
}
Ok(())
}