1use std::sync::Arc;
6
7use rich::measure::Measurement;
8use rich::{Console, ConsoleOptions, Renderable, Segment, Text};
9use rich_plugin_api::{Plugin, PluginError, PluginMetadata, PluginRegistrar};
10
11use crate::error::MicroError;
12use crate::markup::{Diagnostic, MicroTransform, expand};
13use crate::model::MicroAsset;
14use crate::registry::MicroRegistry;
15use crate::render::{FallbackPreference, placeholder};
16use crate::rows::reserve_rows;
17
18pub trait MicroExt {
33 fn append_micro(
35 &mut self,
36 registry: &MicroRegistry,
37 name: &str,
38 ) -> Result<&mut Self, MicroError>;
39
40 fn append_micro_asset(&mut self, asset: &MicroAsset) -> &mut Self;
42
43 fn expand_micro(&mut self, registry: &MicroRegistry) -> Vec<Diagnostic>;
46}
47
48impl MicroExt for Text {
49 fn append_micro(
50 &mut self,
51 registry: &MicroRegistry,
52 name: &str,
53 ) -> Result<&mut Self, MicroError> {
54 let asset = registry.require(name)?;
55 Ok(self.append_micro_asset(asset))
56 }
57
58 fn append_micro_asset(&mut self, asset: &MicroAsset) -> &mut Self {
59 let appended =
60 std::mem::take(self).append_text(&placeholder(asset, FallbackPreference::Emoji));
61 *self = appended;
62 self
63 }
64
65 fn expand_micro(&mut self, registry: &MicroRegistry) -> Vec<Diagnostic> {
66 let (text, diagnostics) = expand(self, registry, FallbackPreference::Emoji);
67 *self = text;
68 diagnostics
69 }
70}
71
72#[derive(Clone, Debug)]
89pub struct MicroAssetRef {
90 asset: Arc<MicroAsset>,
91 preference: FallbackPreference,
92}
93
94impl MicroAssetRef {
95 pub fn new(asset: impl Into<Arc<MicroAsset>>) -> Self {
96 MicroAssetRef {
97 asset: asset.into(),
98 preference: FallbackPreference::default(),
99 }
100 }
101
102 pub fn from_registry(registry: &MicroRegistry, name: &str) -> Result<Self, MicroError> {
104 Ok(MicroAssetRef::new(Arc::clone(registry.require(name)?)))
105 }
106
107 #[must_use]
108 pub fn preference(mut self, preference: FallbackPreference) -> Self {
109 self.preference = preference;
110 self
111 }
112
113 pub fn asset(&self) -> &MicroAsset {
114 &self.asset
115 }
116
117 pub fn text(&self) -> Text {
119 placeholder(&self.asset, self.preference)
120 }
121}
122
123impl Renderable for MicroAssetRef {
124 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
125 reserve_rows(self.text().rich_render(console, options))
126 }
127
128 fn measure(&self, _console: &Console, options: &ConsoleOptions) -> Measurement {
129 let cols = self.asset.cols();
130 Measurement::new(cols, cols).clamp(None, Some(options.max_width))
131 }
132
133 fn fit_to_measurement(&self) -> bool {
134 true
135 }
136}
137
138#[derive(Clone, Debug)]
141pub struct MicroPlugin {
142 registry: Arc<MicroRegistry>,
143}
144
145impl MicroPlugin {
146 pub fn new(registry: Arc<MicroRegistry>) -> Self {
147 MicroPlugin { registry }
148 }
149}
150
151impl Plugin for MicroPlugin {
152 fn metadata(&self) -> PluginMetadata {
153 PluginMetadata::new("micro", "Micro assets", env!("CARGO_PKG_VERSION"))
154 .description("Replaces :micro:name: tokens with inline micro assets")
155 }
156
157 fn register(&self, registrar: &mut dyn PluginRegistrar) -> Result<(), PluginError> {
158 registrar.transform(
159 "micro",
160 Arc::new(MicroTransform::new(Arc::clone(&self.registry))),
161 );
162 Ok(())
163 }
164}