-- | @key: value@ database, allows duplicate @key@s. module Music.Theory.DB.Plain where import Data.List {- base -} import qualified Data.List.Split as Split {- split -} import Data.Maybe {- base -} import qualified Safe as Safe {- safe -} import qualified Music.Theory.IO as IO {- hmt -} import qualified Music.Theory.List as T {- hmt -} -- | (RECORD-SEPARATOR,FIELD-SEPARATOR,ENTRY-SEPARATOR) type SEP = (String,String,String) type Key = String type Value = String type Entry = (Key,[Value]) type Record = [Entry] type DB = [Record] sep_plain :: SEP sep_plain = (['\n','\n'],['\n'],": ") -- > record_parse (";","=") "F=f/rec;E=au;C=A;K=P;K=Q" record_parse :: (String,String) -> String -> Record record_parse (fs,es) = T.collate_adjacent . mapMaybe (T.separate_at es) . Split.splitOn fs record_lookup :: Key -> Record -> [Value] record_lookup k = fromMaybe [] . lookup k record_lookup_at :: (Key,Int) -> Record -> Maybe Value record_lookup_at (k,n) = flip Safe.atMay n . record_lookup k record_has_key :: Key -> Record -> Bool record_has_key k = isJust . lookup k record_lookup_uniq :: Key -> Record -> Maybe Value record_lookup_uniq k r = case record_lookup k r of [] -> Nothing [v] -> Just v _ -> error "record_lookup_uniq: non uniq" db_parse :: SEP -> String -> [Record] db_parse (rs,fs,es) s = let r = Split.splitOn rs s in map (record_parse (fs,es)) r db_sort :: [(Key,Int)] -> [Record] -> [Record] db_sort k = T.sort_by_n_stage (map record_lookup_at k) db_load_utf8 :: SEP -> FilePath -> IO [Record] db_load_utf8 sep = fmap (db_parse sep) . IO.read_file_utf8 -- > record_pp (";","=") [("F","f/rec.au"),("C","A")] record_pp :: (String,String) -> Record -> String record_pp (fs,es) = intercalate fs . map (\(k,v) -> k ++ es ++ v) . T.uncollate db_store_utf8 :: SEP -> FilePath -> [Record] -> IO () db_store_utf8 (rs,fs,es) fn = IO.write_file_utf8 fn . intercalate rs . map (record_pp (fs,es))