use crate::clip::{Clip, ClipId};
use crate::logging::Rating;
use crate::marker::Marker;
use crate::note::Note;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Logger {
pub session_id: String,
pub started_at: DateTime<Utc>,
pub user: Option<String>,
logs: HashMap<ClipId, LogEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntry {
pub logged_at: DateTime<Utc>,
pub rating: Rating,
pub keywords: Vec<String>,
pub markers: Vec<Marker>,
pub notes: Vec<Note>,
pub is_favorite: bool,
pub is_rejected: bool,
}
impl Logger {
#[must_use]
pub fn new(session_id: impl Into<String>) -> Self {
Self {
session_id: session_id.into(),
started_at: Utc::now(),
user: None,
logs: HashMap::new(),
}
}
pub fn set_user(&mut self, user: impl Into<String>) {
self.user = Some(user.into());
}
#[allow(clippy::too_many_arguments)]
pub fn log_clip(
&mut self,
clip_id: ClipId,
rating: Rating,
keywords: Vec<String>,
markers: Vec<Marker>,
notes: Vec<Note>,
is_favorite: bool,
is_rejected: bool,
) {
let entry = LogEntry {
logged_at: Utc::now(),
rating,
keywords,
markers,
notes,
is_favorite,
is_rejected,
};
self.logs.insert(clip_id, entry);
}
pub fn add_rating(&mut self, clip_id: ClipId, rating: Rating) {
self.logs
.entry(clip_id)
.or_insert_with(|| LogEntry {
logged_at: Utc::now(),
rating: Rating::Unrated,
keywords: Vec::new(),
markers: Vec::new(),
notes: Vec::new(),
is_favorite: false,
is_rejected: false,
})
.rating = rating;
}
pub fn add_keyword(&mut self, clip_id: ClipId, keyword: impl Into<String>) {
let entry = self.logs.entry(clip_id).or_insert_with(|| LogEntry {
logged_at: Utc::now(),
rating: Rating::Unrated,
keywords: Vec::new(),
markers: Vec::new(),
notes: Vec::new(),
is_favorite: false,
is_rejected: false,
});
let keyword = keyword.into();
if !entry.keywords.contains(&keyword) {
entry.keywords.push(keyword);
}
}
pub fn add_marker(&mut self, clip_id: ClipId, marker: Marker) {
self.logs
.entry(clip_id)
.or_insert_with(|| LogEntry {
logged_at: Utc::now(),
rating: Rating::Unrated,
keywords: Vec::new(),
markers: Vec::new(),
notes: Vec::new(),
is_favorite: false,
is_rejected: false,
})
.markers
.push(marker);
}
#[must_use]
pub fn get_log(&self, clip_id: &ClipId) -> Option<&LogEntry> {
self.logs.get(clip_id)
}
#[must_use]
pub fn logged_clips(&self) -> Vec<ClipId> {
self.logs.keys().copied().collect()
}
#[must_use]
pub fn count(&self) -> usize {
self.logs.len()
}
pub fn apply_to_clips(&self, clips: &mut HashMap<ClipId, Clip>) {
for (clip_id, entry) in &self.logs {
if let Some(clip) = clips.get_mut(clip_id) {
clip.set_rating(entry.rating);
clip.set_favorite(entry.is_favorite);
clip.set_rejected(entry.is_rejected);
for keyword in &entry.keywords {
clip.add_keyword(keyword.clone());
}
for marker in &entry.markers {
clip.add_marker(marker.clone());
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logger_creation() {
let logger = Logger::new("session-001");
assert_eq!(logger.session_id, "session-001");
assert_eq!(logger.count(), 0);
}
#[test]
fn test_log_clip() {
let mut logger = Logger::new("session-001");
let clip_id = ClipId::new();
logger.add_rating(clip_id, Rating::FourStars);
logger.add_keyword(clip_id, "interview");
logger.add_keyword(clip_id, "john-doe");
let entry = logger.get_log(&clip_id).expect("get_log should succeed");
assert_eq!(entry.rating, Rating::FourStars);
assert_eq!(entry.keywords.len(), 2);
assert_eq!(logger.count(), 1);
}
}