vite-static-shared 0.4.0

Shared code for vite-static
Documentation
#![warn(clippy::pedantic)]

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

/// Type of `.vite/manifest.json`.
pub type ViteManifest<S = String> = HashMap<S, ManifestChunk<S>>;

/// Type alias for dynamic allocated [`ManifestChunk`].
pub type DynamicManifestChunk = ManifestChunk<String, Vec<String>, Vec<u8>>;

/// Type alias for static [`ManifestChunk`].
pub type StaticManifestChunk = ManifestChunk<&'static str, &'static [&'static str], &'static [u8]>;

/// Vite manifest chunk.
///
/// Where possible, use [`StaticManifestChunk`] and [`DynamicManifestChunk`] instead.
///
/// [See Vite documentation](https://vite.dev/guide/backend-integration)
///
/// # Generics
///
/// - Generic `S` can be used to change type of strings in struct.
/// - Generic `Vs` can be used to change type of array of strings.
/// - Generic `Vu8` can be used to change type of byte arrays (f.e. `Vec<u8>` or `&[u8]`).
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ManifestChunk<S = String, Vs = Vec<S>, Vu8 = Vec<u8>> {
    /// The key of this chunk in manifest.
    ///
    /// This field is added by `vite-static` and skipped during (de)serialization.
    #[serde(skip)]
    pub key: S,

    /// The contents of manifest chunk.
    ///
    /// This field is added by `vite-static` and skipped during (de)serialization.
    #[serde(skip)]
    pub contents: Vu8,

    /// Hash of this chunk. Derive macro uses [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) as hashing algorithm.
    ///
    /// This field is added by `vite-static` and skipped during (de)serialization.
    #[serde(skip)]
    pub hash: S,

    /// Mime type of this chunk.
    ///
    /// This field is added by `vite-static` and skipped during (de)serialization.
    #[serde(skip)]
    pub mime_type: S,

    /// The input file name of this chunk/asset if known.
    pub src: Option<S>,

    /// The output file name of this chunk/asset.
    pub file: S,

    /// The list of CSS files imported by this chunk.
    #[serde(default)]
    pub css: Vs,

    /// The list of asset files imported by this chunk, excluding CSS files.
    #[serde(default)]
    pub assets: Vs,

    /// Whether this chunk or asset is an entry point
    #[serde(default)]
    pub is_entry: bool,

    /// The name of this chunk/asset if known.
    pub name: Option<S>,

    /// Whether this chunk is a dynamic entry point
    ///
    /// This field is only present in JS chunks.
    #[serde(default)]
    pub is_dynamic_entry: bool,

    /// The list of statically imported chunks by this chunk
    ///
    /// The values are the keys of the manifest. This field is only present in JS chunks.
    #[serde(default)]
    pub imports: Vs,

    /// The list of dynamically imported chunks by this chunk
    ///
    /// The values are the keys of the manifest. This field is only present in JS chunks.
    #[serde(default)]
    pub dynamic_imports: Vs,
}

/// Trait, that helps query chunks in Vite manifest.
///
/// See `Manifest` derive and [`ManifestChunk`].
pub trait Manifest<'a> {
    /// Get array of `(key, output_filename)` pairs.
    ///
    /// Array must be sorted by `key`
    fn manifest_key_output_pairs(&self) -> &'a [(&'a str, &'a str)];

    /// Get [`ManifestChunk`]s as `(output_filename, chunk)` pairs.
    ///
    /// Array must be sorted by `output_filename`
    fn manifest_chunks(&self) -> &'a [(&'a str, &'a StaticManifestChunk)];

    fn boxed(&self) -> Box<dyn Manifest<'a>>;

    /// Get `output_filename` from manifest key.
    fn resolve_output(&self, key: &str) -> Option<&'a str> {
        let pairs = self.manifest_key_output_pairs();

        match pairs.binary_search_by_key(&key, |&(this_key, _)| this_key) {
            Ok(index) => Some(pairs[index].1),
            Err(_) => None,
        }
    }

    /// Get [`ManifestChunk`] from output filename.
    fn chunk(&self, output: &str) -> Option<&'a StaticManifestChunk> {
        let chunks = self.manifest_chunks();

        match chunks.binary_search_by_key(&output, |&(this_output, _)| this_output) {
            Ok(index) => Some(chunks[index].1),
            Err(_) => None,
        }
    }

    /// Get [`ManifestChunk`] from manifest key.
    fn chunk_by_key(&self, output: &str) -> Option<&'a StaticManifestChunk> {
        match self.resolve_output(output) {
            Some(output) => self.chunk(output),
            None => None,
        }
    }
}