use std::sync::Arc;
use systemprompt_logging::CliService;
use systemprompt_models::{AppPaths, Config, ContentConfigRaw};
#[cfg(feature = "geolocation")]
use systemprompt_analytics::GeoIpReader;
#[cfg(feature = "geolocation")]
use crate::error::RuntimeError;
#[cfg(feature = "geolocation")]
pub(super) fn load_geoip_database(
config: &Config,
show_warnings: bool,
) -> Result<Option<GeoIpReader>, RuntimeError> {
let Some(geoip_path) = &config.geoip_database_path else {
if show_warnings {
CliService::warning(
"GeoIP database not configured - geographic data will not be available",
);
CliService::info(" To enable geographic data:");
CliService::info(
" 1. Download MaxMind GeoLite2-City database from: https://dev.maxmind.com/geoip/geolite2-free-geolocation-data",
);
CliService::info(
" 2. Add paths.geoip_database to your profile pointing to the .mmdb file",
);
}
return Ok(None);
};
match maxminddb::Reader::open_readfile(geoip_path) {
Ok(reader) => Ok(Some(Arc::new(reader))),
Err(e) => Err(RuntimeError::GeoIpUnreadable {
path: geoip_path.clone(),
message: e.to_string(),
}),
}
}
#[cfg(not(feature = "geolocation"))]
#[expect(
clippy::missing_const_for_fn,
reason = "mirrors the geolocation loader signature; const would propagate a \
feature-forked constness to callers"
)]
pub(super) fn load_geoip_database(
_config: &Config,
_show_warnings: bool,
) -> Result<Option<systemprompt_analytics::GeoIpReader>, crate::error::RuntimeError> {
Ok(None)
}
pub(super) fn load_content_config(
config: &Config,
app_paths: &AppPaths,
) -> Option<Arc<ContentConfigRaw>> {
let content_config_path = app_paths.system().content_config().to_path_buf();
if !content_config_path.exists() {
CliService::warning(&format!(
"Content config not found at: {}",
content_config_path.display()
));
CliService::info(" Landing page detection will not be available.");
return None;
}
let yaml_content = match std::fs::read_to_string(&content_config_path) {
Ok(c) => c,
Err(e) => {
CliService::warning(&format!(
"Could not read content config from {}: {}",
content_config_path.display(),
e
));
CliService::info(" Landing page detection will not be available.");
return None;
},
};
match serde_yaml::from_str::<ContentConfigRaw>(&yaml_content) {
Ok(mut content_cfg) => {
let base_url = config.api_external_url.trim_end_matches('/');
base_url.clone_into(&mut content_cfg.metadata.structured_data.organization.url);
let logo = &content_cfg.metadata.structured_data.organization.logo;
if logo.starts_with('/') {
content_cfg.metadata.structured_data.organization.logo =
format!("{base_url}{logo}");
}
Some(Arc::new(content_cfg))
},
Err(e) => {
CliService::warning(&format!(
"Could not parse content config from {}: {}",
content_config_path.display(),
e
));
CliService::info(" Landing page detection will not be available.");
None
},
}
}