Annotation For src/utils.rs
Logged in as anonymous

Lines of src/utils.rs from check-in 512369f93e that are changed by the sequence of edits moving toward check-in 0acb6536ae:

                         1: //! Utility functions and types for the application.
                         2: 
                         3: use crate::Cursor;
                         4: 
                         5: use std::{
                         6: 	borrow::Cow,
                         7: 	sync::LazyLock,
                         8: };
                         9: 
                        10: use html_escape::encode_text;
                        11: use regex::{
                        12: 	Regex,
                        13: 	RegexBuilder,
                        14: };
                        15: use stacked_errors::{
                        16: 	bail,
                        17: 	Result,
                        18: };
                        19: 
                        20: pub static RE_DOMAIN: LazyLock<Regex> = LazyLock::new(|| {
                        21: 	Regex::new(r"^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$").expect("Invalid domain regex")
                        22: });
                        23: pub static RE_CLOSING: LazyLock<Regex> = LazyLock::new(|| {
                        24: 	RegexBuilder::new(r"</[ \t]*(pre|code)[ \t]*>")
                        25: 		.case_insensitive(true).build().expect("Invalid closing tag regex")
                        26: });
                        27: 
                        28: /// Stores binary attachment data and metadata for Telegram messages.
                        29: /// The data is wrapped in a `Cursor<Vec<u8>>` for efficient streaming,
                        30: /// while `name` holds the filename or display name of the attachment.
                        31: #[derive(Debug)]
                        32: pub struct Attachment {
                        33: 	pub data: Cursor<Vec<u8>>,
                        34: 	pub name: String,
                        35: }
                        36: 
                        37: /// Validates text to ensure it doesn't break Telegram's preformatted blocks.
                        38: ///
                        39: /// Escapes HTML special characters to prevent injection.
                        40: ///
                        41: /// # Arguments
                        42: /// * `text` - Text to validate and escape.
                        43: ///
                        44: /// # Returns
                        45: /// * `Result<Cow<'a, str>>` - Escaped text or error if invalid.
                        46: ///
                        47: /// # Errors
                        48: /// Returns an error if the text contains Telegram closing tags (`</pre>`, `</code>`).
                        49: pub fn validate <'a>(text: &'a str) -> Result<Cow<'a, str>> {
                        50: 	if RE_CLOSING.is_match(text) {
512369f93e 2026-08-01   51: 		bail!("Telegram closing tag found.");
                        52: 	} else {
                        53: 		Ok(encode_text(text))
512369f93e 2026-08-01   54: 	}
512369f93e 2026-08-01   55: }