use crate::git;
use anyhow::Result;
use camino::Utf8PathBuf;
use log::info;
use serde::{Deserialize, Serialize};
use std::{
fs::{self, File},
io::Read,
path::{Path, PathBuf},
string::String,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoConfig {
pub url: String,
#[serde(default = "RepoConfig::default_branch")]
pub branch: String,
#[serde(default = "RepoConfig::default_file")]
pub file: String,
}
impl RepoConfig {
fn new(url: String) -> Self {
Self {
url,
branch: RepoConfig::default_branch(),
file: RepoConfig::default_file(),
}
}
fn default_branch() -> String {
"master".to_owned()
}
fn default_file() -> String {
".emplace".to_owned()
}
pub fn path(&self) -> PathBuf {
PathBuf::from(self.file.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
#[serde(default = "Config::default_mirror_dir_string")]
pub repo_directory: String,
pub repo: RepoConfig,
}
impl Config {
pub fn new<P>(path: P) -> Result<Self>
where
P: AsRef<Path>,
{
info!("No configuration file found.");
let repo_url = dialoguer::Input::<String>::new()
.with_prompt("The URL of the git repository you (want to) store the mirrors in")
.interact()?;
let config = Config {
repo_directory: Config::default_mirror_dir_string(),
repo: RepoConfig::new(repo_url),
};
config.save(path)?;
Ok(config)
}
pub fn clone_repo_ask(&mut self) -> Result<bool> {
let term = console::Term::stdout();
let theme: Box<dyn dialoguer::theme::Theme> = if term.features().colors_supported() {
Box::<dialoguer::theme::ColorfulTheme>::default()
} else {
Box::new(dialoguer::theme::SimpleTheme)
};
let prompt = String::from("Choose what to do with the repository");
let choices = &[
"Change repository path before cloning it",
"Clone the repo or create the repository locally",
"Do nothing for now",
];
let chosen = dialoguer::MultiSelect::with_theme(&*theme)
.with_prompt(prompt)
.items(choices)
.interact()?;
if chosen.contains(&2) {
return Ok(false);
}
if chosen.contains(&0) {
let repo_path = dialoguer::Input::<String>::with_theme(&*theme)
.with_prompt("Where do you want your repository to be located")
.interact()?;
self.repo_directory = repo_path;
self.save_to_default_path()?;
}
if chosen.contains(&1) {
let prompt = "What do you want to do?";
let choices_in = &["Clone the repo", "Create it locally"];
let chosen_in = dialoguer::Select::with_theme(&*theme)
.with_prompt(prompt)
.items(choices_in)
.clear(true)
.interact_on(&term)?;
if chosen_in == 0 {
return git::clone_full(&self.repo_directory, &self.repo.url);
} else {
fs::DirBuilder::new()
.recursive(true)
.create(&self.repo_directory)?;
git::init_repo(&self.repo_directory)?;
return git::set_remote(&self.repo_directory, &self.repo.url);
}
}
Ok(true)
}
pub fn from_default_file() -> Result<Option<Self>> {
Config::from_path(&Config::default_path())
}
pub fn from_path_or_new<P: AsRef<Path>>(file_path: P) -> Result<Self> {
match Config::from_default_file()? {
Some(config) => Ok(config),
None => Config::new(file_path),
}
}
pub fn from_path<P: AsRef<Path>>(file_path: &P) -> Result<Option<Self>> {
if !file_path.as_ref().exists() {
return Ok(None);
}
let mut file = File::open(file_path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(Some(toml::from_str(&contents)?))
}
pub fn save_to_default_path(&self) -> Result<()> {
let result = self.save(Config::default_path());
info!("You can edit the git repository URL and other settings here later.");
result
}
pub fn save<P>(&self, path: P) -> Result<()>
where
P: AsRef<Path>,
{
let value = toml::Value::try_from(self)?;
fs::write(
path.as_ref(),
toml::to_string(&value)?,
)?;
info!("Config saved to: \"{}\".", path.as_ref().to_str().unwrap());
Ok(())
}
pub fn full_file_path(&self) -> PathBuf {
let mut base = PathBuf::from(&self.repo_directory);
base.push(self.repo.path());
base
}
pub fn default_path() -> Utf8PathBuf {
Utf8PathBuf::from_path_buf(
dirs::config_dir()
.expect("Could not find config dir")
.join("emplace.toml"),
)
.expect("Path is not valid UTF-8")
}
fn default_mirror_dir() -> Utf8PathBuf {
Utf8PathBuf::from_path_buf(
dirs::data_local_dir()
.expect("Could not find local data dir")
.join("emplace"),
)
.expect("Path is not valid UTF-8")
}
fn default_mirror_dir_string() -> String {
Config::default_mirror_dir().to_string()
}
}