1use std::sync::RwLock;
9
10pub const DEFAULT: &str = concat!(
12 "datui/",
13 env!("CARGO_PKG_VERSION"),
14 " (+https://github.com/derekwisong/datui)"
15);
16
17static CONFIGURED: RwLock<Option<String>> = RwLock::new(None);
20
21pub fn resolve(setting: &str) -> String {
24 let setting = setting.trim();
25 if setting.is_empty() {
26 DEFAULT.to_string()
27 } else {
28 setting.to_string()
29 }
30}
31
32pub fn is_valid(value: &str) -> bool {
35 value.chars().all(|c| (' '..='~').contains(&c))
36}
37
38pub fn configure(setting: &str) {
40 let value = setting.trim();
41 *CONFIGURED.write().unwrap_or_else(|e| e.into_inner()) =
42 (!value.is_empty()).then(|| value.to_string());
43}
44
45pub fn get() -> String {
47 match CONFIGURED
48 .read()
49 .unwrap_or_else(|e| e.into_inner())
50 .as_deref()
51 {
52 Some(value) => value.to_string(),
53 None => DEFAULT.to_string(),
54 }
55}
56
57#[cfg(any(feature = "http", feature = "cloud"))]
59pub fn ureq_config() -> ureq::config::ConfigBuilder<ureq::typestate::AgentScope> {
60 ureq::Agent::config_builder().user_agent(get())
61}
62
63#[cfg(feature = "cloud")]
66pub const CLIENT_KEY: object_store::ClientConfigKey = object_store::ClientConfigKey::UserAgent;
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn the_default_names_datui_its_version_and_home_and_nothing_else() {
74 assert_eq!(
75 DEFAULT,
76 format!(
77 "datui/{} (+https://github.com/derekwisong/datui)",
78 env!("CARGO_PKG_VERSION")
79 )
80 );
81 assert!(is_valid(DEFAULT));
82 }
83
84 #[test]
85 fn a_blank_setting_is_the_default_and_anything_else_replaces_it() {
86 assert_eq!(resolve(""), DEFAULT);
87 assert_eq!(resolve(" "), DEFAULT);
88 assert_eq!(resolve(" research-crawler/2 "), "research-crawler/2");
89 }
90
91 #[test]
92 fn control_characters_are_not_a_header() {
93 assert!(is_valid("my-tool/1.0 (+https://example.com)"));
94 assert!(!is_valid("a\r\nX-Injected: 1"));
95 assert!(!is_valid("café"));
96 }
97}