feat: ability to categorize

This commit is contained in:
Mark
2026-03-02 00:21:17 +01:00
parent 19f51c5732
commit ab054f3007
5 changed files with 165 additions and 114 deletions

View File

@@ -45,6 +45,8 @@ span {
<h2><!-- t: safe sitename --></h2> <h2><!-- t: safe sitename --></h2>
<!-- t: end --> <!-- t: end -->
<!-- t: set ignore
-->
<!-- t: if running --> <!-- t: if running -->
@@ -52,6 +54,8 @@ span {
<ul> <ul>
<!-- t: for running --> <!-- t: for running -->
<!-- t: ifline id ignore -->
<!-- t: else -->
<li> <li>
<!-- t: if info --> <!-- t: if info -->
<a href="/info/<!-- t: raw info -->"> <a href="/info/<!-- t: raw info -->">
@@ -98,6 +102,7 @@ span {
<!-- t: end --> <!-- t: end -->
</ul> </ul>
<!-- t: end -->
<!-- t: end --> <!-- t: end -->
<!-- t: if static --> <!-- t: if static -->
@@ -106,6 +111,7 @@ span {
<ul> <ul>
<!-- t: for static --> <!-- t: for static -->
<!-- t: ifline id ignore -->
<li> <li>
<!-- t: if info --> <!-- t: if info -->
<a href="/info/<!-- t: raw info -->"> <a href="/info/<!-- t: raw info -->">
@@ -152,6 +158,7 @@ span {
<!-- t: end --> <!-- t: end -->
</ul> </ul>
<!-- t: end -->
<!-- t: end --> <!-- t: end -->
<!-- t: if docslink --> <!-- t: if docslink -->

View File

@@ -32,7 +32,7 @@ impl Data {
status_updated: Mutex::new((Instant::now(), false)), status_updated: Mutex::new((Instant::now(), false)),
} }
} }
pub fn status_sync(&self, dbg: bool) -> MutexGuard<Status> { pub fn status_sync(&'_ self, dbg: bool) -> MutexGuard<'_, Status> {
let mut updated = self.status_updated.blocking_lock(); let mut updated = self.status_updated.blocking_lock();
let now = Instant::now(); let now = Instant::now();
if (now - updated.0).as_secs_f32() > 3.0 || (dbg && !updated.1) { if (now - updated.0).as_secs_f32() > 3.0 || (dbg && !updated.1) {
@@ -47,7 +47,7 @@ impl Data {
self.status.blocking_lock() self.status.blocking_lock()
} }
} }
pub async fn status_async(&self, dbg: bool) -> MutexGuard<Status> { pub async fn status_async(&'_ self, dbg: bool) -> MutexGuard<'_, Status> {
let mut updated = self.status_updated.lock().await; let mut updated = self.status_updated.lock().await;
let now = Instant::now(); let now = Instant::now();
if (now - updated.0).as_secs_f32() > 3.0 || (dbg && !updated.1) { if (now - updated.0).as_secs_f32() > 3.0 || (dbg && !updated.1) {

View File

@@ -26,7 +26,7 @@ async fn index_dbg(data: &State<Data>) -> RawHtml<String> {
async fn index_gen(data: &State<Data>, dbg: bool) -> RawHtml<String> { async fn index_gen(data: &State<Data>, dbg: bool) -> RawHtml<String> {
let status = data.status_async(dbg).await; let status = data.status_async(dbg).await;
return RawHtml(data.index.gen(&status, dbg, data.globals.clone())); RawHtml(data.index.gen(&status, dbg, data.globals.clone()))
} }
#[get("/int")] #[get("/int")]

View File

@@ -2,8 +2,8 @@ use std::{collections::BTreeMap, os::unix::fs::PermissionsExt, path::Path, time:
use tokio::time::Instant; use tokio::time::Instant;
const SLASHINFO: &'static str = "/srv/tomatenmhark-slashinfo/"; const SLASHINFO: &str = "/srv/tomatenmhark-slashinfo/";
const REDIRECT: &'static str = "/srv/tomatenmhark-redirect/"; const REDIRECT: &str = "/srv/tomatenmhark-redirect/";
pub struct Status( pub struct Status(
pub BTreeMap<String, (bool, bool, String, Option<String>, Option<Duration>)>, pub BTreeMap<String, (bool, bool, String, Option<String>, Option<Duration>)>,
@@ -40,16 +40,15 @@ impl Status {
); );
let mut rest = BTreeMap::new(); let mut rest = BTreeMap::new();
if let Ok(rd) = std::fs::read_dir(SLASHINFO) { if let Ok(rd) = std::fs::read_dir(SLASHINFO) {
for f in rd { for f in rd.filter_map(Result::ok) {
if let Ok(f) = f {
if let Some(id) = f.file_name().to_str() { if let Some(id) = f.file_name().to_str() {
if !map.contains_key(id) { if !map.contains_key(id) {
let mut p = f.path(); let mut p = f.path();
p.push("desc"); p.push("desc");
if let Ok(desc) = std::fs::read_to_string(&p) { if let Ok(desc) = std::fs::read_to_string(&p) {
let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = Path::new(SLASHINFO).join(id).join("index.html");
let info = info.starts_with(SLASHINFO) let info =
&& info.try_exists().ok() == Some(true); info.starts_with(SLASHINFO) && info.try_exists().ok() == Some(true);
let redirect = Path::new(REDIRECT).join(id); let redirect = Path::new(REDIRECT).join(id);
let redirect = redirect.starts_with(REDIRECT) let redirect = redirect.starts_with(REDIRECT)
&& redirect.try_exists().ok() == Some(true); && redirect.try_exists().ok() == Some(true);
@@ -65,11 +64,7 @@ impl Status {
), ),
); );
} else { } else {
rest.insert( rest.insert(id.to_owned(), (info, redirect, desc.to_owned(), None));
id.to_owned(),
(info, redirect, desc.to_owned(), None),
);
}
} }
} }
} }
@@ -143,20 +138,21 @@ impl Status {
fn query_status_sync(mut func: impl FnMut(&str, bool, bool, &str, Option<Duration>), dbg: bool) { fn query_status_sync(mut func: impl FnMut(&str, bool, bool, &str, Option<Duration>), dbg: bool) {
if let Ok(rd) = std::fs::read_dir("/tmp/") { if let Ok(rd) = std::fs::read_dir("/tmp/") {
for f in rd { for f in rd.filter_map(Result::ok) {
if let Ok(f) = f {
if let Some(name) = f.file_name().to_str() { if let Some(name) = f.file_name().to_str() {
if name.starts_with("tomatenmhark-status-") { if let Some(id) = name
let id = name["tomatenmhark-status-".len()..].trim(); .strip_prefix("tomatenmhark-status-")
if !id.is_empty() { .map(|v| v.trim())
.filter(|v| !v.is_empty())
{
if let Ok(status) = std::fs::read_to_string(f.path()) if let Ok(status) = std::fs::read_to_string(f.path())
.as_ref() .as_ref()
.map(|v| v.trim_end()) .map(|v| v.trim_end())
{ {
if !status.is_empty() { if !status.is_empty() {
let info = Path::new(SLASHINFO).join(id).join("index.html"); let info = Path::new(SLASHINFO).join(id).join("index.html");
let info = info.starts_with(SLASHINFO) let info =
&& info.try_exists().ok() == Some(true); info.starts_with(SLASHINFO) && info.try_exists().ok() == Some(true);
let redirect = Path::new(REDIRECT).join(id); let redirect = Path::new(REDIRECT).join(id);
let redirect = redirect.starts_with(REDIRECT) let redirect = redirect.starts_with(REDIRECT)
&& redirect.try_exists().ok() == Some(true); && redirect.try_exists().ok() == Some(true);
@@ -167,11 +163,8 @@ fn query_status_sync(mut func: impl FnMut(&str, bool, bool, &str, Option<Duratio
} }
} }
} }
}
}
if let Ok(rd) = std::fs::read_dir("/srv/tomatenmhark-dystatus/") { if let Ok(rd) = std::fs::read_dir("/srv/tomatenmhark-dystatus/") {
for f in rd { for f in rd.filter_map(Result::ok) {
if let Ok(f) = f {
if let Some(name) = f.file_name().to_str() { if let Some(name) = f.file_name().to_str() {
if f.metadata() if f.metadata()
.is_ok_and(|meta| meta.is_file() && meta.permissions().mode() & 1 == 1) .is_ok_and(|meta| meta.is_file() && meta.permissions().mode() & 1 == 1)
@@ -199,7 +192,6 @@ fn query_status_sync(mut func: impl FnMut(&str, bool, bool, &str, Option<Duratio
} }
} }
} }
}
async fn query_status_async( async fn query_status_async(
mut func: impl FnMut(&str, bool, bool, &str, Option<Duration>), mut func: impl FnMut(&str, bool, bool, &str, Option<Duration>),
dbg: bool, dbg: bool,
@@ -207,9 +199,11 @@ async fn query_status_async(
if let Ok(mut rd) = tokio::fs::read_dir("/tmp/").await { if let Ok(mut rd) = tokio::fs::read_dir("/tmp/").await {
while let Ok(Some(f)) = rd.next_entry().await { while let Ok(Some(f)) = rd.next_entry().await {
if let Some(name) = f.file_name().to_str() { if let Some(name) = f.file_name().to_str() {
if name.starts_with("tomatenmhark-status-") { if let Some(id) = name
let id = name["tomatenmhark-status-".len()..].trim(); .strip_prefix("tomatenmhark-status-")
if !id.is_empty() { .map(|v| v.trim())
.filter(|v| !v.is_empty())
{
if let Ok(status) = tokio::fs::read_to_string(f.path()) if let Ok(status) = tokio::fs::read_to_string(f.path())
.await .await
.as_ref() .as_ref()
@@ -229,7 +223,6 @@ async fn query_status_async(
} }
} }
} }
}
if let Ok(mut rd) = tokio::fs::read_dir("/srv/tomatenmhark-dystatus/").await { if let Ok(mut rd) = tokio::fs::read_dir("/srv/tomatenmhark-dystatus/").await {
while let Ok(Some(f)) = rd.next_entry().await { while let Ok(Some(f)) = rd.next_entry().await {
if let Some(name) = f.file_name().to_str() { if let Some(name) = f.file_name().to_str() {

View File

@@ -13,6 +13,8 @@ pub enum TemplateType {
Set(usize, String), Set(usize, String),
Concat(usize, usize), Concat(usize, usize),
If(usize, Templates, Option<Templates>), If(usize, Templates, Option<Templates>),
IfEq(usize, usize, Templates, Option<Templates>),
IfLine(usize, usize, Templates, Option<Templates>),
For(Vec<TemplateLoop>, Templates), For(Vec<TemplateLoop>, Templates),
} }
@@ -94,8 +96,8 @@ pub struct TemplateInfo {
last_end_was_else: Option<bool>, last_end_was_else: Option<bool>,
} }
const COMMENT_START: &'static str = "<!-- t: "; const COMMENT_START: &str = "<!-- t: ";
const COMMENT_END: &'static str = "-->"; const COMMENT_END: &str = " -->";
impl Template { impl Template {
pub fn parse( pub fn parse(
mut src: &str, mut src: &str,
@@ -137,13 +139,14 @@ impl Templates {
if let Some(comment_end) = src.find(COMMENT_END) { if let Some(comment_end) = src.find(COMMENT_END) {
// extract comment content // extract comment content
let comment_og = src[..comment_end].trim(); let comment_og = src[..comment_end].trim_start();
*src = &src[comment_end + COMMENT_END.len()..].trim_start_matches('\r'); *src = src[comment_end + COMMENT_END.len()..].trim_start_matches('\r');
*src = src.strip_prefix('\n').unwrap_or(src); *src = src.strip_prefix('\n').unwrap_or(src);
// do template things // do template things
let comment = comment_og.to_lowercase(); let comment = comment_og.to_lowercase();
let mut comment = comment.split(char::is_whitespace); let mut comment = comment.split(char::is_whitespace);
match comment.next().unwrap_or("") { let comment_match = comment.next().unwrap_or("");
match comment_match {
"//" | "#" => {} "//" | "#" => {}
"end" => { "end" => {
break 'parsing; break 'parsing;
@@ -170,7 +173,8 @@ impl Templates {
let var_name = comment let var_name = comment
.next() .next()
.ok_or(TemplateParseError::MissingVariable("set"))?; .ok_or(TemplateParseError::MissingVariable("set"))?;
let value = comment_og[3..].trim_start()[var_name.len()..].trim_start(); let value = &comment_og[3..].trim_start()[var_name.len()..];
let value = value.strip_prefix(char::is_whitespace).unwrap_or(value);
out.push(TemplateType::Set(info.var(var_name), value.to_owned())); out.push(TemplateType::Set(info.var(var_name), value.to_owned()));
} }
"concat" => { "concat" => {
@@ -187,22 +191,55 @@ impl Templates {
), ),
)); ));
} }
"if" => { "if" | "ifeq" | "ifline" => {
let prev_lewe = info.last_end_was_else; let prev_lewe = info.last_end_was_else;
info.last_end_was_else = Some(false); info.last_end_was_else = Some(false);
out.push(TemplateType::If( let (t_if, t_else) = (
info.var(
comment
.next()
.ok_or(TemplateParseError::MissingVariable("if"))?,
),
Templates::parse(src, info)?, Templates::parse(src, info)?,
if info.last_end_was_else.is_some_and(|v| v) { if info.last_end_was_else.is_some_and(|v| v) {
Some(Templates::parse(src, info)?) Some(Templates::parse(src, info)?)
} else { } else {
None None
}, },
)); );
out.push(match comment_match {
"if" => TemplateType::If(
info.var(
comment
.next()
.ok_or(TemplateParseError::MissingVariable("if"))?,
),
t_if,
t_else,
),
"ifeq" => TemplateType::IfEq(
info.var(
comment
.next()
.ok_or(TemplateParseError::MissingVariable("ifeq"))?,
),
info.var(
comment
.next()
.ok_or(TemplateParseError::MissingVariable("ifeq"))?,
),
t_if,
t_else,
),
"ifline" => {
TemplateType::IfLine(
info.var(comment.next().ok_or(
TemplateParseError::MissingVariable("ifline"),
)?),
info.var(comment.next().ok_or(
TemplateParseError::MissingVariable("ifline"),
)?),
t_if,
t_else,
)
}
_ => unreachable!(),
});
info.last_end_was_else = prev_lewe; info.last_end_was_else = prev_lewe;
} }
"for" => { "for" => {
@@ -296,16 +333,16 @@ impl TemplateType {
Self::Concat(v, a) => { Self::Concat(v, a) => {
if *v == *a { if *v == *a {
let a = &mut vars[*v]; let a = &mut vars[*v];
a.reserve(a.len()); let len = a.len();
a.reserve(len);
let mut i = 0; let mut i = 0;
while let Some(ch) = a[i..].chars().next() { while let Some(ch) = a[i..len].chars().next() {
i += ch.len_utf8(); i += ch.len_utf8();
a.push(ch); a.push(ch);
} }
} else { } else {
let a_temp = std::mem::replace(&mut vars[*a], String::new()); let [a, v] = vars.get_disjoint_mut([*a, *v]).unwrap();
vars[*v].push_str(&a_temp); v.push_str(a);
vars[*a] = a_temp;
} }
} }
Self::If(v, template, else_template) => { Self::If(v, template, else_template) => {
@@ -315,6 +352,20 @@ impl TemplateType {
else_template.gen(status, debug, vars, out); else_template.gen(status, debug, vars, out);
} }
} }
Self::IfEq(x, y, template, else_template) => {
if vars[*x] == vars[*y] {
template.gen(status, debug, vars, out);
} else if let Some(else_template) = else_template {
else_template.gen(status, debug, vars, out);
}
}
Self::IfLine(x, y, template, else_template) => {
if vars[*y].lines().any(|line| line == vars[*x]) {
template.gen(status, debug, vars, out);
} else if let Some(else_template) = else_template {
else_template.gen(status, debug, vars, out);
}
}
Self::For(src, template) => { Self::For(src, template) => {
for src in src { for src in src {
match src { match src {