fix: scan ahead panic

This commit is contained in:
trisua 2025-07-24 15:10:59 -04:00
parent 9fe5735127
commit 2cc9ed7445
3 changed files with 127 additions and 13 deletions

View file

@ -323,7 +323,7 @@ hr.margin {
}
span.img_sizer {
display: block;
display: inline-block;
}
p,

View file

@ -27,13 +27,29 @@ pub fn render_markdown(input: &str) -> String {
.replace("</span>:temp_style", "</style>")
}
pub(crate) fn is_numeric(value: &str) -> bool {
let mut is_numeric = false;
for char in value.chars() {
is_numeric = char.is_numeric();
}
is_numeric
}
pub(crate) fn slice(x: &str, range: core::ops::RangeFrom<usize>) -> String {
(&x.chars().collect::<Vec<char>>()[range])
.iter()
.collect::<String>()
}
fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) {
let mut in_color_buffer = false;
let mut in_main_buffer = false;
let mut color_buffer = String::new();
let mut close_1 = false;
for char in line.chars() {
for (i, char) in line.chars().enumerate() {
if close_1 && char != '%' {
// we expected to see another percentage to close the main buffer,
// not getting that means this wasn't meant to be a color
@ -60,7 +76,7 @@ fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) {
// by this point, we have: !
// %color_buffer%main_buffer%%
output.push_str(&format!(
"<span style=\"color: {color_buffer}\">{buffer}</span>\n"
"<span style=\"color: {color_buffer}\">{buffer}</span>"
));
color_buffer.clear();
@ -73,6 +89,13 @@ fn parse_text_color_line(output: &mut String, buffer: &mut String, line: &str) {
}
// start
// scan ahead
let ahead = slice(line, i..);
if !ahead.contains("%%") {
// no closing sequence, we're done
continue;
}
// flush buffer
output.push_str(&buffer);
buffer.clear();
@ -336,7 +359,17 @@ fn parse_image_size_line(output: &mut String, buffer: &mut String, line: &str) {
if in_size && in_size_rhs {
// end
output.push_str(&format!(
"<span style=\"width: {size_lhs}; height: {size_rhs}\" class=\"img_sizer\">![{buffer}</span>"
"<span style=\"width: {}; height: {}\" class=\"img_sizer\">![{buffer}</span>",
if is_numeric(&size_lhs) {
format!("{size_lhs}px")
} else {
size_lhs
},
if is_numeric(&size_rhs) {
format!("{size_rhs}px")
} else {
size_rhs
}
));
size_lhs = String::new();
@ -487,8 +520,7 @@ fn parse_link_line(output: &mut String, buffer: &mut String, line: &str) {
buffer.clear();
// scan for closing, otherwise quit
let haystack = &line[i..];
let haystack = slice(line, i..);
if !haystack.contains("]") {
output.push('[');
continue;

View file

@ -1,3 +1,4 @@
use crate::markdown::is_numeric;
use serde::{Deserialize, Serialize};
use serde_valid::Validate;
use std::fmt::Display;
@ -259,12 +260,64 @@ pub struct EntryMetadata {
#[serde(default, alias = "CONTAINER_SHADOW")]
#[validate(max_length = 32)]
pub container_shadow: String,
/// If the container is a flexbox.
#[serde(default, alias = "CONTAINER_FLEX")]
pub container_flex: bool,
/// The direction of the container's content (if container has flex enabled).
#[serde(default, alias = "CONTAINER_FLEX_DIRECTION")]
#[validate(max_length = 16)]
pub container_flex_direction: String,
/// The gap of the container's content (if container has flex enabled).
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/gap>
#[serde(default, alias = "CONTAINER_FLEX_GAP")]
#[validate(max_length = 16)]
pub container_flex_gap: String,
/// If the container's content wraps (if container has flex enabled).
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap>
#[serde(default, alias = "CONTAINER_FLEX_WRAP")]
pub container_flex_wrap: bool,
/// The alignment of the container's content horizontally (if container has flex enabled).
///
/// Swapped to vertical when direction is `column*`.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/justify-content>
#[serde(default, alias = "CONTAINER_FLEX_ALIGN_HORIZONTAL")]
#[validate(max_length = 16)]
pub container_flex_align_horizontal: String,
/// The alignment of the container's content vertically (if container has flex enabled).
///
/// Swapped to horizontal when direction is `column*`.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/align-items>
#[serde(default, alias = "CONTAINER_FLEX_ALIGN_VERTICAL")]
#[validate(max_length = 16)]
pub container_flex_align_vertical: String,
/// The shadow under text.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow>
#[serde(default, alias = "CONTENT_TEXT_SHADOW")]
#[validate(max_length = 32)]
pub content_text_shadow: String,
/// The shadow under text.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow>
#[serde(default, alias = "CONTENT_TEXT_SHADOW_COLOR")]
#[validate(max_length = 32)]
pub content_text_shadow_color: String,
/// The shadow under text.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow>
#[serde(default, alias = "CONTENT_TEXT_SHADOW_OFFSET")]
#[validate(max_length = 32)]
pub content_text_shadow_offset: String,
/// The shadow under text.
///
/// Syntax: <https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow>
#[serde(default, alias = "CONTENT_TEXT_SHADOW_BLUR")]
#[validate(max_length = 32)]
pub content_text_shadow_blur: String,
/// The name of a font from Google Fonts to use.
#[serde(default, alias = "CONTENT_FONT")]
#[validate(max_length = 32)]
@ -287,6 +340,9 @@ pub struct EntryMetadata {
/// The color of links.
#[serde(default, alias = "CONTENT_TEXT_LINK_COLOR")]
pub content_text_link_color: String,
/// If paragraph elements have a margin below them.
#[serde(default, alias = "CONTENT_DISABLE_PARAGRAPH_MARGIN")]
pub content_disable_paragraph_margin: bool,
}
macro_rules! metadata_css {
@ -446,6 +502,31 @@ impl EntryMetadata {
));
}
if !self.content_text_shadow_color.is_empty() {
output.push_str(&format!(
".container {{ text-shadow: {} {} {}; }}",
self.content_text_shadow_offset,
self.content_text_shadow_blur,
self.content_text_shadow_color
));
}
if self.container_flex {
output.push_str(".container { display: flex; }");
metadata_css!(".container", "gap", self.container_flex_gap->output);
metadata_css!(".container", "flex-direction", self.container_flex_direction->output);
metadata_css!(".container", "align-items", self.container_flex_align_vertical->output);
metadata_css!(".container", "justify-content", self.container_flex_align_horizontal->output);
if self.container_flex_wrap {
output.push_str(".container { flex-wrap: wrap; }");
}
}
if self.content_disable_paragraph_margin {
output.push_str(".container p { margin: 0; }");
}
output + "</style>"
}
@ -482,13 +563,14 @@ impl EntryMetadata {
value = value.trim().to_string();
// determine if we need to stringify
let mut is_numeric = false;
for char in value.chars() {
is_numeric = char.is_numeric();
}
if !is_numeric && !value.starts_with("[") && !value.starts_with("\"") {
let is_numeric = is_numeric(&value);
if !is_numeric
&& !value.starts_with("[")
&& !value.starts_with("\"")
&& value != "true"
&& value != "false"
|| value.starts_with("#")
{
value = format!("\"{value}\"");
}