gled-core 2.34.0

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! The files in the storage directory: one JSON file per asset, and the
//! `version` file naming the gled that wrote them.

use super::{
    ERROR, STORAGE_DIR, STORAGE_VERSION_FILE, STORAGE_VERSION_WRITTEN,
    action::StorageAction,
    asset::{Asset, AssetTrait},
    asset_id::AssetId,
    collection::Collection,
    git::Git,
};
use crate::action::UiAction;
use std::{path::PathBuf, str::FromStr, sync::atomic::Ordering::Relaxed};
use uuid::Uuid;

pub(super) fn asset_path(id: Uuid, dir_name: &str) -> PathBuf {
    STORAGE_DIR.join(dir_name).join(format!("{id}.json"))
}

pub(super) fn write_storage_version_file(git: &mut Git) {
    if STORAGE_VERSION_WRITTEN.load(Relaxed) {
        return;
    }

    if let Err(err) = std::fs::write(
        STORAGE_VERSION_FILE.as_path(),
        format!("{}\n", env!("CARGO_PKG_VERSION")),
    ) {
        tracing::error!("Could not write storage version file: {err}");
        ERROR
            .lock()
            .replace(format!("Could not write storage version file: {err}"));
        StorageAction::Stop.enqueue();
        return;
    }

    if let Err(err) = git.add(STORAGE_VERSION_FILE.as_path()) {
        tracing::error!("Could not add storage version file to git: {err}");
        ERROR
            .lock()
            .replace(format!("Could not add storage version file to git: {err}"));
        StorageAction::Stop.enqueue();
        return;
    }

    STORAGE_VERSION_WRITTEN.store(true, Relaxed);
}

/// Reads every asset of a kind from the storage directory.
pub(super) fn load_collection<T: AssetTrait>() -> Collection<T> {
    let path = STORAGE_DIR.join(T::DIR_NAME);
    let Ok(directory) = path.read_dir() else {
        return Collection::default();
    };

    let folders = directory
        .into_iter()
        .filter_map(|entry| {
            let entry = entry
                .map_err(|err| tracing::warn!("Could not read dir entry: {err:?}"))
                .ok()?;
            let file_type = entry
                .file_type()
                .map_err(|err| tracing::warn!("Could not get file type: {err:?}"))
                .ok()?;

            if file_type.is_file() {
                let id = Uuid::from_str(
                    entry
                        .file_name()
                        .into_string()
                        .map_err(|err| {
                            tracing::warn!("Could not convert file name to string: {err:?}")
                        })
                        .ok()?
                        .strip_suffix(".json")?,
                )
                .map_err(|err| tracing::warn!("Could not parse UUID: {err:?}"))
                .ok()?;

                let asset = Asset::<T>::read(
                    std::fs::File::open(entry.path()).ok()?,
                    AssetId::from_uuid(id),
                )
                .map_err(|err| {
                    UiAction::Error(format!(
                        "Could not read/parse asset {}: {err:?}",
                        entry.path().display()
                    ))
                    .enqueue();
                })
                .ok()?;

                return Some((id, asset));
            }

            None
        })
        .collect();

    Collection::from_assets(folders)
}