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
use crate::{get_executable_directory, read_json_file};
use anyhow::{Context, Result};
use core::fmt;
use regex::Regex;
use std::{fs::File, io::Write};

/// Data type for the API setting file.
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
pub struct ApiSetting {
    pub key: String,
}

/// Saves the given API key into the API setting file.
pub fn setup_api(key: String) -> Result<()> {
    use crate::constants::API_JSON_NAME;

    let executable_dir = get_executable_directory()?;
    let regex = Regex::new(r"^[a-zA-Z0-9]+$")?;

    // Api key validation.
    if key.len() != 32 || !regex.is_match(&key) {
        println!("Please enter a valid key!");
    } else {
        let new_api_setting = ApiSetting { key };

        let api_json_string = serde_json::to_string(&new_api_setting)?;
        File::create(format!(
            "{}/weather-cli-{}.json",
            executable_dir, API_JSON_NAME
        ))?
        .write_all(api_json_string.as_bytes())?;
        println!("Successfully updated your key!");
    }

    Ok(())
}

/// City data type. It's a part of the user setting file.
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
pub struct City {
    pub name: String,
    pub lat: f64,
    pub lon: f64,
    pub country: String,
}
impl fmt::Display for City {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
        let output = format!(
            "{}, {} (lat: {}, lon: {})",
            self.name, self.country, self.lat, self.lon
        );
        write!(f, "{}", output)
    }
}

/// Enum for units. It's a part of the user setting file.
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, PartialEq)]
pub enum Unit {
    Metric,
    Imperial,
}
impl fmt::Display for Unit {
    /// Returns the unit name.
    fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
        match self {
            Unit::Metric => write!(f, "metric"),
            Unit::Imperial => write!(f, "imperial"),
        }
    }
}

/// User setting data type.
#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)]
pub struct UserSetting {
    pub city: Option<City>,
    pub unit: Option<Unit>,
    pub display_emoji: Option<bool>,
}

/// Update user settings.
pub fn update_setting(setting_args: &UserSetting) -> Result<()> {
    use crate::constants::SETTINGS_JSON_NAME;

    let mut json_data = read_json_file::<UserSetting>(SETTINGS_JSON_NAME)?;

    // Update the setting file with given arguments.
    // 1. City
    {
        let mut using_city: Option<City> = None;
        if let Some(args_city) = &setting_args.city {
            using_city = Some(City {
                name: String::from(&args_city.name),
                lat: args_city.lat,
                lon: args_city.lon,
                country: String::from(&args_city.country),
            });
        }
        json_data.city = using_city;
    }
    // 2. Unit
    if let Some(unit) = &setting_args.unit {
        json_data.unit = Some(unit.clone());
    }
    // 3. Emoji
    if let Some(display_emoji) = &setting_args.display_emoji {
        json_data.display_emoji = Some(*display_emoji);
    }

    let json_string = serde_json::to_string(&json_data)?;

    // Generate a new setting file.
    let executable_dir = get_executable_directory()?;
    File::create(format!(
        "{}/weather-cli-{}.json",
        executable_dir, SETTINGS_JSON_NAME
    ))?
    .write_all(json_string.as_bytes())
    .context("Couldn't write JSON file.")?;

    Ok(())
}