Skip to content

Commit 9db7ec2

Browse files
committed
wip!
1 parent eeaabd4 commit 9db7ec2

File tree

8 files changed

+249
-220
lines changed

8 files changed

+249
-220
lines changed

src/env.rs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ use std::path::PathBuf;
77
use std::sync::LazyLock;
88
use std::time::Duration;
99

10-
pub struct Metadata<'a> {
11-
pub title: String,
12-
pub version: &'a str,
13-
pub highlight: &'a highlight::Data,
14-
}
15-
1610
pub const DEFAULT_HTTP_TIMEOUT: Duration = Duration::from_secs(5);
1711

1812
const VAR_ADDRESS_PORT: &str = "WASTEBIN_ADDRESS_PORT";
@@ -44,6 +38,8 @@ pub enum Error {
4438
HttpTimeout(ParseIntError),
4539
#[error("failed to parse {VAR_MAX_PASTE_EXPIRATION}: {0}")]
4640
MaxPasteExpiration(ParseIntError),
41+
#[error("unknown theme {0}")]
42+
UnknownTheme(String),
4743
}
4844

4945
pub struct BasePath(String);
@@ -65,18 +61,6 @@ impl Default for BasePath {
6561
}
6662
}
6763

68-
pub static METADATA: LazyLock<Metadata> = LazyLock::new(|| {
69-
let title = std::env::var("WASTEBIN_TITLE").unwrap_or_else(|_| "wastebin".to_string());
70-
let version = env!("CARGO_PKG_VERSION");
71-
let highlight = &highlight::DATA;
72-
73-
Metadata {
74-
title,
75-
version,
76-
highlight,
77-
}
78-
});
79-
8064
// NOTE: This relies on `VAR_BASE_URL` but repeats parsing to handle errors.
8165
pub static BASE_PATH: LazyLock<BasePath> = LazyLock::new(|| {
8266
std::env::var(VAR_BASE_URL).map_or_else(
@@ -107,24 +91,25 @@ pub static BASE_PATH: LazyLock<BasePath> = LazyLock::new(|| {
10791
)
10892
});
10993

110-
pub static THEME: LazyLock<highlight::Theme> = LazyLock::new(|| {
94+
pub fn title() -> String {
95+
std::env::var("WASTEBIN_TITLE").unwrap_or_else(|_| "wastebin".to_string())
96+
}
97+
98+
pub fn theme() -> Result<highlight::Theme, Error> {
11199
std::env::var(VAR_THEME).map_or_else(
112-
|_| highlight::Theme::Ayu,
100+
|_| Ok(highlight::Theme::Ayu),
113101
|var| match var.as_str() {
114-
"ayu" => highlight::Theme::Ayu,
115-
"base16ocean" => highlight::Theme::Base16Ocean,
116-
"coldark" => highlight::Theme::Coldark,
117-
"gruvbox" => highlight::Theme::Gruvbox,
118-
"monokai" => highlight::Theme::Monokai,
119-
"onehalf" => highlight::Theme::Onehalf,
120-
"solarized" => highlight::Theme::Solarized,
121-
_ => {
122-
tracing::error!("unrecognized theme {var}");
123-
highlight::Theme::Ayu
124-
}
102+
"ayu" => Ok(highlight::Theme::Ayu),
103+
"base16ocean" => Ok(highlight::Theme::Base16Ocean),
104+
"coldark" => Ok(highlight::Theme::Coldark),
105+
"gruvbox" => Ok(highlight::Theme::Gruvbox),
106+
"monokai" => Ok(highlight::Theme::Monokai),
107+
"onehalf" => Ok(highlight::Theme::Onehalf),
108+
"solarized" => Ok(highlight::Theme::Solarized),
109+
_ => Err(Error::UnknownTheme(var)),
125110
},
126111
)
127-
});
112+
}
128113

129114
pub fn cache_size() -> Result<NonZeroUsize, Error> {
130115
std::env::var(VAR_CACHE_SIZE)

src/highlight.rs

Lines changed: 76 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::db::read::Entry;
22
use crate::errors::Error;
3-
use sha2::{Digest, Sha256};
43
use std::cmp::Ordering;
5-
use std::sync::LazyLock;
64
use syntect::html::{line_tokens_to_classed_spans, ClassStyle};
75
use syntect::parsing::{ParseState, ScopeStack, SyntaxReference, SyntaxSet};
86
use syntect::util::LinesWithEndings;
@@ -23,108 +21,98 @@ pub enum Theme {
2321
#[derive(Clone)]
2422
pub struct Html(String);
2523

26-
pub static DATA: LazyLock<Data> = LazyLock::new(|| {
27-
let syntax_set = two_face::syntax::extra_newlines();
28-
let mut syntaxes = syntax_set.syntaxes().to_vec();
29-
syntaxes.sort_by(|a, b| {
30-
a.name
31-
.to_lowercase()
32-
.partial_cmp(&b.name.to_lowercase())
33-
.unwrap_or(Ordering::Less)
34-
});
35-
36-
Data {
37-
syntax_set,
38-
syntaxes,
39-
}
40-
});
41-
42-
/// Combines content with a filename containing the hash of the content.
43-
pub struct Hashed<'a> {
44-
pub name: String,
45-
pub content: &'a str,
46-
}
47-
48-
pub struct Data {
49-
pub syntax_set: SyntaxSet,
24+
#[derive(Clone)]
25+
pub struct Highlighter {
26+
syntax_set: SyntaxSet,
5027
pub syntaxes: Vec<SyntaxReference>,
5128
}
5229

53-
impl<'a> Hashed<'a> {
54-
fn new(name: &str, ext: &str, content: &'a str) -> Self {
55-
let name = format!(
56-
"{name}.{}.{ext}",
57-
hex::encode(Sha256::digest(content.as_bytes()))
58-
.get(0..16)
59-
.expect("at least 16 characters")
60-
);
61-
62-
Self { name, content }
63-
}
64-
}
65-
66-
fn highlight(source: &str, ext: &str) -> Result<String, Error> {
67-
let syntax_ref = DATA
68-
.syntax_set
69-
.find_syntax_by_extension(ext)
70-
.unwrap_or_else(|| {
71-
DATA.syntax_set
72-
.find_syntax_by_extension("txt")
73-
.expect("finding txt syntax")
30+
impl Default for Highlighter {
31+
fn default() -> Self {
32+
let syntax_set = two_face::syntax::extra_newlines();
33+
let mut syntaxes = syntax_set.syntaxes().to_vec();
34+
syntaxes.sort_by(|a, b| {
35+
a.name
36+
.to_lowercase()
37+
.partial_cmp(&b.name.to_lowercase())
38+
.unwrap_or(Ordering::Less)
7439
});
7540

76-
let mut parse_state = ParseState::new(syntax_ref);
77-
let mut html = String::from("<table><tbody>");
78-
let mut scope_stack = ScopeStack::new();
79-
80-
for (mut line_number, line) in LinesWithEndings::from(source).enumerate() {
81-
let (formatted, delta) = if line.len() > HIGHLIGHT_LINE_LENGTH_CUTOFF {
82-
(line.to_string(), 0)
83-
} else {
84-
let parsed = parse_state.parse_line(line, &DATA.syntax_set)?;
85-
line_tokens_to_classed_spans(
86-
line,
87-
parsed.as_slice(),
88-
ClassStyle::Spaced,
89-
&mut scope_stack,
90-
)?
91-
};
92-
93-
line_number += 1;
94-
let line_number = format!(
95-
r#"<tr><td class="line-number" id="L{line_number}"><a href=#L{line_number}>{line_number:>4}</a></td>"#
96-
);
97-
html.push_str(&line_number);
98-
html.push_str(r#"<td class="line">"#);
99-
100-
if delta < 0 {
101-
html.push_str(&"<span>".repeat(delta.abs().try_into()?));
41+
Self {
42+
syntax_set,
43+
syntaxes,
10244
}
45+
}
46+
}
10347

104-
// Strip stray newlines that cause vertically stretched lines.
105-
for c in formatted.chars().filter(|c| *c != '\n') {
106-
html.push(c);
48+
impl Highlighter {
49+
fn highlight_inner(&self, source: &str, ext: &str) -> Result<String, Error> {
50+
let syntax_ref = self
51+
.syntax_set
52+
.find_syntax_by_extension(ext)
53+
.unwrap_or_else(|| {
54+
self.syntax_set
55+
.find_syntax_by_extension("txt")
56+
.expect("finding txt syntax")
57+
});
58+
59+
let mut parse_state = ParseState::new(syntax_ref);
60+
let mut html = String::from("<table><tbody>");
61+
let mut scope_stack = ScopeStack::new();
62+
63+
for (mut line_number, line) in LinesWithEndings::from(source).enumerate() {
64+
let (formatted, delta) = if line.len() > HIGHLIGHT_LINE_LENGTH_CUTOFF {
65+
(line.to_string(), 0)
66+
} else {
67+
let parsed = parse_state.parse_line(line, &self.syntax_set)?;
68+
line_tokens_to_classed_spans(
69+
line,
70+
parsed.as_slice(),
71+
ClassStyle::Spaced,
72+
&mut scope_stack,
73+
)?
74+
};
75+
76+
line_number += 1;
77+
let line_number = format!(
78+
r#"<tr><td class="line-number" id="L{line_number}"><a href=#L{line_number}>{line_number:>4}</a></td>"#
79+
);
80+
html.push_str(&line_number);
81+
html.push_str(r#"<td class="line">"#);
82+
83+
if delta < 0 {
84+
html.push_str(&"<span>".repeat(delta.abs().try_into()?));
85+
}
86+
87+
// Strip stray newlines that cause vertically stretched lines.
88+
for c in formatted.chars().filter(|c| *c != '\n') {
89+
html.push(c);
90+
}
91+
92+
if delta > 0 {
93+
html.push_str(&"</span>".repeat(delta.try_into()?));
94+
}
95+
96+
html.push_str("</td></tr>");
10797
}
10898

109-
if delta > 0 {
110-
html.push_str(&"</span>".repeat(delta.try_into()?));
111-
}
99+
html.push_str("</tbody></table>");
112100

113-
html.push_str("</td></tr>");
101+
Ok(html)
114102
}
115103

116-
html.push_str("</tbody></table>");
117-
118-
Ok(html)
119-
}
104+
/// Highlight `entry` with the given file extension.
105+
pub async fn highlight(&self, entry: Entry, ext: String) -> Result<Html, Error> {
106+
let highlighter = self.clone();
120107

121-
impl Html {
122-
pub async fn from(entry: Entry, ext: String) -> Result<Self, Error> {
123-
Ok(Self(
124-
tokio::task::spawn_blocking(move || highlight(&entry.text, &ext)).await??,
108+
Ok(Html(
109+
tokio::task::spawn_blocking(move || highlighter.highlight_inner(&entry.text, &ext))
110+
.await??,
125111
))
126112
}
113+
}
127114

115+
impl Html {
128116
pub fn into_inner(self) -> String {
129117
self.0
130118
}

0 commit comments

Comments
 (0)