1use errors::DirectoryError;
2
3#[cfg(feature = "audio")]
4pub mod audio;
5pub mod errors;
6pub mod logger;
7#[cfg(feature = "rpc")]
8pub mod rpc;
9pub mod state;
10#[cfg(any(test, feature = "test_utils"))]
11pub mod test_utils;
12#[cfg(feature = "rpc")]
13pub mod udp;
14
15#[cfg(test)]
16extern crate rstest_reuse;
17
18#[macro_export]
41macro_rules! function_name {
42 () => {{
43 const fn f() {}
45 fn type_name_of<T>(_: T) -> &'static str {
46 std::any::type_name::<T>()
47 }
48 let name = type_name_of(f);
49 &name[..name.len() - 3]
51 }};
52}
53
54#[must_use]
55pub fn format_duration(duration: &std::time::Duration) -> String {
56 let total_seconds = duration.as_secs();
57 let hours = total_seconds / 3600;
58 let minutes = (total_seconds % 3600) / 60;
59 let seconds = duration.as_secs_f32() % 60.;
60
61 format!("{hours:02}:{minutes:02}:{seconds:05.2}")
62}
63
64pub fn get_data_dir() -> Result<std::path::PathBuf, DirectoryError> {
73 let directory = if let Ok(s) = std::env::var("MECOMP_DATA") {
74 std::path::PathBuf::from(s)
75 } else if let Some(proj_dirs) = directories::ProjectDirs::from("", "", "mecomp") {
76 proj_dirs.data_local_dir().to_path_buf()
77 } else {
78 return Err(DirectoryError::Data);
79 };
80 Ok(directory)
81}
82
83pub fn get_config_dir() -> Result<std::path::PathBuf, DirectoryError> {
92 let directory = if let Ok(s) = std::env::var("MECOMP_CONFIG") {
93 std::path::PathBuf::from(s)
94 } else if let Some(proj_dirs) = directories::ProjectDirs::from("", "", "mecomp") {
95 proj_dirs.config_local_dir().to_path_buf()
96 } else {
97 return Err(DirectoryError::Config);
98 };
99 Ok(directory)
100}
101
102#[must_use]
105pub fn is_server_running(port: u16) -> bool {
106 std::net::TcpStream::connect(format!("localhost:{port}")).is_ok()
107}
108
109#[cfg(test)]
110mod test {
111 use super::format_duration;
112 use pretty_assertions::assert_eq;
113 use rstest::rstest;
114 use std::time::Duration;
115
116 #[rstest]
117 #[case::zero(Duration::from_secs(0), "00:00:00.00")]
118 #[case::sub_second(Duration::from_millis(100), "00:00:00.10")]
119 #[case::sub_second(Duration::from_millis(101), "00:00:00.10")]
120 #[case::one_second(Duration::from_secs(1), "00:00:01.00")]
121 #[case::one_minute(Duration::from_secs(60), "00:01:00.00")]
122 #[case::one_hour(Duration::from_secs(3600), "01:00:00.00")]
123 #[case::one_hour_one_minute_one_second(Duration::from_secs(3661), "01:01:01.00")]
124 #[case(Duration::from_secs(3600 + 120 + 1), "01:02:01.00")]
125 fn test_format_duration(#[case] duration: Duration, #[case] expected: &str) {
126 let actual = format_duration(&duration);
127 assert_eq!(actual, expected);
128 }
129
130 #[test]
131 fn test_function_name() {
132 fn test_function() {
133 let result = super::function_name!();
134 assert!(result.ends_with("test_function"));
135 }
136
137 test_function();
138 }
139
140 #[test]
141 fn test_get_data_dir() {
142 let data_dir = super::get_data_dir().unwrap();
143 assert_eq!(
144 data_dir
145 .components()
146 .last()
147 .unwrap()
148 .as_os_str()
149 .to_string_lossy(),
150 "mecomp"
151 );
152 }
153
154 #[test]
155 fn test_get_config_dir() {
156 let config_dir = super::get_config_dir().unwrap();
157 assert_eq!(
158 config_dir
159 .components()
160 .last()
161 .unwrap()
162 .as_os_str()
163 .to_string_lossy(),
164 "mecomp"
165 );
166 }
167}