1use anyhow::{anyhow, Context, Result};
2
3use std::{
4 env,
5 fs::File,
6 io::{Read, Write},
7};
8
9pub mod api_usage;
10pub mod cmd_line;
11pub mod user_setup;
12
13pub mod program_info {
15 pub const PROGRAM_NAME: &str = "pogoda-terminal";
17 pub const PROGRAM_DESCRIPTION: &str = "Weather for command-line fans!";
19 pub const PROGRAM_AUTHORS: &str = "er2de2";
21 pub const CRATES_IO_URL: &str = "https://crates.io/crates/pogoda-terminal";
23 pub const REPOSITORY_URL: &str = "https://github.com/er2de2/pogoda-terminal";
25}
26
27mod constants {
29 pub const API_JSON_NAME: &str = "api";
31
32 pub const SETTINGS_JSON_NAME: &str = "settings";
34
35 pub const API_URL: &str = "https://api.openweathermap.org/data/2.5/weather?lat={lat_value}&lon={lon_value}&lang=pl&appid={api_key}&units={unit}";
56}
57
58pub fn get_executable_directory() -> Result<String> {
60 let executable_path =
61 env::current_exe().context("Couldn't get the executable file directory!")?;
62 let executable_directory = executable_path
63 .parent()
64 .context("Couldn't get the executable directory!")?;
65
66 if let Some(dir_str) = executable_directory.to_str() {
67 return Ok(dir_str.to_string());
68 }
69
70 Err(anyhow!("Unable to get the executable directory."))
71}
72
73pub fn get_json_file(name: &str) -> Result<File> {
75 let executable_dir = get_executable_directory()?;
76
77 let file = match File::open(format!("{}/weather-cli-{}.json", executable_dir, name)) {
78 Ok(f) => f,
79 Err(_) => {
80 let mut new_file =
81 File::create(format!("{}/weather-cli-{}.json", executable_dir, name))
82 .context("Couldn't create a json file.")?;
83 new_file
84 .write_all("{}".as_bytes())
85 .context("Couldn't create a json file.")?;
86
87 File::open(format!("{}/weather-cli-{}.json", executable_dir, name))
88 .context("Couldn't get the json file.")?
89 }
90 };
91
92 Ok(file)
93}
94
95pub enum ErrorMessageType {
96 SettingRead,
97 ApiResponseRead,
98}
99
100fn get_file_read_error_message(error_type: ErrorMessageType, context: &str) -> String {
101 match error_type {
102 ErrorMessageType::SettingRead => {
103 format!("Failed to read the following file: {}", context)
104 }
105 ErrorMessageType::ApiResponseRead => {
106 format!("The given '{}' JSON input may be invalid.", context)
107 }
108 }
109}
110
111pub fn read_json_file<T: serde::de::DeserializeOwned>(json_name: &str) -> Result<T> {
113 use constants::API_JSON_NAME;
114
115 let mut file = get_json_file(json_name)?;
116 let mut json_string = String::new();
117 file.read_to_string(&mut json_string)?;
118
119 let api_key_data: T = serde_json::from_str(&json_string).context(
120 get_file_read_error_message(ErrorMessageType::SettingRead, API_JSON_NAME),
121 )?;
122
123 Ok(api_key_data)
124}
125
126pub fn read_json_response<T: serde::de::DeserializeOwned>(
128 response: &str,
129 error_message_type: ErrorMessageType,
130 error_context: &str,
131) -> Result<T> {
132 let response_data: T = serde_json::from_str(response).context(get_file_read_error_message(
133 error_message_type,
134 error_context,
135 ))?;
136
137 Ok(response_data)
138}
139
140pub fn get_emoji(icon_id: &str) -> String {
142 let return_value = match icon_id {
143 "01d" => "☀️",
144 "02d" => "⛅️",
145 "03d" => "☁️",
146 "04d" => "☁️",
147 "09d" => "🌧️",
148 "10d" => "🌦️",
149 "11d" => "⛈️",
150 "13d" => "❄️",
151 "50d" => "🌨️",
152 "01n" => "🌑",
153 "02n" => "🌑☁️",
154 "03n" => "☁️",
155 "04n" => "☁️☁️",
156 "09n" => "🌧️",
157 "10n" => "☔️",
158 "11n" => "⛈️",
159 "13n" => "❄️",
160 _ => "",
161 };
162
163 if !return_value.is_empty() {
164 format!("{} ", return_value)
165 } else {
166 return_value.to_string()
167 }
168}
169
170#[cfg(test)]
172mod tests;