use crate::generate::{any_msg, ProjectKind};
use anyhow::{anyhow, Context, Result};
use serde::Deserialize;
use std::{fs, path::PathBuf};
#[derive(Debug, Deserialize)]
pub(crate) struct TemplateSource {
pub(crate) name: String,
pub(crate) description: String,
pub(crate) path: Option<String>,
pub(crate) git: Option<String>,
pub(crate) branch: Option<String>,
pub(crate) subfolder: Option<String>,
}
#[derive(Debug, Deserialize)]
pub(crate) struct Favorites {
#[serde(flatten)]
pub(crate) templates: std::collections::HashMap<String, Vec<TemplateSource>>,
}
const DEFAULT_FAVORITES: &str = include_str!("./favorites.toml");
pub(crate) fn load_favorites(path: Option<&PathBuf>) -> Result<Favorites> {
let data = if let Some(path) = path {
fs::read_to_string(path)
.with_context(|| format!("reading favorites file {}", &path.display()))?
} else {
DEFAULT_FAVORITES.to_string()
};
let fv = toml::from_str::<Favorites>(&data).with_context(|| "parsing favorites".to_string())?;
Ok(fv)
}
pub(crate) fn pick_favorite(
fav_file: Option<&PathBuf>,
kind: &ProjectKind,
silent: bool,
fav_name: Option<&String>,
) -> Result<TemplateSource> {
let mut favorites = load_favorites(fav_file)?;
let fav = match favorites.templates.remove(&kind.to_string()) {
Some(mut type_favs) if !type_favs.is_empty() => {
if let Some(name) = &fav_name {
type_favs
.into_iter()
.find(|f| &f.name == *name)
.ok_or_else(|| {
any_msg(
&format!(
"no {} template with the name '{}'.",
&kind.to_string(),
name
),
"",
)
})?
} else {
let index = if silent || type_favs.len() == 1 {
0
} else {
prompt_for_template(&type_favs, "Select a project template:")?
};
type_favs.remove(index)
}
}
_ => {
return Err(any_msg(
"templates missing for project type",
&kind.to_string(),
))
}
};
Ok(fav)
}
fn prompt_for_template(options: &[TemplateSource], prompt: &str) -> Result<usize> {
let choices = options
.iter()
.map(|s| format!("{}: {}", &s.name, &s.description))
.collect::<Vec<String>>();
let entry = crate::generate::project_variables::StringEntry {
default: None,
choices: Some(choices),
regex: None,
};
crate::generate::interactive::prompt_for_choice(&entry, prompt)
.map_err(|e| anyhow!("console IO error: {}", e))
}