packages feed

emanote-1.0.2.0: src/Emanote/Model/Calendar.hs

{- | A primitive module to eventually pave way towards first-class "calendar"
 (daily notes, etc.) support in Emanote; either built-in or as plugin.
-}
module Emanote.Model.Calendar where

import Data.Time.Calendar (Day, fromGregorianValid)
import Emanote.Model.Note qualified as N
import Emanote.Model.Title (Title)
import Emanote.Model.Type (Model, modelLookupTitle)
import Emanote.Route (LMLRoute)
import Emanote.Route qualified as R
import Relude
import Text.Megaparsec qualified as M
import Text.Megaparsec.Char qualified as M

-- HACK: This is so that calendar backlinks are sorted properly.
backlinkSortKey :: Model -> LMLRoute -> Down Title
backlinkSortKey model =
  Down . flip modelLookupTitle model

-- HACK: Until we have a proper search support. This sorts query results for
-- timeline
noteSortKey :: N.Note -> (Down (Maybe Text), LMLRoute)
noteSortKey note =
  (Down $ N.lookupMeta @Text (one "date") note, N._noteRoute note)

isDailyNote :: LMLRoute -> Bool
isDailyNote =
  isJust . parseRouteDay

parseRouteDay :: LMLRoute -> Maybe Day
parseRouteDay =
  M.parseMaybe parse . R.withLmlRoute R.routeBaseName
  where
    parse :: M.Parsec Void Text Day
    parse = do
      let asInt = maybe (fail "Not an int") pure . readMaybe
      -- Year
      year <- asInt =<< replicateM 4 M.digitChar
      void $ M.string "-"
      -- Month
      month <- asInt =<< replicateM 2 M.digitChar
      void $ M.string "-"
      -- Day
      day <- asInt =<< replicateM 2 M.digitChar
      -- Optional suffix (ignored)
      void $
        optional $ do
          void $ M.oneOf ['-', '_', ' ']
          void M.takeRest
      maybe (fail "Not a date") pure $
        fromGregorianValid year (fromInteger month) (fromInteger day)