|
|
|
|
упрощает создание и использование структур конфигурации с иерархической типизацией 12-факторным способом.
Config позволяет вам установить набор параметров по умолчанию, а затем расширить их путем объединения в конфигурации из различных источников:
- Переменные среды
- Другой экземпляр конфигурации
- Удаленная настройка: etcd, Consul
- Файлы: JSON, YAML, TOML, HJSON
- Ручное, программное переопределение (с помощью .set метода в экземпляре Config)
Дополнительно Config поддерживает:
- Просмотр и перечитывание файлов конфигурации в реальном времени
- Глубокий доступ к объединенной конфигурации через синтаксис пути
- Десериализация через serde конфигурацию или любое подмножество, определенное через путь
|
|
|
[dependencies]
config = "0.9"
ini - Добавляет поддержку для чтения файлов INI
json - Добавляет поддержку для чтения файлов JSON
hjson - Добавляет поддержку для чтения файлов HJSON
yaml - Добавляет поддержку для чтения файлов YAML
toml - Добавляет поддержку для чтения файлов TOML (по умолчанию включена)
|
|
|
Многоуровневая система конфигурации для приложений Rust (с сильной поддержкой 12-факторных приложений).
- Установка значений по умолчанию
- Установите явные значения (для программного переопределения)
- Чтение из JSON, TOML, YAML, HJSON, INI - файлов
- Чтение из среды
Неверно введенный тип - значения конфигурации могут считываться любым поддерживаемым типом, если существует разумное преобразование.
Доступ к вложенным полям с использованием форматированного пути. Использует подмножество JSONPath; в настоящее время поддерживает дочерние (redis.port) и индексированные операторы (databases[0].name)
|
|
Приложение двенадцати факторов( III. Конфигурация )
12factor.net/config
|
Другим подходом к конфигурации является использование конфигурационных файлов, которые не сохраняются в систему контроля версия, например ‘config/database.yml’ в Rails.
Это огромное улучшение перед использованием констант, которые сохраняются в коде, но по-прежнему и у этого метода есть недостатки: легко по ошибке сохранить конфигурационный файл в репозиторий; существует тенденция когда конфигурационные файлы разбросаны в разных местах и в разных форматах, из за этого становится трудно просматривать и управлять всеми настройками в одном месте. Кроме того форматы этих файлов, как правило, специфичны для конкретного языка или фреймворка.
Приложение двенадцати факторов хранит конфигурацию в переменных окружения (часто сокращается до env vars или env).
Переменные окружения легко изменить между развёртываниями, не изменяя код; в отличие от файлов конфигурации, менее вероятно случайно сохранить их в репозиторий кода; и в отличие от пользовательских конфигурационных файлов или других механизмов конфигурации.
В приложении двенадцати факторов переменные окружения являются не связанными между собой средствами управления, где каждая переменная окружения полностью независима от других.
Они никогда не группируются вместе в “окружения”, а вместо этого управляются независимо для каждого развёртывания.
Эта модель которая масштабируется постепенно вместе с естественным появлением большего количества развёртываний приложения за время его жизни.
|
|
|
File Cargo.toml:
dotenv = "0.15"
config = "0.14"
once_cell = "1.10"
serde = { version = "1.0", features = ["derive"] }
File .dev.env:
RUST_LOG=trace #debug
NO_COLOR=1 # Do not output ANSI escape codes
APP_CLIENT_AI_MOCK=true
File config/dev.toml
[open_ai]
key="mock"
model="davinci-002"
File settings.rs:
use config::{Config, Environment};
use serde::Deserialize;
use once_cell::sync::Lazy;
use std::env;
#[derive(Debug, Deserialize)]
pub struct SettingsClientOpenAI {
pub model: String,
pub key: String,
}
#[derive(Debug, Deserialize)]
pub struct Settings {
client_ai_mock: bool,
open_ai: SettingsClientOpenAI,
}
static CONFIG: Lazy<Settings> = Lazy::new(|| {
Settings::new().unwrap()
});
impl Settings {
pub fn global_init(){
let _ = &*CONFIG;
}
fn new() -> Result<Self, config::ConfigError> {
dotenv::from_filename(".dev.env").ok();
if env::var("RUST_LOG").is_err() {
env::set_var("RUST_LOG", "debug");
}
if env::var("NO_COLOR").is_err() {
env::set_var("NO_COLOR", "1");
}
if env::var("APP_CLIENT_AI_MOCK").is_err() {
env::set_var("APP_CLIENT_AI_MOCK", "1");
}
let run_mode = env::var("RUN_MODE").unwrap_or_else(|_| "dev".into());
let conf = Config::builder()
.add_source(config::File::with_name(&format!("config/{}",run_mode)).required(true))
// Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
.add_source(Environment::with_prefix("app").list_separator("_"))
.build()?;
conf.try_deserialize()
}
pub fn get_settings_ai_client<'a>() -> &'a SettingsClientOpenAI{
&CONFIG.open_ai
}
pub fn is_mock_client_ai() -> bool{
CONFIG.client_ai_mock
}
}
|
|
|
File Settings.toml:
debug = false
priority = 32
key = "189rjfadoisfj8923fjio"
File simple.rs:
extern crate config;
use std::collections::HashMap;
use std::collections::BTreeMap;
fn main() {
// Запуск с переменной среды APP_DEBUG переопределит вант файла Settings.toml
// APP_DEBUG=1 cargo run --bin=simple
let mut settings:config::Config = config::Config::default();
settings
// Add in `./Settings.toml`
.merge(config::File::with_name("Settings")).unwrap()
// Add in settings from the environment (with a prefix of APP) Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
.merge(config::Environment::with_prefix("APP")).unwrap();
// println!("{:?}",settings.try_into::>().unwrap());
println!("{:?}",settings.try_into::>().unwrap());
}
Запуск:
$ cargo run --bin=simple
OUTPUT: {"debug": "false", "key": "189rjfadoisfj8923fjio", "priority": "32"}
APP_DEBUG=1 cargo run --bin=simple
OUTPUT: {"debug": "1", "key": "189rjfadoisfj8923fjio", "priority": "32"}
|
|
|
- new
- merge
- refresh
- set_default
- set
- get
- get_str
- get_int
- get_float
- get_bool
- get_table
- get_array
- try_into
- try_from
- deserialize
|
|
Можно преобразовывать в свои тип конфига
config.get::<Vec<Addr>>("db.redis.addrs")
|
|
|
|
#[macro_use]
extern crate lazy_static;
extern crate config;
use std::error::Error;
use std::sync::RwLock;
use config::Config;
use std::collections::btree_map::BTreeMap;
// Глобальный конфиг с изначальной загрузкой и последующем изменении на уровня умного счетчика ссылок !!!!
/*lazy_static! {
static ref SETTINGS: RwLock = RwLock::new(Config::default());
}*/
lazy_static! {
static ref SETTINGS: RwLock = {
let mut settings: Config = Config::default();
settings
.merge(config::File::with_name("Settings")).unwrap()
.merge(config::Environment::with_prefix("APP")).unwrap();
RwLock::new(settings)
};
}
// APP_DEBUG=1 APP_KEY=mykey cargo run --bin=global
fn try_main() -> Result<(), Box> {
// Set property
SETTINGS.write()?.set("property", 42)?;
// Get property
println!("property: {}", SETTINGS.read()?.get::("property")?);
println!("debug: {}", SETTINGS.read()?.get::("debug")?);
println!("priority: {}", SETTINGS.read()?.get::("priority")?);
println!("priority: {}", SETTINGS.read()?.get_int("priority")?);
println!("key: {}", SETTINGS.read()?.get::("key")?);
println!("key: {}", SETTINGS.read()?.get_str("key")?);
Ok(())
}
fn main() {
try_main().unwrap()
}
|
|
|
File lib.rs:
pub mod settings{
use config::{Config};
use std::sync::RwLock;
use lazy_static::lazy_static;
lazy_static! {
static ref SETTINGS: RwLock<Config> = {
let mut settings: Config = Config::default();
settings
.merge(config::File::with_name("src/settings/settings.toml")).unwrap()
.merge(config::Environment::with_prefix("APP")).unwrap();
RwLock::new(settings)
};
}
pub fn config() -> Result<String, Box<std::error::Error>>{
let config = format!("host={host} user={user} port={port} password={password} dbname={dbname}",
host=SETTINGS.read()?.get::<String>("host")?,
user=SETTINGS.read()?.get::<String>("user")?,
port=SETTINGS.read()?.get::<i32>("port")?,
password=SETTINGS.read()?.get::<String>("password")?,
dbname=SETTINGS.read()?.get::<String>("dbname")?);
Ok(config)
}
}
File settings/settings.toml:
host="localhost"
user="rust"
port=5432
password='job_queue'
dbname="rust"
Used File main.rs:
use tokio_postgres_example::settings;
#[tokio::main]
async fn main() -> std::result::Result<(), tokio_postgres::Error> {
let config = settings::config().expect("Error parse config");
let (mut client, connection):(Client,Connection<_,_>) =
tokio_postgres::connect(&config, NoTls).await?;
...
}
|
|
try_into() - Попытка десериализации всей конфигурации в запрошенный тип
|
extern crate config;
use std::collections::HashMap;
use std::collections::BTreeMap;
fn main() {
// Запуск с переменной среды APP_DEBUG переопределит вариант файла Settings.toml
// APP_DEBUG=1 cargo run --bin=simple
let mut settings:config::Config = config::Config::default();
settings
.merge(config::File::with_name("Settings")).unwrap()
.merge(config::Environment::with_prefix("APP")).unwrap();
// Print out our settings (as a HashMap)
// println!("{:?}",settings.try_into::>().unwrap());
println!("{:?}",settings.try_into::>().unwrap());
}
|
|
Как создать конфиг с действующего объекта структуры
|
fn main(){
let mut c = Config::default();
//let mut c = Config::new();
//c.set_default("mode.debug", true).unwrap();
let mut c2 = Config::try_from(&my_conf)?; Работатет слияние двух конфигов
c.merge(c2).unwrap();
//let mut c = Config::try_from(&my_conf)?; Создание с состояния структуры и последующий merge не работатет
c.merge(File::new("Settings_test", FileFormat::Toml)).unwrap();
c.merge(File::new("Settings_test2", FileFormat::Toml)).unwrap();
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|