use crate::{IconError, IconName, Weight, generated};
use gpui::{AssetSource, Result, SharedString};
use std::borrow::Cow;
#[derive(Clone, Copy, Debug, Default)]
pub struct PhosphorAssets;
impl PhosphorAssets {
pub fn with_fallback<S>(fallback: S) -> PhosphorAssetSource<S>
where
S: AssetSource,
{
PhosphorAssetSource { fallback }
}
fn load_known(path: &str) -> Result<Option<Cow<'static, [u8]>>> {
if let Some(bytes) = generated::asset_bytes(path) {
return Ok(Some(Cow::Borrowed(bytes)));
}
if let Some((weight, name)) = requested_icon(path)
&& IconName::from_name(name).is_some()
&& !weight.is_enabled()
{
return Err(anyhow::anyhow!(IconError::WeightNotEnabled(weight)));
}
Ok(None)
}
fn listed_paths(path: &str) -> Vec<SharedString> {
Weight::ALL
.into_iter()
.filter(|weight| weight.is_enabled())
.flat_map(|weight| {
IconName::ALL
.iter()
.copied()
.filter_map(move |name| name.path(weight).ok())
})
.filter(|asset| path.is_empty() || asset.starts_with(path))
.collect()
}
}
impl AssetSource for PhosphorAssets {
fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
Self::load_known(path)?
.map(Some)
.ok_or_else(|| anyhow::anyhow!("could not find Phosphor asset at path `{path}`"))
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
Ok(Self::listed_paths(path))
}
}
pub struct PhosphorAssetSource<S> {
fallback: S,
}
impl<S> PhosphorAssetSource<S> {
#[must_use]
pub const fn fallback(&self) -> &S {
&self.fallback
}
}
impl<S> AssetSource for PhosphorAssetSource<S>
where
S: AssetSource,
{
fn load(&self, path: &str) -> Result<Option<Cow<'static, [u8]>>> {
match PhosphorAssets::load_known(path)? {
Some(asset) => Ok(Some(asset)),
None => self.fallback.load(path),
}
}
fn list(&self, path: &str) -> Result<Vec<SharedString>> {
let mut assets = self.fallback.list(path)?;
for asset in PhosphorAssets::listed_paths(path) {
if !assets.contains(&asset) {
assets.push(asset);
}
}
Ok(assets)
}
}
fn requested_icon(path: &str) -> Option<(Weight, &str)> {
let path = path.strip_prefix("phosphor/")?;
let (weight, name) = path.split_once('/')?;
let name = name.strip_suffix(".svg")?;
Some((Weight::from_name(weight)?, name))
}