@@ -10,17 +10,19 @@ import Control.Arrow (first, (***))
1010import Control.Monad (when)
1111import Control.Monad.Error.Class (catchError)
1212
13−import Data.Monoid ((<>))
1413import Data.Aeson ((.=))
1514import Data.Aeson.BetterErrors
1615import Data.ByteString.Lazy (ByteString)
1716import Data.Either (isLeft, isRight)
18−import Data.Maybe (mapMaybe, fromMaybe)
17+import Data.Maybe (mapMaybe, fromMaybe, maybeToList)
18+import Data.Monoid ((<>))
1919import Data.Text (Text)
20+import Data.Time.Clock (UTCTime)
21+import qualified Data.Time.Format as TimeFormat
2022import Data.Version
21−import qualified Data.Vector as V
2223import qualified Data.Aeson as A
2324import qualified Data.Text as T
25+import qualified Data.Vector as V
2426
2527import qualified Language.PureScript as P
2628
@@ -40,6 +42,10 @@ data Package a = Package
4042 { pkgMeta :: PackageMeta
4143 , pkgVersion :: Version
4244 , pkgVersionTag :: Text
45+ -- TODO: When this field was introduced, it was given the Maybe type for the
46+ -- sake of backwards compatibility, as older JSON blobs will not include the
47+ -- field. It should eventually be changed to just UTCTime.
48+ , pkgTagTime :: Maybe UTCTime
4349 , pkgModules :: [Module]
4450 , pkgBookmarks :: [Bookmark]
4551 , pkgResolvedDependencies :: [(PackageName, Version)]
@@ -62,6 +68,7 @@ verifyPackage verifiedUser Package{..} =
6268 Package pkgMeta
6369 pkgVersion
6470 pkgVersionTag
71+ pkgTagTime
6572 pkgModules
6673 pkgBookmarks
6774 pkgResolvedDependencies
@@ -72,6 +79,29 @@ verifyPackage verifiedUser Package{..} =
7279packageName :: Package a -> PackageName
7380packageName = bowerName . pkgMeta
7481
82+-- |
83+-- The time format used for serializing package tag times in the JSON format.
84+-- This is the ISO 8601 date format which includes a time and a timezone.
85+--
86+jsonTimeFormat :: String
87+jsonTimeFormat = "%Y-%m-%dT%H:%M:%S%z"
88+
89+-- |
90+-- Convenience function for formatting a time in the format expected by this
91+-- module.
92+--
93+formatTime :: UTCTime -> String
94+formatTime =
95+ TimeFormat.formatTime TimeFormat.defaultTimeLocale jsonTimeFormat
96+
97+-- |
98+-- Convenience function for parsing a time in the format expected by this
99+-- module.
100+--
101+parseTime :: String -> Maybe UTCTime
102+parseTime =
103+ TimeFormat.parseTimeM False TimeFormat.defaultTimeLocale jsonTimeFormat
104+
75105data Module = Module
76106 { modName :: P.ModuleName
77107 , modComments :: Maybe Text
@@ -275,6 +305,7 @@ data PackageError
275305 | InvalidFixity
276306 | InvalidKind Text
277307 | InvalidDataDeclType Text
308+ | InvalidTime
278309 deriving (Show, Eq, Ord)
279310
280311type Bookmark = InPackage (P.ModuleName, Text)
@@ -320,6 +351,7 @@ asPackage minimumVersion uploader = do
320351 Package <$> key "packageMeta" asPackageMeta .! ErrorInPackageMeta
321352 <*> key "version" asVersion
322353 <*> key "versionTag" asText
354+ <*> keyMay "tagTime" (withString parseTimeEither)
323355 <*> key "modules" (eachInArray asModule)
324356 <*> key "bookmarks" asBookmarks .! ErrorInPackageMeta
325357 <*> key "resolvedDependencies" asResolvedDependencies
@@ -327,6 +359,10 @@ asPackage minimumVersion uploader = do
327359 <*> key "uploader" uploader
328360 <*> pure compilerVersion
329361
362+parseTimeEither :: String -> Either PackageError UTCTime
363+parseTimeEither =
364+ maybe (Left InvalidTime) Right . parseTime
365+
330366asUploadedPackage :: Version -> Parse PackageError UploadedPackage
331367asUploadedPackage minVersion = asPackage minVersion asNotYetKnown
332368
@@ -359,6 +395,8 @@ displayPackageError e = case e of
359395 "Invalid kind: \"" <> str <> "\""
360396 InvalidDataDeclType str ->
361397 "Invalid data declaration type: \"" <> str <> "\""
398+ InvalidTime ->
399+ "Invalid time"
362400
363401instance A.FromJSON a => A.FromJSON (Package a) where
364402 parseJSON = toAesonParser displayPackageError
@@ -550,7 +588,7 @@ asSourceSpan = P.SourceSpan <$> key "name" asString
550588
551589instance A.ToJSON a => A.ToJSON (Package a) where
552590 toJSON Package{..} =
553− A.object
591+ A.object $
554592 [ "packageMeta" .= pkgMeta
555593 , "version" .= showVersion pkgVersion
556594 , "versionTag" .= pkgVersionTag
@@ -562,7 +600,8 @@ instance A.ToJSON a => A.ToJSON (Package a) where
562600 , "github" .= pkgGithub
563601 , "uploader" .= pkgUploader
564602 , "compilerVersion" .= showVersion P.version
565− ]
603+ ] ++
604+ fmap (\t -> "tagTime" .= formatTime t) (maybeToList pkgTagTime)
566605
567606instance A.ToJSON NotYetKnown where
568607 toJSON _ = A.Null