1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
use std::{env, path::Path, process::Stdio, time::Duration};

use anyhow::{bail, Context, Result};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use sqlx::{Connection, MySqlConnection};
use tokio::{fs, process::Command};

#[derive(Debug, Serialize, Deserialize)]
pub struct Config {
    pub eludris_dir: String,
}

pub async fn get_user_config() -> Result<Option<Config>> {
    let config_dir = Path::new(
        &env::var("XDG_CONFIG_HOME")
            .or_else(|_| env::var("HOME").map(|home| format!("{}/.config", home)))
            .context("Could not determine config path")?,
    )
    .join("eludris");

    if !config_dir.exists() {
        fs::create_dir_all(&config_dir)
            .await
            .context("Could not create config directory")?;
    }

    let config_path = config_dir.join("Cli.toml");

    if !config_path.exists() {
        Ok(None)
    } else {
        let config = fs::read_to_string(config_path)
            .await
            .context("Could not read config file")?;
        Ok(Some(
            toml::from_str(&config).context("Could not parse config file")?,
        ))
    }
}

pub async fn update_config_file(config: &Config) -> Result<()> {
    let config_dir = Path::new(
        &env::var("XDG_CONFIG_HOME")
            .or_else(|_| env::var("HOME").map(|home| format!("{}/.config", home)))
            .context("Could not determine config path")?,
    )
    .join("eludris");

    if !config_dir.exists() {
        fs::create_dir_all(&config_dir)
            .await
            .context("Could not create config directory")?;
    }

    let config_path = config_dir.join("Cli.toml");

    fs::write(
        config_path,
        toml::to_string(&config).context("Could not serialize default config")?,
    )
    .await
    .context("Could not find config file")?;
    Ok(())
}

pub fn check_eludris_exists(config: &Config) -> Result<bool> {
    let path = Path::new(&config.eludris_dir);
    if !path.is_dir() && path.exists() {
        bail!("An Eludris file exists but it is not a directory");
    }
    Ok(path.join("Eludris.toml").exists())
}

pub fn new_progress_bar(message: &str) -> ProgressBar {
    let bar = ProgressBar::new_spinner()
        .with_message(message.to_string())
        .with_prefix("~>")
        .with_style(
            ProgressStyle::with_template("{prefix:.yellow.bold} {spinner:.blue.bold} {msg}")
                .unwrap()
                .tick_strings(&[".    ", "..   ", "...  ", ".... ", "....."]),
        );
    bar.enable_steady_tick(Duration::from_millis(100));
    bar
}

pub fn end_progress_bar(bar: ProgressBar, message: &str) {
    bar.set_style(ProgressStyle::with_template("{prefix:.green.bold} {msg}").unwrap());
    bar.finish_with_message(message.to_string());
}

pub fn new_docker_command(config: &Config) -> Command {
    let mut command = Command::new("docker-compose");
    command
        .current_dir(&config.eludris_dir)
        .arg("-f")
        .arg("docker-compose.override.yml")
        .arg("-f")
        .arg("docker-compose.yml");
    command
}

pub async fn new_database_connection() -> Result<MySqlConnection> {
    let stdout = Command::new("docker")
        .arg("inspect")
        .arg("-f")
        .arg("{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}")
        .arg("eludris-mariadb-1")
        .stdout(Stdio::piped())
        .output()
        .await
        .context("Could not fetch mariadb address, is the docker daemon running?")?
        .stdout;
    let address = String::from_utf8(stdout).context("Could not convert address to a string")?;

    MySqlConnection::connect(&format!("mysql://root:root@{}:3306/eludris", address))
        .await
        .context("Could not connect to database")
}

pub async fn download_file(
    config: &Config,
    client: &Client,
    name: &str,
    next: bool,
    save_name: Option<&str>,
) -> Result<()> {
    log::info!("Fetching {}", name);
    let file = client
        .get(format!(
            "https://raw.githubusercontent.com/eludris/eludris/{}/{}",
            if next { "next" } else { "main" },
            if name == "docker-compose.prebuilt.yml" && next {
                "docker-compose.next.yml"
            } else {
                name
            }
        ))
        .send()
        .await
        .context(
            "Failed to fetch necessary files for setup. Please check your connection and try again",
        )?
        .text()
        .await
        .context("Failed to fetch necessary files for setup")?;
    log::info!("Writing {}", name);
    fs::write(
        format!("{}/{}", config.eludris_dir, save_name.unwrap_or(name)),
        file,
    )
    .await
    .context("Could not write setup files")?;
    Ok(())
}