use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use super::layout;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SessionConfig {
pub slug: String,
pub topic: String,
pub preset: String,
pub created_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_sources: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub closed_at: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_slug: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
}
impl SessionConfig {
pub fn new(
slug: impl Into<String>,
topic: impl Into<String>,
preset: impl Into<String>,
) -> Self {
Self {
slug: slug.into(),
topic: topic.into(),
preset: preset.into(),
created_at: Utc::now(),
max_sources: None,
closed_at: None,
parent_slug: None,
tags: Vec::new(),
}
}
pub fn is_closed(&self) -> bool {
self.closed_at.is_some()
}
}
pub fn read(slug: &str) -> std::io::Result<SessionConfig> {
let path = layout::session_toml(slug);
let text = fs::read_to_string(&path)?;
toml::from_str(&text).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
}
pub fn write(slug: &str, cfg: &SessionConfig) -> std::io::Result<()> {
let path = layout::session_toml(slug);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let text = toml::to_string_pretty(cfg)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
fs::write(&path, text)
}
pub fn exists(slug: &str) -> bool {
Path::new(&layout::session_toml(slug)).exists()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn toml_round_trip_in_memory() {
let cfg = SessionConfig::new("foo", "topic one", "tech");
let text = toml::to_string_pretty(&cfg).unwrap();
let back: SessionConfig = toml::from_str(&text).unwrap();
assert_eq!(back.slug, "foo");
assert_eq!(back.topic, "topic one");
assert_eq!(back.preset, "tech");
assert!(!back.is_closed());
}
}