Библиотека serde_yaml: работа с YAML в Rust
YAML (YAML Ain't Markup Language) — один из самых популярных форматов сериализации данных, широко используемый для конфигурационных файлов, описания структур данных и обмена информацией между сервисами. В экосистеме Rust для работы с YAML существует зрелая и производительная библиотека serde_yaml, построенная на базе мощного фреймворка сериализации serde. В этой статье мы подробно разберём, как парсить, создавать и обрабатывать YAML-документы на Rust.
Установка и подключение
Для начала работы добавьте следующие зависимости в ваш Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
Библиотека serde_yaml автоматически подтянет все необходимые компоненты для парсинга и генерации YAML.
Базовая десериализация (YAML → структура Rust)
Самый распространённый сценарий — чтение конфигурационного файла. Определим структуру, которая соответствует YAML-документу:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
server: ServerConfig,
database: DatabaseConfig,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
workers: Option<u32>,
}
#[derive(Debug, Deserialize)]
struct DatabaseConfig {
url: String,
pool_size: u32,
}
Теперь прочитаем YAML-файл и преобразуем его в структуру:
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let yaml_content = fs::read_to_string("config.yaml")?;
let config: Config = serde_yaml::from_str(&yaml_content)?;
println!("Сервер: {}:{}", config.server.host, config.server.port);
println!("База данных: {}", config.database.url);
Ok(())
}
Пример файла config.yaml:
server:
host: "127.0.0.1"
port: 8080
workers: 4
database:
url: "postgres://user:pass@localhost/db"
pool_size: 10
Сериализация (структура Rust → YAML)
Обратный процесс — создание YAML-документа из данных в памяти:
use serde::Serialize;
#[derive(Debug, Serialize)]
struct Project {
name: String,
version: String,
dependencies: Vec<Dependency>,
}
#[derive(Debug, Serialize)]
struct Dependency {
name: String,
version: String,
optional: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let project = Project {
name: "my-app".to_string(),
version: "0.1.0".to_string(),
dependencies: vec![
Dependency {
name: "serde".to_string(),
version: "1.0".to_string(),
optional: false,
},
Dependency {
name: "tokio".to_string(),
version: "1.35".to_string(),
optional: true,
},
],
};
let yaml_string = serde_yaml::to_string(&project)?;
println!("{}", yaml_string);
// Сохраняем в файл
std::fs::write("project.yaml", &yaml_string)?;
Ok(())
}
Работа с произвольными YAML-документами
Иногда структура YAML заранее неизвестна. В таких случаях используйте serde_yaml::Value:
use serde_yaml::Value;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let yaml_data = "
apiVersion: v1
kind: Service
metadata:
name: my-service
labels:
app: web
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: web
";
let value: Value = serde_yaml::from_str(yaml_data)?;
// Доступ к вложенным данным
if let Some(ports) = value["spec"]["ports"].as_sequence() {
for port in ports {
if let Some(port_num) = port["port"].as_u64() {
println!("Порт: {}", port_num);
}
}
}
// Изменение значения
if let Some(metadata) = value.get_mut("metadata") {
if let Some(labels) = metadata.get_mut("labels") {
labels.as_mapping_mut()
.unwrap()
.insert(Value::String("env".into()), Value::String("production".into()));
}
}
println!("{}", serde_yaml::to_string(&value)?);
Ok(())
}
Обработка ошибок и лучшие практики
- Типизированные ошибки: используйте
serde_yaml::Errorдля точного определения проблемы при парсин