summaryrefslogtreecommitdiff
path: root/lib/render/escape.mli
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 07:47:30 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 07:47:30 +0200
commiteff4b89cff4e1b8fcceb23c54cb62ec636ce62fe (patch)
tree9ec7b1f1dac45249cf8434097623b7db9f0e47c8 /lib/render/escape.mli
parent897c274fd28402159ca6d45eedc1257b1ce98696 (diff)
downloadcolitur-eff4b89cff4e1b8fcceb23c54cb62ec636ce62fe.tar.gz
colitur-eff4b89cff4e1b8fcceb23c54cb62ec636ce62fe.zip
feat(render): per-flavour escaping and RFC 5545 line folding
Six flavours: latex, groff, html, xml, ics, none. Markdown, AsciiDoc and plain text map to none deliberately -- their metacharacters are context-dependent and escaping them aggressively produces worse output than not escaping. An unrecognised extension returns None rather than falling back to none: guessing the flavour wrong produces malformed output that looks fine until it does not. Folding backs off to a non-continuation byte, so a fold never splits a UTF-8 sequence -- the failure mode that would corrupt Polish and Latin names in a published feed.
Diffstat (limited to 'lib/render/escape.mli')
-rw-r--r--lib/render/escape.mli28
1 files changed, 28 insertions, 0 deletions
diff --git a/lib/render/escape.mli b/lib/render/escape.mli
new file mode 100644
index 0000000..97cf050
--- /dev/null
+++ b/lib/render/escape.mli
@@ -0,0 +1,28 @@
+(** Per-flavour escaping for the template engine, plus RFC 5545 line folding.
+
+ Knows nothing about calendars. Pure and total: every function is defined on
+ every string, and none reads the clock, the environment or the filesystem. *)
+
+(** The six escaping modes. Markdown, AsciiDoc and plain text all use [None_]:
+ their metacharacter sets are context-dependent, and escaping them
+ aggressively produces worse output than not escaping at all (spec section 5).
+ This is a documented limitation of those flavours. *)
+type flavour = Latex | Groff | Html | Xml | Ics | None_
+
+val all : flavour list
+val to_string : flavour -> string
+val of_string : string -> flavour option
+
+(** [of_extension ".tex"] is [Some Latex]. Returns [None] for an unrecognised
+ extension: the caller must treat that as an error, never as a fallback to
+ [None_] (spec section 5 -- guessing wrong here produces malformed output
+ that looks fine until it does not). *)
+val of_extension : string -> flavour option
+
+(** Escape one interpolated value for [flavour]. *)
+val apply : flavour -> string -> string
+
+(** Fold one unfolded content line per RFC 5545 section 3.1: at most 75 octets
+ per line, continuation lines prefixed with one space, CRLF terminators,
+ never splitting a UTF-8 sequence. The returned string ends with CRLF. *)
+val fold_ics : string -> string