summaryrefslogtreecommitdiff
path: root/Logs/UUIDBased.hs
blob: ac876e65f059b2f3ea2e96074395ab1800902526 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
{- git-annex uuid-based logs
 -
 - This is used to store information about UUIDs in a way that can
 - be union merged.
 -
 - A line of the log will look like: "UUID[ INFO[ timestamp=foo]]"
 - The timestamp is last for backwards compatability reasons,
 - and may not be present on old log lines.
 -
 - New uuid based logs instead use the form: "timestamp UUID INFO"
 - 
 - Copyright 2011-2013 Joey Hess <id@joeyh.name>
 -
 - Licensed under the GNU GPL version 3 or higher.
 -}

module Logs.UUIDBased (
	Log,
	LogEntry(..),
	TimeStamp(..),
	parseLog,
	parseLogNew,
	parseLogWithUUID,
	showLog,
	showLogNew,
	changeLog,
	addLog,
	simpleMap,
) where

import qualified Data.Map as M
import Data.Time.Clock.POSIX
import Data.Time
import System.Locale

import Common
import Types.UUID
import Logs.MapLog

type Log v = MapLog UUID v

showLog :: (v -> String) -> Log v -> String
showLog shower = unlines . map showpair . M.toList
  where
	showpair (k, LogEntry (Date p) v) =
		unwords [fromUUID k, shower v, tskey ++ show p]
	showpair (k, LogEntry Unknown v) =
		unwords [fromUUID k, shower v]

parseLog :: (String -> Maybe a) -> String -> Log a
parseLog = parseLogWithUUID . const

parseLogWithUUID :: (UUID -> String -> Maybe a) -> String -> Log a
parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines
  where
	parse line
		-- This is a workaround for a bug that caused
		-- NoUUID items to be stored in the log.
		-- It can be removed at any time; is just here to clean
		-- up logs where that happened temporarily.
		| " " `isPrefixOf` line = Nothing
		| null ws = Nothing
		| otherwise = parser u (unwords info) >>= makepair
	  where
		makepair v = Just (u, LogEntry ts v)
		ws = words line
		u = toUUID $ Prelude.head ws
		t = Prelude.last ws
		ts
			| tskey `isPrefixOf` t =
				pdate $ drop 1 $ dropWhile (/= '=') t
			| otherwise = Unknown
		info
			| ts == Unknown = drop 1 ws
			| otherwise = drop 1 $ beginning ws
		pdate s = case parseTime defaultTimeLocale "%s%Qs" s of
			Nothing -> Unknown
			Just d -> Date $ utcTimeToPOSIXSeconds d

showLogNew :: (v -> String) -> Log v -> String
showLogNew = showMapLog fromUUID

parseLogNew :: (String -> Maybe v) -> String -> Log v
parseLogNew = parseMapLog (Just . toUUID)

changeLog :: POSIXTime -> UUID -> v -> Log v -> Log v
changeLog = changeMapLog

addLog :: UUID -> LogEntry v -> Log v -> Log v
addLog = addMapLog

tskey :: String
tskey = "timestamp="