use std::sync::Arc;
use rich::measure::Measurement;
use rich::{Console, ConsoleOptions, Renderable, Segment, Text};
use rich_plugin_api::{Plugin, PluginError, PluginMetadata, PluginRegistrar};
use crate::error::MicroError;
use crate::markup::{Diagnostic, MicroTransform, expand};
use crate::model::MicroAsset;
use crate::registry::MicroRegistry;
use crate::render::{FallbackPreference, placeholder};
use crate::rows::reserve_rows;
pub trait MicroExt {
fn append_micro(
&mut self,
registry: &MicroRegistry,
name: &str,
) -> Result<&mut Self, MicroError>;
fn append_micro_asset(&mut self, asset: &MicroAsset) -> &mut Self;
fn expand_micro(&mut self, registry: &MicroRegistry) -> Vec<Diagnostic>;
}
impl MicroExt for Text {
fn append_micro(
&mut self,
registry: &MicroRegistry,
name: &str,
) -> Result<&mut Self, MicroError> {
let asset = registry.require(name)?;
Ok(self.append_micro_asset(asset))
}
fn append_micro_asset(&mut self, asset: &MicroAsset) -> &mut Self {
let appended =
std::mem::take(self).append_text(&placeholder(asset, FallbackPreference::Emoji));
*self = appended;
self
}
fn expand_micro(&mut self, registry: &MicroRegistry) -> Vec<Diagnostic> {
let (text, diagnostics) = expand(self, registry, FallbackPreference::Emoji);
*self = text;
diagnostics
}
}
#[derive(Clone, Debug)]
pub struct MicroAssetRef {
asset: Arc<MicroAsset>,
preference: FallbackPreference,
}
impl MicroAssetRef {
pub fn new(asset: impl Into<Arc<MicroAsset>>) -> Self {
MicroAssetRef {
asset: asset.into(),
preference: FallbackPreference::default(),
}
}
pub fn from_registry(registry: &MicroRegistry, name: &str) -> Result<Self, MicroError> {
Ok(MicroAssetRef::new(Arc::clone(registry.require(name)?)))
}
#[must_use]
pub fn preference(mut self, preference: FallbackPreference) -> Self {
self.preference = preference;
self
}
pub fn asset(&self) -> &MicroAsset {
&self.asset
}
pub fn text(&self) -> Text {
placeholder(&self.asset, self.preference)
}
}
impl Renderable for MicroAssetRef {
fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
reserve_rows(self.text().rich_render(console, options))
}
fn measure(&self, _console: &Console, options: &ConsoleOptions) -> Measurement {
let cols = self.asset.cols();
Measurement::new(cols, cols).clamp(None, Some(options.max_width))
}
fn fit_to_measurement(&self) -> bool {
true
}
}
#[derive(Clone, Debug)]
pub struct MicroPlugin {
registry: Arc<MicroRegistry>,
}
impl MicroPlugin {
pub fn new(registry: Arc<MicroRegistry>) -> Self {
MicroPlugin { registry }
}
}
impl Plugin for MicroPlugin {
fn metadata(&self) -> PluginMetadata {
PluginMetadata::new("micro", "Micro assets", env!("CARGO_PKG_VERSION"))
.description("Replaces :micro:name: tokens with inline micro assets")
}
fn register(&self, registrar: &mut dyn PluginRegistrar) -> Result<(), PluginError> {
registrar.transform(
"micro",
Arc::new(MicroTransform::new(Arc::clone(&self.registry))),
);
Ok(())
}
}