pub mod hostname;
pub mod hosts;
pub mod lan_ip;
pub mod mdns;
pub mod server;
pub mod trust;
pub mod worktree;
pub(crate) fn ascii_case_collisions<'a>(
keys: impl Iterator<Item = &'a str>,
) -> std::collections::HashSet<String> {
let mut seen = std::collections::HashSet::new();
let mut collisions = std::collections::HashSet::new();
for key in keys {
let folded = key.to_ascii_lowercase();
if !seen.insert(folded.clone()) {
collisions.insert(folded);
}
}
collisions
}
pub fn effective_tld(s: &crate::settings::Settings) -> &str {
if s.proxy.lan || !s.proxy.lan_ip.is_empty() {
"local"
} else {
&s.proxy.tld
}
}
pub fn build_proxy_url(host: Option<&str>, s: &crate::settings::Settings) -> Option<String> {
if !s.proxy.enable {
return None;
}
let host = host?;
let scheme = if s.proxy.https { "https" } else { "http" };
let tld = effective_tld(s);
let standard_port = if s.proxy.https { 443u16 } else { 80u16 };
let effective_port = u16::try_from(s.proxy.port).ok().filter(|&p| p > 0)?;
let authority = format!("{host}.{tld}");
Some(if effective_port == standard_port {
format!("{scheme}://{authority}")
} else {
format!("{scheme}://{authority}:{effective_port}")
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ascii_case_collisions() {
let none = ascii_case_collisions(["myapp", "other", "third"].into_iter());
assert!(none.is_empty());
let folded = ascii_case_collisions(["MyApp", "myapp", "other"].into_iter());
assert_eq!(folded.len(), 1);
assert!(folded.contains("myapp"));
let exact = ascii_case_collisions(["dup", "dup"].into_iter());
assert!(exact.contains("dup"));
let unicode = ascii_case_collisions(["café", "CAFÉ"].into_iter());
assert!(unicode.is_empty());
}
fn proxy_settings() -> crate::settings::Settings {
let mut s = crate::settings::Settings::default();
s.proxy.enable = true;
s.proxy.https = true;
s.proxy.port = 443;
s.proxy.tld = "localhost".to_string();
s
}
#[test]
fn test_build_proxy_url_port_suffix() {
let mut s = proxy_settings();
assert_eq!(
build_proxy_url(Some("api.myproj"), &s).as_deref(),
Some("https://api.myproj.localhost")
);
s.proxy.port = 8088;
assert_eq!(
build_proxy_url(Some("api.myproj"), &s).as_deref(),
Some("https://api.myproj.localhost:8088")
);
s.proxy.https = false;
s.proxy.port = 80;
assert_eq!(
build_proxy_url(Some("api.myproj"), &s).as_deref(),
Some("http://api.myproj.localhost")
);
}
#[test]
fn test_build_proxy_url_requires_enabled_proxy_and_host() {
let mut s = proxy_settings();
assert_eq!(build_proxy_url(None, &s), None);
s.proxy.enable = false;
assert_eq!(build_proxy_url(Some("api.myproj"), &s), None);
}
#[test]
fn test_build_proxy_url_uses_lan_tld() {
let mut s = proxy_settings();
s.proxy.tld = "test".to_string();
assert_eq!(effective_tld(&s), "test");
s.proxy.lan = true;
assert_eq!(effective_tld(&s), "local");
assert_eq!(
build_proxy_url(Some("api.myproj"), &s).as_deref(),
Some("https://api.myproj.local")
);
s.proxy.lan = false;
s.proxy.lan_ip = "192.168.1.42".to_string();
assert_eq!(
build_proxy_url(Some("api.myproj"), &s).as_deref(),
Some("https://api.myproj.local")
);
}
#[test]
fn test_build_proxy_url_rejects_invalid_port() {
let mut s = proxy_settings();
s.proxy.port = 0;
assert_eq!(build_proxy_url(Some("api.myproj"), &s), None);
s.proxy.port = 70000;
assert_eq!(build_proxy_url(Some("api.myproj"), &s), None);
}
}