television_previewers/
previewers.rs1use std::sync::Arc;
2
3use devicons::FileIcon;
4use television_channels::entry::{Entry, PreviewType};
5
6pub mod basic;
7pub mod cache;
8pub mod command;
9pub mod env;
10pub mod files;
11pub mod meta;
12
13pub use basic::BasicPreviewer;
15pub use basic::BasicPreviewerConfig;
16pub use command::CommandPreviewer;
17pub use command::CommandPreviewerConfig;
18pub use env::EnvVarPreviewer;
19pub use env::EnvVarPreviewerConfig;
20pub use files::FilePreviewer;
21pub use files::FilePreviewerConfig;
22use television_utils::cache::RingSet;
23use television_utils::syntax::HighlightedLines;
24
25#[derive(Clone, Debug)]
26pub enum PreviewContent {
27 Empty,
28 FileTooLarge,
29 SyntectHighlightedText(HighlightedLines),
30 Loading,
31 Timeout,
32 NotSupported,
33 PlainText(Vec<String>),
34 PlainTextWrapped(String),
35 AnsiText(String),
36}
37
38impl PreviewContent {
39 pub fn total_lines(&self) -> u16 {
40 match self {
41 PreviewContent::SyntectHighlightedText(hl_lines) => {
42 hl_lines.lines.len().try_into().unwrap_or(u16::MAX)
43 }
44 PreviewContent::PlainText(lines) => {
45 lines.len().try_into().unwrap_or(u16::MAX)
46 }
47 PreviewContent::AnsiText(text) => {
48 text.lines().count().try_into().unwrap_or(u16::MAX)
49 }
50 _ => 0,
51 }
52 }
53}
54
55pub const PREVIEW_NOT_SUPPORTED_MSG: &str =
56 "Preview for this file type is not supported";
57pub const FILE_TOO_LARGE_MSG: &str = "File too large";
58pub const LOADING_MSG: &str = "Loading...";
59pub const TIMEOUT_MSG: &str = "Preview timed out";
60
61#[derive(Clone, Debug)]
67pub struct Preview {
68 pub title: String,
69 pub content: PreviewContent,
70 pub icon: Option<FileIcon>,
71 pub partial_offset: Option<usize>,
74 pub total_lines: u16,
75}
76
77impl Default for Preview {
78 fn default() -> Self {
79 Preview {
80 title: String::new(),
81 content: PreviewContent::Empty,
82 icon: None,
83 partial_offset: None,
84 total_lines: 0,
85 }
86 }
87}
88
89impl Preview {
90 pub fn new(
91 title: String,
92 content: PreviewContent,
93 icon: Option<FileIcon>,
94 partial_offset: Option<usize>,
95 total_lines: u16,
96 ) -> Self {
97 Preview {
98 title,
99 content,
100 icon,
101 partial_offset,
102 total_lines,
103 }
104 }
105
106 pub fn total_lines(&self) -> u16 {
107 match &self.content {
108 PreviewContent::SyntectHighlightedText(hl_lines) => {
109 hl_lines.lines.len().try_into().unwrap_or(u16::MAX)
110 }
111 PreviewContent::PlainText(lines) => {
112 lines.len().try_into().unwrap_or(u16::MAX)
113 }
114 PreviewContent::AnsiText(text) => {
115 text.lines().count().try_into().unwrap_or(u16::MAX)
116 }
117 _ => 0,
118 }
119 }
120}
121
122#[derive(Debug, Default)]
123pub struct Previewer {
124 basic: BasicPreviewer,
125 file: FilePreviewer,
126 env_var: EnvVarPreviewer,
127 command: CommandPreviewer,
128 requests: RingSet<Entry>,
129}
130
131#[derive(Debug, Default)]
132pub struct PreviewerConfig {
133 basic: BasicPreviewerConfig,
134 file: FilePreviewerConfig,
135 env_var: EnvVarPreviewerConfig,
136 command: CommandPreviewerConfig,
137}
138
139impl PreviewerConfig {
140 pub fn basic(mut self, config: BasicPreviewerConfig) -> Self {
141 self.basic = config;
142 self
143 }
144
145 pub fn file(mut self, config: FilePreviewerConfig) -> Self {
146 self.file = config;
147 self
148 }
149
150 pub fn env_var(mut self, config: EnvVarPreviewerConfig) -> Self {
151 self.env_var = config;
152 self
153 }
154}
155
156const REQUEST_STACK_SIZE: usize = 20;
157
158impl Previewer {
159 pub fn new(config: Option<PreviewerConfig>) -> Self {
160 let config = config.unwrap_or_default();
161 Previewer {
162 basic: BasicPreviewer::new(Some(config.basic)),
163 file: FilePreviewer::new(Some(config.file)),
164 env_var: EnvVarPreviewer::new(Some(config.env_var)),
165 command: CommandPreviewer::new(Some(config.command)),
166 requests: RingSet::with_capacity(REQUEST_STACK_SIZE),
167 }
168 }
169
170 fn dispatch_request(&mut self, entry: &Entry) -> Option<Arc<Preview>> {
171 match &entry.preview_type {
172 PreviewType::Basic => Some(self.basic.preview(entry)),
173 PreviewType::EnvVar => Some(self.env_var.preview(entry)),
174 PreviewType::Files => self.file.preview(entry),
175 PreviewType::Command(cmd) => self.command.preview(entry, cmd),
176 PreviewType::None => Some(Arc::new(Preview::default())),
177 }
178 }
179
180 fn cached(&self, entry: &Entry) -> Option<Arc<Preview>> {
181 match &entry.preview_type {
182 PreviewType::Files => self.file.cached(entry),
183 PreviewType::Command(_) => self.command.cached(entry),
184 PreviewType::Basic | PreviewType::EnvVar => None,
185 PreviewType::None => Some(Arc::new(Preview::default())),
186 }
187 }
188
189 pub fn preview(&mut self, entry: &Entry) -> Option<Arc<Preview>> {
190 self.requests.push(entry.clone());
192
193 if let Some(preview) = self.dispatch_request(entry) {
194 return Some(preview);
195 }
196 for request in self.requests.back_to_front() {
198 if let Some(preview) = self.cached(&request) {
199 return Some(preview);
200 }
201 }
202 None
203 }
204
205 pub fn set_config(&mut self, config: PreviewerConfig) {
206 self.basic = BasicPreviewer::new(Some(config.basic));
207 self.file = FilePreviewer::new(Some(config.file));
208 self.env_var = EnvVarPreviewer::new(Some(config.env_var));
209 }
210}