{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances #-} module Celtchar.Novel.Structure where import Control.Applicative ((<|>)) import Data.HashMap.Strict ((!)) import Data.Maybe (fromMaybe) import Data.Text (pack) import Data.Yaml import GHC.Generics class HasDependencies c where getDeps :: c -> [FilePath] data Language = French | English deriving (Generic) newtype Document = Document FilePath deriving (Generic, Show) instance HasDependencies Document where getDeps (Document fp) = [fp] instance FromJSON Document where parseJSON v = Document <$> parseJSON v instance ToJSON Document where toJSON (Document path) = String (pack path) data Chapter = Chapter { chapterTitle :: Maybe String , documents :: [Document] } deriving (Generic, Show) instance HasDependencies Chapter where getDeps (Chapter _ docs) = docs >>= getDeps instance FromJSON Chapter where parseJSON (Object v) = Chapter <$> v .:? "title" <*> v .: "documents" instance ToJSON Chapter where toJSON (Chapter title docs) = object [ "title" .= title , "documents" .= docs ] data Part = Part { partTitle :: String , chapters :: [Chapter] } deriving (Generic, Show) instance FromJSON Part where parseJSON (Object v) = Part <$> v .: "title" <*> v .: "chapters" instance ToJSON Part where toJSON (Part title chaps) = object [ "title" .= title , "chapters" .= chaps ] instance HasDependencies Part where getDeps (Part _ chaps) = chaps >>= getDeps data Manuscript = WithParts [Part] | WithChapters [Chapter] deriving (Generic, Show) instance HasDependencies Manuscript where getDeps (WithParts parts) = parts >>= getDeps getDeps (WithChapters chaps) = chaps >>= getDeps instance FromJSON Manuscript where parseJSON v = (WithParts <$> parseJSON v) <|> (WithChapters <$> parseJSON v) instance ToJSON Manuscript where toJSON (WithParts p) = toJSON p toJSON (WithChapters c) = toJSON c instance FromJSON Language where parseJSON (String "english") = pure English parseJSON (String "french") = pure French parseJSON _ = fail "unknown language" instance ToJSON Language where toJSON English = String $ pack "english" toJSON French = String $ pack "french" instance Show Language where show English = "english" show French = "french" data Novel = Novel { author :: String , language :: Language , novelTitle :: String , frontmatter :: Maybe [Chapter] , manuscript :: Manuscript , appendix :: Maybe [Chapter] } deriving (Generic, Show) instance HasDependencies Novel where getDeps (Novel _ _ _ mfrontmatter man mappendix) = (frontmatter >>= getDeps) `mappend` getDeps man `mappend` (appendix >>= getDeps) where frontmatter = fromMaybe [] mfrontmatter appendix = fromMaybe [] mappendix instance FromJSON Novel where parseJSON (Object v) = Novel <$> v .: "author" <*> v .: "language" <*> v .: "title" <*> v .:? "frontmatter" <*> v .: "manuscript" <*> v .:? "appendix" instance ToJSON Novel where toJSON (Novel author language title front man app) = object [ "language" .= language , "author" .= author , "frontmatter" .= front , "title" .= title , "manuscript" .= man , "appendix" .= app ] getNovelStructure :: FilePath -> IO (Either String Novel) getNovelStructure conf = do ec <- decodeFileEither conf case ec of Right novel -> pure . Right $ novel Left ex -> pure . Left $ prettyPrintParseException ex defaultNovel :: Novel defaultNovel = Novel { novelTitle = "title" , author = "author" , language = English , frontmatter = Just [] , appendix = Just [] , manuscript = WithParts [ Part { partTitle = "part title" , chapters = [ Chapter { chapterTitle = Just "chapter title" , documents = [] }] } ] }