use std::{iter::FusedIterator, ops::Deref, str::FromStr};
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Manifest {
artifacts: &'static [Artifact<[u8]>],
buckets: &'static [Bucket],
}
impl std::fmt::Debug for Manifest {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Manifest").field(&self.artifacts).finish()
}
}
impl Manifest {
#[inline]
pub const fn get(&self, id: Id) -> Option<Artifact> {
let bucket_index = id.bucket_index(self.buckets.len());
match self.buckets[bucket_index].find(id) {
Some(index) => Some(self.artifacts[index]),
None => None,
}
}
#[inline]
pub const fn artifacts(&self) -> &'static [Artifact] {
self.artifacts
}
}
impl IntoIterator for Manifest {
type Item = Artifact;
type IntoIter = Iter<[u8]>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
Iter {
artifacts: self.artifacts(),
index: 0,
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Manifest {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
<[_] as serde::Serialize>::serialize(self.artifacts(), serializer)
}
}
#[derive(Debug, Clone)]
pub struct Iter<T>
where
T: ?Sized + 'static,
{
artifacts: &'static [Artifact<T>],
index: usize,
}
impl<T> Iterator for Iter<T>
where
T: ?Sized + 'static,
{
type Item = Artifact<T>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.artifacts.len() {
let artifact = self.artifacts[self.index];
self.index += 1;
Some(artifact)
} else {
None
}
}
}
impl<T> FusedIterator for Iter<T> where T: ?Sized + 'static {}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Artifact<T = [u8]>
where
T: ?Sized + 'static,
{
mime: &'static str,
id: Id,
value: &'static T,
}
impl<T> Clone for Artifact<T>
where
T: ?Sized,
{
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Artifact<T> where T: ?Sized {}
impl<T> Artifact<T>
where
T: ?Sized,
{
#[inline]
pub const fn id(&self) -> Id {
self.id
}
#[inline]
pub const fn mime(&self) -> &'static str {
self.mime
}
}
impl Artifact<[u8]> {
#[inline]
pub const fn as_bytes(&self) -> &'static [u8] {
self.value
}
#[doc(hidden)]
#[inline]
pub const fn __into_bytes_artifact(self) -> Artifact<[u8]> {
self
}
}
impl Artifact<str> {
#[inline]
pub const fn as_bytes(&self) -> &'static [u8] {
self.value.as_bytes()
}
#[inline]
pub const fn as_str(&self) -> &'static str {
self.value
}
#[inline]
pub const fn into_bytes_artifact(self) -> Artifact<[u8]> {
Artifact {
id: self.id,
mime: self.mime,
value: self.value.as_bytes(),
}
}
#[doc(hidden)]
#[inline]
pub const fn __into_bytes_artifact(self) -> Artifact<[u8]> {
self.into_bytes_artifact()
}
}
impl AsRef<[u8]> for Artifact<[u8]> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<[u8]> for Artifact<str> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<str> for Artifact<str> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Deref for Artifact<[u8]> {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl Deref for Artifact<str> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[derive(Default, Clone, Copy, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
#[allow(clippy::derived_hash_with_manual_eq)]
pub struct Id([u8; 28]);
impl std::fmt::Debug for Id {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Id")
.field(&const_hex::Buffer::<_, false>::new().const_format(self.as_bytes()))
.finish()
}
}
impl std::fmt::Display for Id {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(
const_hex::Buffer::<_, false>::new()
.const_format(self.as_bytes())
.as_str(),
)
}
}
impl Id {
pub const MIN: Self = Self([u8::MIN; _]);
pub const MAX: Self = Self([u8::MAX; _]);
#[inline]
pub const fn eq(&self, other: &Self) -> bool {
let mut x = true;
let mut i = 0;
while i < self.0.len() {
x = x && self.0[i] == other.0[i];
i += 1;
}
x
}
#[inline]
pub const fn as_bytes(&self) -> &[u8; 28] {
&self.0
}
#[inline]
pub const fn to_bytes(self) -> [u8; 28] {
self.0
}
#[inline]
pub const fn from_bytes(x: [u8; 28]) -> Self {
Self(x)
}
#[inline]
const fn bucket_index(&self, n: usize) -> usize {
usize::from_ne_bytes(*self.0.first_chunk().unwrap()) & !(usize::MAX << n.ilog2())
}
}
impl PartialEq for Id {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.eq(other)
}
}
impl AsRef<[u8]> for Id {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Deref for Id {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl FromStr for Id {
type Err = const_hex::FromHexError;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
const_hex::decode_to_array(s).map(Self::from_bytes)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Id {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(
const_hex::Buffer::<_, false>::new()
.const_format(self.as_bytes())
.as_str(),
)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Id {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
<&str as serde::Deserialize>::deserialize(deserializer)
.and_then(|x| Self::from_str(x).map_err(<D::Error as serde::de::Error>::custom))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc(hidden)]
#[repr(transparent)]
pub struct Bucket(&'static [Entry]);
impl Bucket {
const fn find(&self, id: Id) -> Option<usize> {
let mut i = 0;
while i < self.0.len() {
if self.0[i].id.eq(&id) {
return Some(self.0[i].index as usize);
}
i += 1;
}
None
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[doc(hidden)]
pub struct Entry {
id: Id,
index: u32,
}
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
#[macro_export]
macro_rules! collect {
(@impl $($x:ident)::+ ::*) => {
&$($x)::+ ::__ARTIFACTS
};
(@impl $($x:ident)::+) => {
&[($($x)::+).__into_bytes_artifact()]
};
($($($tt:tt)::+),* $(,)?) => {
#[allow(unused)]
#[doc(hidden)]
pub const __ARTIFACTS: &'static [$crate::__macro::Artifact]
= $crate::__macro::concat_slices!([$crate::__macro::Artifact]: $($crate::collect!(@impl $($tt)::+)),*);
};
}
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
#[macro_export]
macro_rules! manifest {
($($tt:tt)*) => {{
$crate::collect!($($tt)*);
const MANIFEST: $crate::__macro::Manifest = {
const BUCKET_LEN: usize = 1 << __ARTIFACTS.len().ilog2() as usize;
const ENTRIES_LEN: usize = {
let mut entries_len = [0usize; BUCKET_LEN];
let mut entries_max = 0;
let mut i = 0;
while i < __ARTIFACTS.len() {
let x = &mut entries_len
[$crate::__macro::bucket_index(&__ARTIFACTS[i].id(), BUCKET_LEN)];
*x += 1;
if *x > entries_max {
entries_max = *x;
}
i += 1;
}
entries_max
};
const BUCKETS_RAW: [([$crate::__macro::Entry; ENTRIES_LEN], usize); BUCKET_LEN] = {
let mut buckets =
[([$crate::__macro::entry($crate::__macro::Id::MIN, 0); ENTRIES_LEN], 0usize); BUCKET_LEN];
let mut i = 0;
while i < __ARTIFACTS.len() {
let x =
&mut buckets[$crate::__macro::bucket_index(&__ARTIFACTS[i].id(), BUCKET_LEN)];
let k = x.1;
x.0[k] = $crate::__macro::entry(__ARTIFACTS[i].id(), i as u32);
x.1 += 1;
i += 1;
}
buckets
};
const BUCKETS: [$crate::__macro::Bucket; BUCKET_LEN] = {
let mut buckets = [$crate::__macro::bucket(&[]); BUCKET_LEN];
let mut i = 0;
while i < BUCKET_LEN {
buckets[i] = $crate::__macro::bucket(BUCKETS_RAW[i].0.split_at(BUCKETS_RAW[i].1).0);
i += 1;
}
buckets
};
$crate::__macro::manifest(__ARTIFACTS, &BUCKETS)
};
MANIFEST
}};
}
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mbed_core::include_str;
#[cfg(feature = "macros")]
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
pub use mbed_core::include_bytes;
#[cfg(feature = "css")]
#[cfg_attr(docsrs, doc(cfg(feature = "css")))]
pub mod css {
#[cfg(feature = "tailwindcss")]
#[cfg_attr(docsrs, doc(cfg(feature = "tailwindcss")))]
pub use mbed_css::tailwindcss;
}
#[cfg(feature = "image")]
#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
pub mod image {
use std::ops::Deref;
use crate::Artifact;
pub use mbed_image::include;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Image {
format: Format,
width: u32,
height: u32,
bytes: &'static [u8],
}
impl Artifact<Image> {
#[inline]
pub const fn format(&self) -> Format {
self.value.format
}
#[inline]
pub const fn width(&self) -> u32 {
self.value.width
}
#[inline]
pub const fn height(&self) -> u32 {
self.value.height
}
#[inline]
pub const fn as_bytes(&self) -> &'static [u8] {
self.value.bytes
}
#[doc(hidden)]
#[inline]
pub const fn __into_bytes_artifact(self) -> Artifact<[u8]> {
Artifact {
mime: self.mime(),
id: self.id(),
value: self.as_bytes(),
}
}
}
impl AsRef<[u8]> for Artifact<Image> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Deref for Artifact<Image> {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Format {
Png,
Jpeg,
Gif,
WebP,
Pnm,
Tiff,
Tga,
Bmp,
Ico,
Hdr,
OpenExr,
Farbfeld,
#[cfg(any(feature = "image-avif", doc, docsrs))]
#[cfg_attr(docsrs, doc(cfg(feature = "image-avif")))]
Avif,
Qoi,
}
#[cfg(feature = "macros")]
#[doc(hidden)]
pub mod __macro {
pub use super::{Format, Image};
#[inline]
pub const fn image(format: Format, width: u32, height: u32, bytes: &'static [u8]) -> Image {
Image {
format,
width,
height,
bytes,
}
}
}
}
#[cfg(feature = "js")]
#[cfg_attr(docsrs, doc(cfg(feature = "js")))]
pub mod js {
use std::ops::Deref;
use crate::Artifact;
#[cfg(feature = "bundle")]
#[cfg_attr(docsrs, doc(cfg(feature = "bundle")))]
pub use mbed_js::bundle;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Bundle {
code: &'static str,
sourcemap: &'static str,
}
impl Artifact<Bundle> {
#[inline]
pub const fn as_bytes(&self) -> &'static [u8] {
self.as_str().as_bytes()
}
#[inline]
pub const fn as_str(&self) -> &'static str {
self.code()
}
#[inline]
pub const fn code(&self) -> &'static str {
self.value.code
}
#[inline]
pub const fn sourcemap(&self) -> &'static str {
self.value.sourcemap
}
#[doc(hidden)]
#[inline]
pub const fn __into_bytes_artifact(self) -> Artifact<[u8]> {
Artifact {
mime: self.mime(),
id: self.id(),
value: self.code().as_bytes(),
}
}
}
impl AsRef<[u8]> for Artifact<Bundle> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<str> for Artifact<Bundle> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Deref for Artifact<Bundle> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
#[cfg(feature = "macros")]
#[doc(hidden)]
pub mod __macro {
pub use super::Bundle;
#[inline]
pub const fn bundle(code: &'static str, sourcemap: &'static str) -> Bundle {
Bundle { code, sourcemap }
}
}
}
#[cfg(feature = "macros")]
#[doc(hidden)]
pub mod __macro {
pub use crate::{Artifact, Bucket, Entry, Id, Manifest};
pub use constcat::concat_slices;
#[inline]
pub const fn manifest(
artifacts: &'static [Artifact<[u8]>],
buckets: &'static [Bucket],
) -> Manifest {
Manifest { artifacts, buckets }
}
#[inline]
pub const fn artifact<T>(mime: &'static str, id: Id, value: &'static T) -> Artifact<T>
where
T: ?Sized + 'static,
{
Artifact { mime, id, value }
}
#[inline]
pub const fn bucket(x: &'static [Entry]) -> Bucket {
Bucket(x)
}
#[inline]
pub const fn entry(id: Id, index: u32) -> Entry {
Entry { id, index }
}
#[inline]
pub const fn bucket_index(x: &Id, n: usize) -> usize {
x.bucket_index(n)
}
}