why2_chat/
misc.rs

1/*
2This is part of WHY2
3Copyright (C) 2022-2026 Václav Šmejkal
4
5This program is free software: you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation, either version 3 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use std::
20{
21    fs,
22    path::Path,
23    time::Duration,
24};
25
26use ureq::{ Error, Agent };
27
28use serde_json::Value;
29
30use semver::Version;
31
32use crate::options;
33
34#[cfg(feature = "client")]
35use std::sync::mpsc::Sender;
36
37#[cfg(feature = "client")]
38use crate::network::client::ClientEvent;
39
40//PRIVATE
41fn get_dir(dir: &str) -> String
42{
43    dir.replace("{HOME}", dirs::home_dir().expect("Could not determine home directory").to_str().expect("Invalid home directory"))
44}
45
46fn get_config_dir() -> String
47{
48    get_dir(options::USER_CONFIG_DIR)
49}
50
51//PUBLIC
52pub fn get_version<'a>() -> &'a str //GET COMPILED PACKAGE VERSION
53{
54    env!("CARGO_PKG_VERSION")
55}
56
57pub fn get_identifier() -> String //GET IDENTIFIER OF PACKAGE VERSION [WHY2/VERSION]
58{
59    format!("WHY2/{}", get_version())
60}
61
62pub fn fetch_data(url: &str) -> Result<String, Error> //FETCH DATA USING REQWEST
63{
64    //CREATE CUSTOM CLIENT (WITH TIMEOUT)
65    let agent: Agent = Agent::config_builder()
66        .timeout_global(Some(Duration::from_millis(options::FETCH_TIMEOUT)))
67        .build()
68        .into();
69
70    //FETCH DATA
71    agent.get(url)
72        .header("User-Agent", &get_identifier())
73        .call()?
74        .body_mut()
75        .read_to_string()
76}
77
78pub fn check_version(#[cfg(feature = "client")] tx: &Sender<ClientEvent>) //CHECK FOR LATEST WHY2 VERSION
79{
80    //FETCH METADATA (USE CUSTOM User-Agent, FOR CRATES.IO TO WORK)
81    let metadata_raw = match fetch_data(options::METADATA_URL)
82    {
83        Ok(m) => m,
84        Err(_) =>
85        {
86            let print_text = "Fetching versions failed, this release could be unsafe!";
87
88            #[cfg(feature = "client")]
89            {
90                tx.send(ClientEvent::Info(print_text.to_string(), true, 0)).unwrap()
91            }
92
93            #[cfg(feature = "server")]
94            {
95                log::warn!("{print_text}");
96            }
97
98            return;
99        }
100    };
101
102    //PARSE METADATA TO JSON
103    let metadata: Value = serde_json::from_str(&metadata_raw).expect("Parsing versions failed"); //PARSE
104    let newest_version = metadata.get("crate") //GET LATEST VERSION
105        .and_then(|c| c.get("newest_version"))
106        .and_then(|v| v.as_str())
107        .unwrap();
108
109    //OUTDATED VERSION, CALCULATE HOW MANY NEWER VERSIONS EXIST
110    let current_version = get_version();
111    if current_version != newest_version
112    {
113        //GET ARRAY OF VERSIONS
114        let versions = metadata.get("versions").and_then(|v| v.as_array()).unwrap();
115        let mut newer_versions = 0;
116        let current_version = Version::parse(current_version).expect("Invalid version");
117
118        //CALCULATE
119        for version in versions
120        {
121            //FOUND NEWER VERSION
122            if Version::parse(version.get("num").and_then(|n| n.as_str()).unwrap()).unwrap() > current_version
123            {
124                //INCREMENT COUNTER
125                newer_versions += 1;
126            }
127        }
128
129        let print_text = format!("This release could be unsafe! You are {newer_versions} versions behind! ({current_version}/{newest_version})");
130
131        #[cfg(feature = "client")]
132        {
133            tx.send(ClientEvent::Info(print_text, true, 0)).unwrap()
134        }
135
136        #[cfg(feature = "server")]
137        {
138            log::warn!("{print_text}");
139        }
140    }
141}
142
143pub fn check_directory() //CREATE WHY2 CONFIG DIRECTORY
144{
145    let config = get_config_dir() + options::CONFIG_DIR;
146
147    //CREATE WHY2 CONFIG DIRECTORY
148    if !Path::new(&config).is_dir()
149    {
150        fs::create_dir_all(config).expect("Failed to create WHY2 config directory");
151    }
152}
153
154pub fn get_why2_dir() -> String //RETURN PATH TO WHY2 CONFIG DIRECTORY
155{
156    get_config_dir() + options::CONFIG_DIR
157}