From 959f73ad1972cf775b4584d5b88e26694aabd780 Mon Sep 17 00:00:00 2001 From: 81reap Date: Thu, 13 Aug 2026 01:41:34 -0400 Subject: [PATCH 1/2] fix(functions) :: don't use macro to import and build SqlPageFunctionName --- .git-blame-ignore-revs | 4 ++ .../sqlpage_functions/function_traits.rs | 6 +-- .../database/sqlpage_functions/functions.rs | 46 +++++++++++++++++-- 3 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..10d72a1c --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,4 @@ +# ignore commits from showing up on git diffs. + +# === large formatting commits === +# TODO :: add commit once merged into mainline \ No newline at end of file diff --git a/src/webserver/database/sqlpage_functions/function_traits.rs b/src/webserver/database/sqlpage_functions/function_traits.rs index 7a18a9b9..c1f34c14 100644 --- a/src/webserver/database/sqlpage_functions/function_traits.rs +++ b/src/webserver/database/sqlpage_functions/function_traits.rs @@ -222,13 +222,9 @@ impl<'a, T: IntoCow<'a>> IntoCow<'a> for Option { } } -/// Declares the listed function modules and builds the [`SqlPageFunctionName`] dispatch enum from -/// them. +/// Builds the [`SqlPageFunctionName`] dispatch enum from the listed function modules. macro_rules! sqlpage_functions { ($($func:ident),* $(,)?) => { - $( - mod $func; - )* /// One variant per built-in `sqlpage.*` function. #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/src/webserver/database/sqlpage_functions/functions.rs b/src/webserver/database/sqlpage_functions/functions.rs index 0cb64a5f..4028b01a 100644 --- a/src/webserver/database/sqlpage_functions/functions.rs +++ b/src/webserver/database/sqlpage_functions/functions.rs @@ -1,15 +1,53 @@ //! Built-in `SQLPage` SQL functions. //! //! Every function is a plain `async fn` in its own module under [`functions/`](self). To add one, -//! create `functions/.rs` with an `async fn ` and add it to the -//! [`sqlpage_functions!`](super::function_traits::sqlpage_functions) call below. The macro declares -//! the module and adds it to the dispatch enum. Argument conversion and -//! dispatch are handled generically in [`super::function_traits`]. +//! create `functions/.rs` with an `async fn `, declare the module below and add it to +//! the [`sqlpage_functions!`](super::function_traits::sqlpage_functions) call. Argument conversion +//! and dispatch are handled generically in [`super::function_traits`]. use std::fmt::Write; use super::function_traits::sqlpage_functions; +mod basic_auth_password; +mod basic_auth_username; +mod client_ip; +mod configuration_directory; +mod cookie; +mod current_working_directory; +mod environment_variable; +mod exec; +mod fetch; +mod fetch_with_meta; +mod hash_password; +mod header; +mod headers; +mod hmac; +mod link; +mod oidc_logout_url; +mod path; +mod persist_uploaded_file; +mod protocol; +mod random_string; +mod read_file_as_data_url; +mod read_file_as_text; +mod regex_match; +mod request_body; +mod request_body_base64; +mod request_method; +mod run_sql; +mod send_mail; +mod set_variable; +mod uploaded_file_mime_type; +mod uploaded_file_name; +mod uploaded_file_path; +mod url_encode; +mod user_info; +mod user_info_token; +mod variables; +mod version; +mod web_root; + sqlpage_functions! { basic_auth_password, basic_auth_username, From a732ccac215d7ab5fb01096b7d22b346b0e3aeac Mon Sep 17 00:00:00 2001 From: 81reap Date: Thu, 13 Aug 2026 01:42:09 -0400 Subject: [PATCH 2/2] fix(rust) :: format codebase --- .../sqlpage_functions/functions/cookie.rs | 5 +- .../functions/environment_variable.rs | 4 +- .../sqlpage_functions/functions/fetch.rs | 7 +- .../sqlpage_functions/functions/header.rs | 5 +- .../functions/persist_uploaded_file.rs | 10 +- .../functions/random_string.rs | 1 - .../functions/read_file_as_data_url.rs | 8 +- .../sqlpage_functions/functions/send_mail.rs | 103 ++++++++++-------- .../functions/set_variable.rs | 3 +- .../functions/uploaded_file_mime_type.rs | 5 +- .../sqlpage_functions/functions/version.rs | 1 - 11 files changed, 89 insertions(+), 63 deletions(-) diff --git a/src/webserver/database/sqlpage_functions/functions/cookie.rs b/src/webserver/database/sqlpage_functions/functions/cookie.rs index 5b0a210a..e54741e8 100644 --- a/src/webserver/database/sqlpage_functions/functions/cookie.rs +++ b/src/webserver/database/sqlpage_functions/functions/cookie.rs @@ -2,6 +2,9 @@ use std::borrow::Cow; use crate::webserver::{http_request_info::RequestInfo, single_or_vec::SingleOrVec}; -pub(super) async fn cookie<'a>(request: &'a RequestInfo, name: Cow<'a, str>) -> Option> { +pub(super) async fn cookie<'a>( + request: &'a RequestInfo, + name: Cow<'a, str>, +) -> Option> { request.cookies.get(&*name).map(SingleOrVec::as_json_str) } diff --git a/src/webserver/database/sqlpage_functions/functions/environment_variable.rs b/src/webserver/database/sqlpage_functions/functions/environment_variable.rs index 4a94e911..93fc77d4 100644 --- a/src/webserver/database/sqlpage_functions/functions/environment_variable.rs +++ b/src/webserver/database/sqlpage_functions/functions/environment_variable.rs @@ -3,7 +3,9 @@ use std::borrow::Cow; use anyhow::Context; /// Returns the value of an environment variable. -pub(super) async fn environment_variable(name: Cow<'_, str>) -> anyhow::Result>> { +pub(super) async fn environment_variable( + name: Cow<'_, str>, +) -> anyhow::Result>> { match std::env::var(&*name) { Ok(value) => Ok(Some(Cow::Owned(value))), Err(std::env::VarError::NotPresent) if name.contains(['=', '\0']) => anyhow::bail!( diff --git a/src/webserver/database/sqlpage_functions/functions/fetch.rs b/src/webserver/database/sqlpage_functions/functions/fetch.rs index 74945752..e737afe2 100644 --- a/src/webserver/database/sqlpage_functions/functions/fetch.rs +++ b/src/webserver/database/sqlpage_functions/functions/fetch.rs @@ -6,8 +6,7 @@ use tracing::Instrument; use crate::webserver::{ database::sqlpage_functions::http_fetch_request::HttpFetchRequest, - http_client::make_http_client, - http_request_info::RequestInfo, + http_client::make_http_client, http_request_info::RequestInfo, }; pub(super) fn build_request<'a>( @@ -94,8 +93,8 @@ pub(super) async fn fetch( async { let response_result = send_request(request, &http_request)?.await; - let mut response = response_result - .map_err(|e| anyhow!("Unable to fetch {}: {e}", http_request.url))?; + let mut response = + response_result.map_err(|e| anyhow!("Unable to fetch {}: {e}", http_request.url))?; tracing::Span::current().record( otel::HTTP_RESPONSE_STATUS_CODE, diff --git a/src/webserver/database/sqlpage_functions/functions/header.rs b/src/webserver/database/sqlpage_functions/functions/header.rs index a1be99b9..30b7d70c 100644 --- a/src/webserver/database/sqlpage_functions/functions/header.rs +++ b/src/webserver/database/sqlpage_functions/functions/header.rs @@ -2,7 +2,10 @@ use std::borrow::Cow; use crate::webserver::{http_request_info::RequestInfo, single_or_vec::SingleOrVec}; -pub(super) async fn header<'a>(request: &'a RequestInfo, name: Cow<'a, str>) -> Option> { +pub(super) async fn header<'a>( + request: &'a RequestInfo, + name: Cow<'a, str>, +) -> Option> { let lower_name = name.to_ascii_lowercase(); request .headers diff --git a/src/webserver/database/sqlpage_functions/functions/persist_uploaded_file.rs b/src/webserver/database/sqlpage_functions/functions/persist_uploaded_file.rs index ddc766ab..ac5d8ab1 100644 --- a/src/webserver/database/sqlpage_functions/functions/persist_uploaded_file.rs +++ b/src/webserver/database/sqlpage_functions/functions/persist_uploaded_file.rs @@ -72,7 +72,10 @@ pub(super) async fn persist_uploaded_file<'a>( } #[cfg(unix)] -pub(super) async fn set_file_mode(path: &std::path::Path, mode: Option<&str>) -> anyhow::Result<()> { +pub(super) async fn set_file_mode( + path: &std::path::Path, + mode: Option<&str>, +) -> anyhow::Result<()> { use std::os::unix::fs::PermissionsExt; let mode = if let Some(mode) = mode { u32::from_str_radix(mode, 8) @@ -87,6 +90,9 @@ pub(super) async fn set_file_mode(path: &std::path::Path, mode: Option<&str>) -> } #[cfg(not(unix))] -pub(super) async fn set_file_mode(_path: &std::path::Path, _mode: Option<&str>) -> anyhow::Result<()> { +pub(super) async fn set_file_mode( + _path: &std::path::Path, + _mode: Option<&str>, +) -> anyhow::Result<()> { Ok(()) } diff --git a/src/webserver/database/sqlpage_functions/functions/random_string.rs b/src/webserver/database/sqlpage_functions/functions/random_string.rs index 4446d34e..0a4c0ee4 100644 --- a/src/webserver/database/sqlpage_functions/functions/random_string.rs +++ b/src/webserver/database/sqlpage_functions/functions/random_string.rs @@ -1,4 +1,3 @@ - /// Returns a random string of the specified length. pub(super) async fn random_string(len: usize) -> anyhow::Result { // OsRng can block on Linux, so we run this on a blocking thread. diff --git a/src/webserver/database/sqlpage_functions/functions/read_file_as_data_url.rs b/src/webserver/database/sqlpage_functions/functions/read_file_as_data_url.rs index 5645842c..5a4925ab 100644 --- a/src/webserver/database/sqlpage_functions/functions/read_file_as_data_url.rs +++ b/src/webserver/database/sqlpage_functions/functions/read_file_as_data_url.rs @@ -5,14 +5,16 @@ use anyhow::Context; use crate::{ filesystem::FileAccess, webserver::{ - database::blob_to_data_url::vec_to_data_uri_with_mime, - http_request_info::RequestInfo, + database::blob_to_data_url::vec_to_data_uri_with_mime, http_request_info::RequestInfo, }, }; use super::uploaded_file_mime_type::{mime_from_upload_path, mime_guess_from_filename}; -pub(super) async fn read_file_bytes(request: &RequestInfo, path_str: &str) -> Result, anyhow::Error> { +pub(super) async fn read_file_bytes( + request: &RequestInfo, + path_str: &str, +) -> Result, anyhow::Error> { let path = std::path::Path::new(path_str); // If the path is relative, it's relative to the web root, not the current working directory, // and it can be fetched from the on-database filesystem table diff --git a/src/webserver/database/sqlpage_functions/functions/send_mail.rs b/src/webserver/database/sqlpage_functions/functions/send_mail.rs index 0f4860ca..7f73dfee 100644 --- a/src/webserver/database/sqlpage_functions/functions/send_mail.rs +++ b/src/webserver/database/sqlpage_functions/functions/send_mail.rs @@ -102,10 +102,7 @@ enum SendMailError { InvalidEmailCc { address: Option }, InvalidEmailReplyTo { address: String }, InvalidAttachmentFilename { index: usize }, - InvalidAttachment { - index: usize, - reason: anyhow::Error, - }, + InvalidAttachment { index: usize, reason: anyhow::Error }, SmtpTlsFailed(anyhow::Error), SmtpTimeout(anyhow::Error), SmtpRejected(anyhow::Error), @@ -136,10 +133,11 @@ impl SendMailError { impl fmt::Display for SendMailError { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::InvalidMessage(reason) => write_reason(formatter, "Invalid email message", reason), - Self::SmtpNotConfigured => formatter.write_str( - "sqlpage.send_mail() requires the smtp_host configuration option", - ), + Self::InvalidMessage(reason) => { + write_reason(formatter, "Invalid email message", reason) + } + Self::SmtpNotConfigured => formatter + .write_str("sqlpage.send_mail() requires the smtp_host configuration option"), Self::MissingEmailFrom => formatter.write_str( "Email has no from address; set its from property or configure smtp_from", ), @@ -153,14 +151,22 @@ impl fmt::Display for SendMailError { write_invalid_recipient(formatter, "cc", address.as_deref()) } Self::InvalidEmailReplyTo { address } => { - write!(formatter, "'{address}' is not a valid reply_to email address") + write!( + formatter, + "'{address}' is not a valid reply_to email address" + ) } Self::InvalidAttachmentFilename { index } => { - write!(formatter, "Attachment filename at index {index} must not be empty") - } - Self::InvalidAttachment { index, reason } => { - write_reason(formatter, &format!("Invalid attachment at index {index}"), reason) + write!( + formatter, + "Attachment filename at index {index} must not be empty" + ) } + Self::InvalidAttachment { index, reason } => write_reason( + formatter, + &format!("Invalid attachment at index {index}"), + reason, + ), Self::SmtpTlsFailed(reason) => { write_reason(formatter, "Unable to establish SMTP TLS", reason) } @@ -170,9 +176,11 @@ impl fmt::Display for SendMailError { Self::SmtpRejected(reason) => { write_reason(formatter, "SMTP server rejected the email", reason) } - Self::SmtpConnectionFailed(reason) => { - write_reason(formatter, "Unable to communicate with the SMTP server", reason) - } + Self::SmtpConnectionFailed(reason) => write_reason( + formatter, + "Unable to communicate with the SMTP server", + reason, + ), } } } @@ -185,7 +193,10 @@ fn write_invalid_recipient( address: Option<&str>, ) -> fmt::Result { match address { - Some(address) => write!(formatter, "'{address}' is not a valid {field} email address"), + Some(address) => write!( + formatter, + "'{address}' is not a valid {field} email address" + ), None => write!(formatter, "{field} must contain at least one email address"), } } @@ -219,10 +230,7 @@ fn send_mail_result_json(result: SendMailResult<()>) -> String { Ok(()) => serde_json::json!({ "status": "accepted" }).to_string(), Err(error) => { let message = format!("{error:#}"); - log::warn!( - "sqlpage.send_mail failed with {}: {message}", - error.code() - ); + log::warn!("sqlpage.send_mail failed with {}: {message}", error.code()); serde_json::json!({ "status": "error", "error_code": error.code(), @@ -300,12 +308,13 @@ fn resolve_bodies( let html_body = if let Some(markdown_src) = body_md { Some( - crate::template_helpers::render_markdown_to_html(config, markdown_src) - .map_err(|reason| { + crate::template_helpers::render_markdown_to_html(config, markdown_src).map_err( + |reason| { SendMailError::InvalidMessage(anyhow::anyhow!( "Failed to render body_md as HTML: {reason}" )) - })?, + }, + )?, ) } else { body_html.map(std::string::ToString::to_string) @@ -334,7 +343,8 @@ fn build_email(config: &AppConfig, request: MailRequest<'_>) -> SendMailResult) -> SendMailResult) -> SendMailResult().map_err(|_| { - SendMailError::InvalidEmailReplyTo { - address: reply_to.to_string(), - } - })?; + let parsed_reply_to = + reply_to + .parse::() + .map_err(|_| SendMailError::InvalidEmailReplyTo { + address: reply_to.to_string(), + })?; email = email.reply_to(parsed_reply_to); } if attachments.is_empty() { @@ -412,10 +421,9 @@ fn build_email(config: &AppConfig, request: MailRequest<'_>) -> SendMailResult( diff --git a/src/webserver/database/sqlpage_functions/functions/uploaded_file_mime_type.rs b/src/webserver/database/sqlpage_functions/functions/uploaded_file_mime_type.rs index 2105773e..48ed1d44 100644 --- a/src/webserver/database/sqlpage_functions/functions/uploaded_file_mime_type.rs +++ b/src/webserver/database/sqlpage_functions/functions/uploaded_file_mime_type.rs @@ -4,7 +4,10 @@ use mime_guess::mime; use crate::webserver::http_request_info::RequestInfo; -pub(super) fn mime_from_upload_path<'a>(request: &'a RequestInfo, path: &str) -> Option<&'a mime_guess::Mime> { +pub(super) fn mime_from_upload_path<'a>( + request: &'a RequestInfo, + path: &str, +) -> Option<&'a mime_guess::Mime> { request.uploaded_files.values().find_map(|uploaded_file| { if uploaded_file.file.path() == OsStr::new(path) { uploaded_file.content_type.as_ref() diff --git a/src/webserver/database/sqlpage_functions/functions/version.rs b/src/webserver/database/sqlpage_functions/functions/version.rs index 6d0cde3c..ed0bf170 100644 --- a/src/webserver/database/sqlpage_functions/functions/version.rs +++ b/src/webserver/database/sqlpage_functions/functions/version.rs @@ -1,4 +1,3 @@ - /// Returns the version of the sqlpage that is running. pub(super) async fn version() -> &'static str { env!("CARGO_PKG_VERSION")