use std::collections::HashMap;
use std::fmt;
use crate::error::{FoundryLocalError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
Fatal,
}
impl LogLevel {
fn as_core_str(&self) -> &'static str {
match self {
Self::Trace => "Verbose",
Self::Debug => "Debug",
Self::Info => "Information",
Self::Warn => "Warning",
Self::Error => "Error",
Self::Fatal => "Fatal",
}
}
}
pub trait Logger: Send + Sync {
fn log(&self, level: LogLevel, message: &str);
}
#[derive(Default)]
pub struct FoundryLocalConfig {
app_name: String,
app_data_dir: Option<String>,
model_cache_dir: Option<String>,
logs_dir: Option<String>,
log_level: Option<LogLevel>,
web_service_urls: Option<String>,
service_endpoint: Option<String>,
library_path: Option<String>,
additional_settings: Option<HashMap<String, String>>,
logger: Option<Box<dyn Logger>>,
}
impl fmt::Debug for FoundryLocalConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FoundryLocalConfig")
.field("app_name", &self.app_name)
.field("app_data_dir", &self.app_data_dir)
.field("model_cache_dir", &self.model_cache_dir)
.field("logs_dir", &self.logs_dir)
.field("log_level", &self.log_level)
.field("web_service_urls", &self.web_service_urls)
.field("service_endpoint", &self.service_endpoint)
.field("library_path", &self.library_path)
.field("additional_settings", &self.additional_settings)
.field("logger", &self.logger.as_ref().map(|_| ".."))
.finish()
}
}
impl FoundryLocalConfig {
pub fn new(app_name: impl Into<String>) -> Self {
Self {
app_name: app_name.into(),
..Self::default()
}
}
pub fn app_data_dir(mut self, dir: impl Into<String>) -> Self {
self.app_data_dir = Some(dir.into());
self
}
pub fn model_cache_dir(mut self, dir: impl Into<String>) -> Self {
self.model_cache_dir = Some(dir.into());
self
}
pub fn logs_dir(mut self, dir: impl Into<String>) -> Self {
self.logs_dir = Some(dir.into());
self
}
pub fn log_level(mut self, level: LogLevel) -> Self {
self.log_level = Some(level);
self
}
pub fn web_service_urls(mut self, urls: impl Into<String>) -> Self {
self.web_service_urls = Some(urls.into());
self
}
pub fn service_endpoint(mut self, endpoint: impl Into<String>) -> Self {
self.service_endpoint = Some(endpoint.into());
self
}
pub fn library_path(mut self, path: impl Into<String>) -> Self {
self.library_path = Some(path.into());
self
}
pub fn additional_setting(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.additional_settings
.get_or_insert_with(HashMap::new)
.insert(key.into(), value.into());
self
}
pub fn logger(mut self, logger: impl Logger + 'static) -> Self {
self.logger = Some(Box::new(logger));
self
}
}
#[derive(Debug, Clone)]
pub(crate) struct Configuration {
pub params: HashMap<String, String>,
}
impl Configuration {
pub fn new(config: FoundryLocalConfig) -> Result<(Self, Option<Box<dyn Logger>>)> {
let app_name = config.app_name.trim().to_string();
if app_name.is_empty() {
return Err(FoundryLocalError::InvalidConfiguration {
reason: "app_name must be set and non-empty".into(),
});
}
let mut params = HashMap::new();
params.insert("AppName".into(), app_name);
let optional_fields = [
("AppDataDir", config.app_data_dir),
("ModelCacheDir", config.model_cache_dir),
("LogsDir", config.logs_dir),
("LogLevel", config.log_level.map(|l| l.as_core_str().into())),
("WebServiceUrls", config.web_service_urls),
("WebServiceExternalUrl", config.service_endpoint),
("FoundryLocalCorePath", config.library_path),
];
for (key, value) in optional_fields {
if let Some(v) = value {
params.insert(key.into(), v);
}
}
if let Some(extra) = config.additional_settings {
params.extend(extra);
}
Ok((Self { params }, config.logger))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_config() {
let cfg = FoundryLocalConfig::new("TestApp").log_level(LogLevel::Debug);
let (c, _) = Configuration::new(cfg).unwrap();
assert_eq!(c.params["AppName"], "TestApp");
assert_eq!(c.params["LogLevel"], "Debug");
}
#[test]
fn empty_app_name_fails() {
let cfg = FoundryLocalConfig::new(" ");
assert!(Configuration::new(cfg).is_err());
}
#[test]
fn builder_additional_settings() {
let cfg = FoundryLocalConfig::new("App").additional_setting("Foo", "bar");
let (c, _) = Configuration::new(cfg).unwrap();
assert_eq!(c.params["Foo"], "bar");
}
}