From 69e07588741f4ceba9e12c1d1d236c266ad5f0ca Mon Sep 17 00:00:00 2001
From: Alice BRENON <alice.brenon@ens-lyon.fr>
Date: Fri, 26 Jan 2024 15:17:23 +0100
Subject: [PATCH] Add parsing UIDs as a requirement for Record, explain how
 they propagate along @-chains and make a case-insensitive instance for Work

---
 lib/GEODE/Metadata/ArticleRecord.hs | 13 +++++++++----
 lib/GEODE/Metadata/Record.hs        | 10 ++++++++--
 lib/GEODE/Metadata/Work.hs          | 15 ++++++++++-----
 3 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/lib/GEODE/Metadata/ArticleRecord.hs b/lib/GEODE/Metadata/ArticleRecord.hs
index 1337b74..023c45b 100644
--- a/lib/GEODE/Metadata/ArticleRecord.hs
+++ b/lib/GEODE/Metadata/ArticleRecord.hs
@@ -1,10 +1,9 @@
 {-# LANGUAGE DeriveGeneric, FlexibleContexts, FlexibleInstances, NamedFieldPuns, OverloadedStrings #-}
 module GEODE.Metadata.ArticleRecord
   ( Work(..)
-  , ArticleRecord(..)
-  , relativePath
-  , uid ) where
+  , ArticleRecord(..) ) where
 
+import Control.Monad.State (MonadState(..), StateT(..), evalStateT, lift)
 import Data.Aeson ((.=), FromJSON(..), ToJSON(..))
 import Data.Aeson.KeyMap as KeyMap (fromList)
 import Data.Csv (DefaultOrdered(..), FromNamedRecord(..), ToNamedRecord(..))
@@ -14,6 +13,7 @@ import GEODE.Metadata.Work (Work(..))
 import GHC.Generics (Generic)
 import System.FilePath ((</>), (<.>))
 import Text.Printf (printf)
+import Text.Read (readEither)
 
 data ArticleRecord = ArticleRecord
   { work :: Work
@@ -37,8 +37,13 @@ instance ToJSONObject ArticleRecord where
 instance FromJSON ArticleRecord
 
 instance Record ArticleRecord where
-  uid (ArticleRecord {work, volume, article}) =
+  toUID (ArticleRecord {work, volume, article}) =
     printf "%s_%d_%d" (show $ work) volume article
 
+  fromUID = evalStateT $ ArticleRecord <$> nextBlock <*> nextBlock <*> nextBlock
+    where
+      nextBlock :: Read a => StateT String (Either String) a
+      nextBlock = state (fmap (drop 1) . break (== '_')) >>= lift . readEither
+
   relativePath (ArticleRecord {work, volume, article}) extension =
     (show work) </> ("T" <> show volume) </> (show article) <.> extension
diff --git a/lib/GEODE/Metadata/Record.hs b/lib/GEODE/Metadata/Record.hs
index 094fc10..abf55ab 100644
--- a/lib/GEODE/Metadata/Record.hs
+++ b/lib/GEODE/Metadata/Record.hs
@@ -7,10 +7,16 @@ import System.FilePath ((</>))
 import Text.Printf (printf)
 
 class Record a where
-  uid :: a -> String
+  toUID :: a -> String
+  fromUID :: String -> Either String a
   relativePath :: a -> String -> FilePath
 
 instance (Record a, Record b) => Record (a @ b) where
-  uid (a :@: b) = printf "%s_%s" (uid a) (uid b)
+  toUID (a :@: b) = printf "%s_%s" (toUID a) (toUID b)
+
+  fromUID s = do
+    prefix <- fromUID s
+    (prefix :@:) <$> fromUID (drop (length (toUID prefix) + 1) s)
+
   relativePath (a :@: b) extension =
     relativePath a "" </> relativePath b extension
diff --git a/lib/GEODE/Metadata/Work.hs b/lib/GEODE/Metadata/Work.hs
index 68e4117..e1e7660 100644
--- a/lib/GEODE/Metadata/Work.hs
+++ b/lib/GEODE/Metadata/Work.hs
@@ -2,29 +2,34 @@
 module GEODE.Metadata.Work
   ( Work(..) ) where
 
+import Control.Applicative (Alternative(..))
 import Data.Aeson (FromJSON(..), ToJSON(..), withText)
 import Data.ByteString.Char8 as ByteString (unpack)
 import Data.Csv (FromField(..), ToField(..))
 import Data.Char (toLower)
 import Data.Text as Text (unpack)
 import GHC.Generics (Generic)
+import Text.Read (Read(..), get)
 
 data Work = EDdA | LGE | Wikipedia deriving (Eq, Generic, Ord, Show)
 
-tolerantParser :: (Applicative m, Monoid (m Work)) => String -> m Work
-tolerantParser = recognize . fmap toLower
+readCaseInsensitive :: Alternative m => String -> m Work
+readCaseInsensitive = recognize . fmap toLower
   where
     recognize "edda" = pure EDdA
     recognize "lge" = pure LGE
     recognize "wikipedia" = pure Wikipedia
-    recognize _ = mempty
+    recognize _ = empty
+
+instance Read Work where
+  readPrec = many get >>= readCaseInsensitive
 
 instance FromField Work where
-  parseField = tolerantParser . ByteString.unpack
+  parseField = readCaseInsensitive . ByteString.unpack
 
 instance ToField Work where
   toField = toField . show
 
 instance ToJSON Work
 instance FromJSON Work where
-  parseJSON = withText "Work" $ tolerantParser . Text.unpack
+  parseJSON = withText "Work" $ readCaseInsensitive . Text.unpack
-- 
GitLab