1
// This file is part of hnefatafl-copenhagen.
2
//
3
// hnefatafl-copenhagen is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU Affero General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// hnefatafl-copenhagen is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU Affero General Public License for more details.
12
//
13
// You should have received a copy of the GNU Affero General Public License
14
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15

            
16
#[cfg(any(target_family = "unix", target_family = "windows"))]
17
use std::process::Command;
18
use std::{env, fs::DirBuilder, path::PathBuf, process::ExitStatus, time::Duration};
19

            
20
use directories::ProjectDirs;
21
use env_logger::Builder;
22
use log::LevelFilter;
23

            
24
use crate::ai::{AI, AiBanal, AiBasic, AiMonteCarlo};
25

            
26
/// # Errors
27
///
28
/// If you don't choose banal, basic, or monte-carlo.
29
pub fn choose_ai(
30
    ai: &str,
31
    seconds: Option<u64>,
32
    depth: Option<u8>,
33
    sequential: bool,
34
) -> anyhow::Result<Box<dyn AI>> {
35
    match ai {
36
        "banal" => Ok(Box::new(AiBanal)),
37
        "basic" => {
38
            let depth = depth.unwrap_or(4);
39

            
40
            Ok(Box::new(AiBasic::new(depth, sequential)))
41
        }
42
        "monte-carlo" => {
43
            let seconds = seconds.unwrap_or(10);
44
            let depth = depth.unwrap_or(20);
45

            
46
            Ok(Box::new(AiMonteCarlo::new(
47
                Duration::from_secs(seconds),
48
                depth,
49
            )))
50
        }
51
        _ => Err(anyhow::Error::msg(
52
            "you must pass banal, basic, or monte-carlo to --ai",
53
        )),
54
    }
55
}
56

            
57
#[allow(clippy::missing_errors_doc)]
58
pub fn clear_screen() -> anyhow::Result<ExitStatus> {
59
    #[cfg(not(any(target_family = "unix", target_family = "windows")))]
60
    return Ok(ExitStatus::default());
61

            
62
    #[cfg(target_family = "unix")]
63
    let exit_status = Command::new("clear").status()?;
64

            
65
    #[cfg(target_family = "windows")]
66
    let exit_status = Command::new("cls").status()?;
67

            
68
    #[cfg(any(target_family = "unix", target_family = "windows"))]
69
    Ok(exit_status)
70
}
71

            
72
/// # Errors
73
///
74
/// If it fails to create the directory or the directory does not already exist.
75
pub fn create_data_folder() -> anyhow::Result<()> {
76
    let project_dir = ProjectDirs::from("org", "Hnefatafl Org", "hnefatafl-copenhagen");
77

            
78
    if let Some(project_dir) = project_dir {
79
        DirBuilder::new()
80
            .recursive(true)
81
            .create(project_dir.data_dir())?;
82
    }
83

            
84
    Ok(())
85
}
86

            
87
#[must_use]
88
pub fn data_file(file: &str) -> PathBuf {
89
    let project_dir = ProjectDirs::from("org", "Hnefatafl Org", "hnefatafl-copenhagen");
90

            
91
    let mut project_dir = if let Some(project_dir) = project_dir {
92
        project_dir.data_local_dir().to_path_buf()
93
    } else {
94
        PathBuf::new()
95
    };
96

            
97
    project_dir.push(file);
98
    project_dir
99
}
100

            
101
pub fn init_logger(module: &str, debug: bool, systemd: bool) {
102
    let mut builder = Builder::new();
103

            
104
    if systemd {
105
        builder.format_timestamp(None);
106
        builder.format_target(false);
107
    }
108

            
109
    if let Ok(var) = env::var("RUST_LOG") {
110
        builder.parse_filters(&var);
111
    } else if debug {
112
        builder.filter(Some(module), LevelFilter::Debug);
113
    } else {
114
        // If no RUST_LOG provided, default to logging at the Info level.
115
        #[cfg(not(feature = "debug"))]
116
        builder.filter(Some(module), LevelFilter::Info);
117
        #[cfg(feature = "debug")]
118
        builder.filter(Some(module), LevelFilter::Debug);
119
    }
120

            
121
    builder.init();
122
}
123

            
124
#[must_use]
125
pub fn split_whitespace_password(string: &str) -> (String, bool) {
126
    let mut ends_with_whitespace = false;
127

            
128
    if string.ends_with(|ch: char| ch.is_whitespace()) {
129
        ends_with_whitespace = true;
130
    }
131

            
132
    let mut string: String = string.split_whitespace().collect();
133

            
134
    if string.is_empty() {
135
        ends_with_whitespace = false;
136
    }
137

            
138
    if ends_with_whitespace {
139
        string.push(' ');
140
    }
141

            
142
    (string, ends_with_whitespace)
143
}