use super::capture::{Pixels, decode_png, design_from_text};
use indicatrix_cut_core::Design;
use indicatrix_vault::{
db::sqlite::Database,
model::{
design_key::normalize_design_uuid,
design_variant::{NewVariant, VariantSummary},
},
};
use std::fmt;
pub(super) struct SaveRequest<'a> {
pub(super) uuid: &'a str,
pub(super) name: &'a str,
pub(super) note: Option<&'a str>,
pub(super) design_text: &'a str,
pub(super) picture_png: Option<&'a [u8]>,
pub(super) parent: Option<i64>,
pub(super) now: i64,
}
fn plain(error: &anyhow::Error) -> String {
format!("The library could not be used. {error:#}")
}
pub(super) fn list(db: &Database, uuid: &str) -> Result<Vec<VariantSummary>, String> {
db.list_variants(uuid).map_err(|error| plain(&error))
}
pub(super) type VariantWithPng = (VariantSummary, Option<Vec<u8>>);
pub(super) fn list_with_png(db: &Database, uuid: &str) -> Result<Vec<VariantWithPng>, String> {
list(db, uuid)?
.into_iter()
.map(|summary| {
let png = db
.variant_thumbnail(summary.variant_id)
.map_err(|error| plain(&error))?;
Ok((summary, png))
})
.collect()
}
pub(super) fn decode_pictures(
variants: Vec<VariantWithPng>,
) -> Vec<(VariantSummary, Option<Pixels>)> {
variants
.into_iter()
.map(|(summary, png)| {
let picture = png.as_deref().and_then(decode_png);
(summary, picture)
})
.collect()
}
pub(super) fn save(db: &Database, request: &SaveRequest<'_>) -> Result<i64, String> {
let parent = match request.parent {
Some(id) => list(db, request.uuid)?
.iter()
.any(|variant| variant.variant_id == id)
.then_some(id),
None => None,
};
db.save_variant(
request.uuid,
&NewVariant {
name: request.name,
parent_variant_id: parent,
note: request.note,
design_text: request.design_text,
thumbnail_png: request.picture_png,
created_at: request.now,
},
)
.map_err(|error| plain(&error))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum LoadProblem {
Missing,
OtherDesign,
Unreadable(String),
}
impl fmt::Display for LoadProblem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Missing => f.write_str("That variant no longer exists."),
Self::OtherDesign => f.write_str("That variant belongs to another design."),
Self::Unreadable(reason) => f.write_str(reason),
}
}
}
pub(super) fn load_design(
db: &Database,
uuid: &str,
id: i64,
) -> Result<(VariantSummary, Design), LoadProblem> {
let loaded = db
.load_variant(id)
.map_err(|error| LoadProblem::Unreadable(plain(&error)))?
.ok_or(LoadProblem::Missing)?;
if normalize_design_uuid(uuid).as_deref() != Some(loaded.summary.design_uuid.as_str()) {
return Err(LoadProblem::OtherDesign);
}
let design = design_from_text(&loaded.design_text).map_err(LoadProblem::Unreadable)?;
Ok((loaded.summary, design))
}
pub(super) fn rename(db: &Database, id: i64, name: &str) -> Result<(), String> {
match db.rename_variant(id, name) {
Ok(0) => Err(LoadProblem::Missing.to_string()),
Ok(_) => Ok(()),
Err(error) => Err(plain(&error)),
}
}
pub(super) fn set_note(db: &Database, id: i64, note: Option<&str>) -> Result<(), String> {
match db.set_variant_note(id, note) {
Ok(0) => Err(LoadProblem::Missing.to_string()),
Ok(_) => Ok(()),
Err(error) => Err(plain(&error)),
}
}
pub(super) fn delete(db: &Database, id: i64) -> Result<(), String> {
db.delete_variant(id)
.map(|_| ())
.map_err(|error| plain(&error))
}