;; SPDX-License-Identifier: EUPL-1.2 ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs (defpackage :ukkoclot/src/strings (:documentation "String-oriented utilities.") (:use :c2cl :iterate) (:import-from :cl-unicode :general-category) (:import-from :serapeum :->) (:import-from :ukkoclot/src/streams :with-format-like-stream) (:export :escape-xml :is-tg-whitespace :is-tg-whitespace-str)) (in-package :ukkoclot/src/strings) ;; These are very inefficient but I don't care until I profile (-> escape-xml (string &optional (or stream boolean)) (or string null)) (defun escape-xml (str &optional out-spec) "Escape special XML characters in the STR." (with-format-like-stream (out out-spec) (iter (for ch in-string str) (case ch (#\< (write-string "<" out)) (#\> (write-string ">" out)) (#\& (write-string "&" out)) (#\" (write-string """ out)) (otherwise (write-char ch out)))))) (-> is-tg-whitespace (character) boolean) (defun is-tg-whitespace (ch) "Checks if CH on its own would be considered whitespace by telegram." (let ((gc (general-category ch))) (or (string= gc "Zs") ; Separator, space (string= gc "Zl") ; Separator, line (string= gc "Zp") ; Separator, paragraph (string= gc "Cc") ; Other, control (= (char-code ch) #x2800)))) ; BRAILLE PATTERN BLANK (-> is-tg-whitespace-str (string) boolean) (defun is-tg-whitespace-str (str) "Checks if message containing just STR would be considered whitespace by telegram." (iter (for ch in-string str) (always (is-tg-whitespace ch))))