lumina-iced/src/core/service_items.rs

299 lines
8.4 KiB
Rust

use std::ops::Deref;
use crisp::types::{Keyword, Symbol, Value};
use miette::Result;
use tracing::{debug, error};
use crate::Slide;
use super::images::Image;
use super::presentations::Presentation;
use super::songs::{lisp_to_song, Song};
use super::videos::Video;
use super::kinds::ServiceItemKind;
#[derive(Debug, PartialEq, Clone)]
pub struct ServiceItem {
pub id: i32,
pub title: String,
pub database_id: i32,
pub kind: ServiceItemKind,
// pub item: Box<dyn ServiceTrait>,
}
impl ServiceItem {
pub fn title(&self) -> String {
self.title.clone()
}
pub fn to_slides(&self) -> Result<Vec<Slide>> {
match &self.kind {
ServiceItemKind::Song(song) => song.to_slides(),
ServiceItemKind::Video(video) => video.to_slides(),
ServiceItemKind::Image(image) => image.to_slides(),
ServiceItemKind::Presentation(presentation) => {
presentation.to_slides()
}
ServiceItemKind::Content(slide) => {
Ok(vec![slide.clone()])
}
}
}
}
impl Default for ServiceItem {
fn default() -> Self {
Self {
id: 0,
title: String::default(),
database_id: 0,
kind: ServiceItemKind::Content(Slide::default()),
// item: Box::new(Image::default()),
}
}
}
impl From<Value> for ServiceItem {
fn from(value: Value) -> Self {
Self::from(&value)
}
}
impl From<&Value> for ServiceItem {
fn from(value: &Value) -> Self {
match value {
Value::List(list) => match &list[0] {
Value::Symbol(Symbol(s)) if s == "slide" => {
let background_pos = list
.iter()
.position(|v| match v {
Value::Keyword(Keyword(background))
if background == "background" =>
{
true
}
_ => false,
})
.map_or_else(|| 1, |pos| pos + 1);
if let Some(background) = list.get(background_pos)
{
match background {
Value::List(item) => match &item[0] {
Value::Symbol(Symbol(s))
if s == "image" =>
{
Self::from(&Image::from(
background,
))
}
Value::Symbol(Symbol(s))
if s == "video" =>
{
Self::from(&Video::from(
background,
))
}
Value::Symbol(Symbol(s))
if s == "presentation" =>
{
Self::from(&Presentation::from(
background,
))
}
_ => todo!(),
},
_ => {
error!(
"There is no background here: {:?}",
background
);
ServiceItem::default()
}
}
} else {
error!(
"There is no background here: {:?}",
background_pos
);
ServiceItem::default()
}
}
Value::Symbol(Symbol(s)) if s == "song" => {
let song = lisp_to_song(list.clone());
Self::from(&song)
}
_ => todo!(),
},
_ => todo!(),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct ServiceItemModel {
items: Vec<ServiceItem>,
}
impl Deref for ServiceItemModel {
type Target = Vec<ServiceItem>;
fn deref(&self) -> &Self::Target {
&self.items
}
}
// impl Iterator for ServiceItemModel {
// type Item = ServiceItem;
// fn next(&mut self) -> Option<Self::Item> {
// *self.items.iter().next()
// }
// }
impl From<Vec<ServiceItem>> for ServiceItemModel {
fn from(items: Vec<ServiceItem>) -> Self {
Self { items }
}
}
impl From<&Song> for ServiceItem {
fn from(song: &Song) -> Self {
Self {
kind: ServiceItemKind::Song(song.clone()),
database_id: song.id,
title: song.title.clone(),
..Default::default()
}
}
}
impl From<&Video> for ServiceItem {
fn from(video: &Video) -> Self {
Self {
kind: ServiceItemKind::Video(video.clone()),
database_id: video.id,
title: video.title.clone(),
..Default::default()
}
}
}
impl From<&Image> for ServiceItem {
fn from(image: &Image) -> Self {
Self {
kind: ServiceItemKind::Image(image.clone()),
database_id: image.id,
title: image.title.clone(),
..Default::default()
}
}
}
impl From<&Presentation> for ServiceItem {
fn from(presentation: &Presentation) -> Self {
Self {
kind: ServiceItemKind::Presentation(presentation.clone()),
database_id: presentation.id,
title: presentation.title.clone(),
..Default::default()
}
}
}
impl ServiceItemModel {
fn add_item(
&mut self,
item: impl Into<ServiceItem>,
) -> Result<()> {
let service_item: ServiceItem = item.into();
self.items.push(service_item);
Ok(())
}
pub fn to_slides(&self) -> Result<Vec<Slide>> {
Ok(self
.items
.iter()
.filter_map(|item| {
let slides = item.to_slides().ok();
debug!(?slides);
slides
})
.flatten()
.collect::<Vec<Slide>>())
}
}
pub trait ServiceTrait {
fn title(&self) -> String;
fn id(&self) -> i32;
fn to_slides(&self) -> Result<Vec<Slide>>;
fn box_clone(&self) -> Box<dyn ServiceTrait>;
}
impl Clone for Box<dyn ServiceTrait> {
fn clone(&self) -> Self {
self.box_clone()
}
}
impl std::fmt::Debug for Box<dyn ServiceTrait> {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> Result<(), std::fmt::Error> {
write!(f, "{}: {}", self.id(), self.title())
}
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use crate::core::presentations::PresKind;
use super::*;
use pretty_assertions::{assert_eq, assert_ne};
fn test_song() -> Song {
Song {
title: "Death Was Arrested".to_string(),
..Default::default()
}
}
fn test_presentation() -> Presentation {
Presentation {
id: 0,
title: "20240327T133649--12-isaiah-and-jesus__lesson_project_tfc".into(),
path: PathBuf::from(
"~/docs/notes/lessons/20240327T133649--12-isaiah-and-jesus__lesson_project_tfc.html",
),
kind: PresKind::Html,
}
}
#[test]
pub fn test_service_item() {
let song = test_song();
let service_item = ServiceItem::from(&song);
let pres = test_presentation();
let pres_item = ServiceItem::from(&pres);
let mut service_model = ServiceItemModel::default();
match service_model.add_item(&song) {
Ok(_) => {
assert_eq!(
ServiceItemKind::Song(song),
service_model.items[0].kind
);
assert_eq!(
ServiceItemKind::Presentation(pres),
pres_item.kind
);
assert_eq!(service_item, service_model.items[0]);
}
Err(e) => panic!("Problem adding item: {:?}", e),
}
}
}