module Data.ItemTemplate exposing ( ItemTemplate , concEquip , concPerson , concat , concerning , corrOrg , corrPerson , correspondent , dateLong , dateShort , direction , dueDateLong , dueDateShort , empty , fileCount , folder , from , fromMaybe , helpMessage , isEmpty , literal , map , name , nonEmpty , readTemplate , render , source , splitTokens ) import Api.Model.IdName exposing (IdName) import Api.Model.ItemLight exposing (ItemLight) import Data.Direction import Set import Util.List import Util.String import Util.Time type ItemTemplate = ItemTemplate (ItemLight -> String) readTemplate : String -> Maybe ItemTemplate readTemplate str = let read tokens = List.map patternToken tokens |> concat in if str == "" then Just empty else Maybe.map read (splitTokens str) render : ItemTemplate -> ItemLight -> String render pattern item = case pattern of ItemTemplate f -> f item isEmpty : ItemTemplate -> ItemLight -> Bool isEmpty pattern item = render pattern item |> String.isEmpty nonEmpty : ItemTemplate -> ItemLight -> Bool nonEmpty pattern item = isEmpty pattern item |> not --- Pattern Combinators map : (String -> String) -> ItemTemplate -> ItemTemplate map f pattern = case pattern of ItemTemplate p -> from (p >> f) map2 : (String -> String -> String) -> ItemTemplate -> ItemTemplate -> ItemTemplate map2 f pattern1 pattern2 = case ( pattern1, pattern2 ) of ( ItemTemplate p1, ItemTemplate p2 ) -> from (\i -> f (p1 i) (p2 i)) combine : String -> ItemTemplate -> ItemTemplate -> ItemTemplate combine sep p1 p2 = map2 (\s1 -> \s2 -> List.filter (String.isEmpty >> not) [ s1, s2 ] |> String.join sep ) p1 p2 concat : List ItemTemplate -> ItemTemplate concat patterns = from (\i -> List.map (\p -> render p i) patterns |> String.join "" ) firstNonEmpty : List ItemTemplate -> ItemTemplate firstNonEmpty patterns = from (\i -> List.map (\p -> render p i) patterns |> List.filter (String.isEmpty >> not) |> List.head |> Maybe.withDefault "" ) --- Patterns from : (ItemLight -> String) -> ItemTemplate from f = ItemTemplate f fromMaybe : (ItemLight -> Maybe String) -> ItemTemplate fromMaybe f = ItemTemplate (f >> Maybe.withDefault "") literal : String -> ItemTemplate literal str = ItemTemplate (\_ -> str) empty : ItemTemplate empty = literal "" name : ItemTemplate name = ItemTemplate (.name >> Util.String.underscoreToSpace) direction : ItemTemplate direction = let dirStr ms = Maybe.andThen Data.Direction.fromString ms |> Maybe.map Data.Direction.toString in fromMaybe (.direction >> dirStr) dateLong : ItemTemplate dateLong = ItemTemplate (.date >> Util.Time.formatDate) dateShort : ItemTemplate dateShort = ItemTemplate (.date >> Util.Time.formatDateShort) dueDateLong : ItemTemplate dueDateLong = fromMaybe (.dueDate >> Maybe.map Util.Time.formatDate) dueDateShort : ItemTemplate dueDateShort = fromMaybe (.dueDate >> Maybe.map Util.Time.formatDateShort) source : ItemTemplate source = ItemTemplate .source folder : ItemTemplate folder = ItemTemplate (.folder >> getName) corrOrg : ItemTemplate corrOrg = ItemTemplate (.corrOrg >> getName) corrPerson : ItemTemplate corrPerson = ItemTemplate (.corrPerson >> getName) correspondent : ItemTemplate correspondent = combine ", " corrOrg corrPerson concPerson : ItemTemplate concPerson = ItemTemplate (.concPerson >> getName) concEquip : ItemTemplate concEquip = ItemTemplate (.concEquipment >> getName) concerning : ItemTemplate concerning = combine ", " concPerson concEquip fileCount : ItemTemplate fileCount = ItemTemplate (.fileCount >> String.fromInt) --- Helpers getName : Maybe IdName -> String getName = Maybe.map .name >> Maybe.withDefault "" --- Parse pattern helpMessage : String helpMessage = """ A pattern allows to customize the title and subtitle of each card. Variables expressions are enclosed in `{{` and `}}`, other text is used as-is. The following variables are available: - `{{name}}` the item name - `{{source}}` the source the item was created from - `{{folder}}` the items folder - `{{corrOrg}}` the correspondent organization - `{{corrPerson}}` the correspondent person - `{{correspondent}}` both organization and person separated by a comma - `{{concPerson}}` the concerning person - `{{concEquip}}` the concerning equipment - `{{concerning}}` both person and equipment separated by a comma - `{{fileCount}}` the number of attachments of this item - `{{dateLong}}` the item date as full formatted date - `{{dateShort}}` the item date as short formatted date (yyyy/mm/dd) - `{{dueDateLong}}` the item due date as full formatted date - `{{dueDateShort}}` the item due date as short formatted date (yyyy/mm/dd) - `{{direction}}` the items direction values as string If some variable is not present, an empty string is rendered. You can combine multiple variables with `|` to use the first non-empty one, for example `{{corrOrg|corrPerson|-}}` would render the organization and if that is not present the person. If both are absent a dash `-` is rendered. """ knownPattern : String -> Maybe ItemTemplate knownPattern str = case str of "{{name}}" -> Just name "{{source}}" -> Just source "{{folder}}" -> Just folder "{{corrOrg}}" -> Just corrOrg "{{corrPerson}}" -> Just corrPerson "{{correspondent}}" -> Just correspondent "{{concPerson}}" -> Just concPerson "{{concEquip}}" -> Just concEquip "{{concerning}}" -> Just concerning "{{fileCount}}" -> Just fileCount "{{dateLong}}" -> Just dateLong "{{dateShort}}" -> Just dateShort "{{dueDateLong}}" -> Just dueDateLong "{{dueDateShort}}" -> Just dueDateShort "{{direction}}" -> Just direction _ -> Nothing patternToken : String -> ItemTemplate patternToken str = knownPattern str |> Maybe.withDefault (alternativeToken str |> Maybe.withDefault (literal str) ) alternativeToken : String -> Maybe ItemTemplate alternativeToken str = let inner = String.dropLeft 2 str |> String.dropRight 2 |> String.split "|" |> List.filter (String.isEmpty >> not) pattern s = knownPattern ("{{" ++ s ++ "}}") |> Maybe.withDefault (literal s) in if String.startsWith "{{" str && String.endsWith "}}" str then case inner of [] -> Nothing _ -> List.map pattern inner |> firstNonEmpty |> Just else Nothing splitTokens : String -> Maybe (List String) splitTokens str = let begins = String.indexes "{{" str ends = String.indexes "}}" str |> List.map ((+) 2) indexes = Set.union (Set.fromList begins) (Set.fromList ends) |> Set.insert 0 |> Set.insert (String.length str) |> Set.toList |> List.sort mkSubstring i1 i2 = String.slice i1 i2 str in if List.length begins == List.length ends then Util.List.sliding mkSubstring indexes |> Just else Nothing