defmodule Excalt.Icalendar.Helper do
def split_by(ical_text, splitter) do
ical_text = unfold(ical_text)
{:ok, regex} = Regex.compile("(BEGIN|END)#{splitter}", [:multiline])
end
def extract_tag(ical_text, tag) do
{:ok, regex} = Regex.compile("^#{tag}[;]?(.*):(.*)$", [:multiline])
ical_text = unfold(ical_text)
[_, properties, values] = Regex.run(regex, ical_text)
{tag, String.trim(properties), String.trim(values)}
end
def change_value(ical_text, tag, new_value) do
{tag, properties, values} = extract_tag(ical_text, tag)
{:ok, regex} = Regex.compile("^#{tag}[;]?.*:.*$", [:multiline])
[{start_idx, str_len}] = Regex.run(regex, ical_text, return: :index)
ics_before = String.slice(ical_text, 0, start_idx)
ics_after = String.slice(ical_text, start_idx + str_len, String.length(ical_text))
new_line =
if String.length(properties) > 0 do
"#{tag};#{properties}:#{new_value}"
else
"#{tag}:#{new_value}"
end
new_line = fold(new_line)
ics_before <> "#{new_line}" <> ics_after
end
def unfold(ical_text) do
ical_text
|> String.replace(~r/\r?\n[ \t]/, "")
end
@doc """
https://www.rfc-editor.org/rfc/rfc5545#section-3.1:
Lines of text SHOULD NOT be longer than 75 octets, excluding the line
break. Long content lines SHOULD be split into a multiple line
representations using a line "folding" technique. That is, a long
line can be split between any two characters by inserting a CRLF
immediately followed by a single linear white-space character (i.e.,
SPACE or HTAB). Any sequence of CRLF followed immediately by a
single linear white-space character is ignored (i.e., removed) when
processing the content type.
The fold function splits a string across graphemes if the byte-size of the substring will
exceed the max_size. It then adds a CRLF and an empty space at the split-point.
"""
def fold(line, max_size \\ 75) do
# current_element = List.last(list)
# if byte_size(current_element + char) > max_size do
# end
# String.split("abc", "", parts: 2)
line
|> String.split("")
|> Enum.reduce([""], fn char, acc ->
[current_element | rest] = acc
if byte_size(current_element <> char) > max_size do
[" " <> char] ++ [current_element <> "\r\n"] ++ rest
else
[current_element <> char | rest]
end
end)
|> Enum.reverse()
|> Enum.join()
end
def create_vevent(
summary,
dtstart,
dtend,
location \\ "",
description \\ "",
method \\ "PUBLISH",
class \\ "PUBLIC"
) do
ical = """
BEGIN:VCALENDAR
VERSION:2.0
PRODID:Excalt
METHOD:#{method}
BEGIN:VEVENT
UID:#{UUID.uuid1()}
LOCATION:#{location}
SUMMARY:#{summary}
DESCRIPTION:#{description}
CLASS:#{class}
DTSTART:#{Timex.format(dtstart, "{ISO:Basic:Z}")}
DTEND:#{Timex.format(dtend, "{ISO:Basic:Z}")}
DTSTAMP:#{Timex.format(DateTime.utc_now(), "{ISO:Basic:Z}")}
END:VEVENT
END:VCALENDAR
"""
end
end