eldiron-cli 0.1.0

Command-line tool for Eldiron
Documentation
use std::path::PathBuf;

use anyhow::Result;
use dialoguer::{theme::ColorfulTheme, Confirm, Input, Select};

use crate::{
    common::{has_installed, remove_dir_if_exist},
    eldiron::{
        server::{build_server, setup_server_service_systemd, ELDIRON_BIN_NAME_SERVER},
        ELDIRON_GIT_URL, ELDIRON_INSTALL_BIN_DIR,
    },
    git::{git_clone, git_get_remote_latest_tag, parse_clone_dir_from_url},
};

enum SourceSelectItem {
    Default,
    Custom,
}

const SOURCE_SELECT_ITEM_LIST: [SourceSelectItem; 2] =
    [SourceSelectItem::Default, SourceSelectItem::Custom];

impl SourceSelectItem {
    fn labels() -> Vec<&'static str> {
        let len = SOURCE_SELECT_ITEM_LIST.len();

        let mut res = Vec::with_capacity(len);

        for i in 0..len {
            res.push(Self::to_label(&SOURCE_SELECT_ITEM_LIST[i]));
        }

        res
    }

    fn to_label(&self) -> &'static str {
        match self {
            Self::Default => "Default (Latest release)",
            Self::Custom => "Custom",
        }
    }
}

enum SourceCustomSelectItem {
    Git,
    Local,
}

const SOURCE_CUSTOM_SELECT_ITEM_LIST: [SourceCustomSelectItem; 2] =
    [SourceCustomSelectItem::Git, SourceCustomSelectItem::Local];

impl SourceCustomSelectItem {
    fn labels() -> Vec<&'static str> {
        let len = SOURCE_CUSTOM_SELECT_ITEM_LIST.len();

        let mut res = Vec::with_capacity(len);

        for i in 0..len {
            res.push(Self::to_label(&SOURCE_CUSTOM_SELECT_ITEM_LIST[i]));
        }

        res
    }

    fn to_label(&self) -> &'static str {
        match self {
            Self::Git => "Git repository",
            Self::Local => "Local directory",
        }
    }
}

enum SourceType {
    CustomGit { url: String, branch: Option<String> },
    CustomLocal(String),
    Latest,
}

pub fn server_setup() -> Result<()> {
    println!("Setting up Eldiron server.");

    if [ELDIRON_INSTALL_BIN_DIR, ELDIRON_BIN_NAME_SERVER]
        .iter()
        .collect::<PathBuf>()
        .as_path()
        .exists()
    {
        if !Confirm::new()
            .with_prompt("Eldiron server already installed on this machine. Re-install?")
            .interact()?
        {
            println!("Abort.");
            return Ok(());
        }
    }

    let root = match select_source()? {
        SourceType::CustomGit { url, branch } => setup_root_custom_git(&url, branch.as_deref()),
        SourceType::CustomLocal(path) => setup_root_custom_local(path),
        SourceType::Latest => setup_root_latest(),
    }?;

    build_server(root)?;

    if has_installed("systemctl") {
        if Confirm::new()
            .with_prompt("Setup sysmtemd service for Eldiron server?")
            .default(true)
            .interact()?
        {
            setup_server_service_systemd()?;
        }
    }

    Ok(())
}

fn select_source() -> Result<SourceType> {
    let selection = Select::with_theme(&ColorfulTheme::default())
        .with_prompt("Please select where is the source code of Eldiron:")
        .items(&SourceSelectItem::labels())
        .default(0)
        .interact_opt()?;

    match selection {
        Some(index) => match SOURCE_SELECT_ITEM_LIST[index] {
            SourceSelectItem::Default => Ok(SourceType::Latest),
            SourceSelectItem::Custom => select_source_custom(),
        },
        None => {
            println!("No selection. Use default option instead.");

            Ok(SourceType::Latest)
        }
    }
}

fn select_source_custom() -> Result<SourceType> {
    let selection = Select::with_theme(&ColorfulTheme::default())
        .with_prompt("Please select custom Eldiron source:")
        .items(&SourceCustomSelectItem::labels())
        .default(0)
        .interact_opt()?;

    match selection {
        Some(index) => match SOURCE_CUSTOM_SELECT_ITEM_LIST[index] {
            SourceCustomSelectItem::Git => select_source_custom_git(),
            SourceCustomSelectItem::Local => select_source_custom_local(),
        },
        None => {
            println!("No selection. Use default option instead.");

            Ok(SourceType::Latest)
        }
    }
}

fn select_source_custom_git() -> Result<SourceType> {
    let url = Input::<String>::with_theme(&ColorfulTheme::default())
        .with_prompt("Git URL (Use default repo if omitted):")
        .default(ELDIRON_GIT_URL.to_owned())
        .interact_text()?;

    let branch = Input::<String>::with_theme(&ColorfulTheme::default())
        .with_prompt("Branch (eg. v0.1.0, master. Use default branch if omitted):")
        .allow_empty(true)
        .interact_text()?;

    let branch = if branch.is_empty() {
        None
    } else {
        Some(branch)
    };

    Ok(SourceType::CustomGit { url, branch })
}

fn select_source_custom_local() -> Result<SourceType> {
    let path = Input::<String>::with_theme(&ColorfulTheme::default())
        .with_prompt("Directory:")
        .default(".".to_owned())
        .interact_text()?;

    Ok(SourceType::CustomLocal(path))
}

fn setup_root_custom_git(url: &str, branch: Option<&str>) -> Result<String> {
    println!("Setup Eldiron source code from Git repository: {}", url);

    if let Some(branch) = branch {
        println!("Branch: {}", branch);
    }

    let root = parse_clone_dir_from_url(&url)?;

    if remove_dir_if_exist(&root)? {
        println!("Removing directory: {}", root);
    }

    git_clone(&url, branch)?;

    Ok(root)
}

fn setup_root_custom_local(path: String) -> Result<String> {
    println!("Setup Eldiron source code from local directory: {}", path);

    Ok(path)
}

fn setup_root_latest() -> Result<String> {
    let latest_tag = git_get_remote_latest_tag(ELDIRON_GIT_URL)?;

    println!(
        "Setup Eldiron source code from latest release: {}",
        latest_tag
    );

    let latest_tag = if latest_tag.is_empty() {
        None
    } else {
        Some(latest_tag.as_str())
    };

    let root = parse_clone_dir_from_url(ELDIRON_GIT_URL)?;

    if remove_dir_if_exist(&root)? {
        println!("Removing directory: {}", root);
    }

    git_clone(ELDIRON_GIT_URL, latest_tag)?;

    Ok(root)
}