From e0f45b29b3f5e17c04ecd054b6c26c67d71e10e4 Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Fri, 11 Oct 2013 02:37:46 +0400 Subject: added gloabl metadata parsing --- src/Hakyll/Core/Provider/Metadata.hs | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'src/Hakyll/Core/Provider') diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index 7e4d7ed..6404205 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -20,6 +20,8 @@ import System.IO as IO import Text.Parsec (()) import qualified Text.Parsec as P import Text.Parsec.String (Parser) +import System.FilePath.Posix +import Control.Monad (liftM) -------------------------------------------------------------------------------- @@ -28,7 +30,7 @@ import Hakyll.Core.Metadata import Hakyll.Core.Provider.Internal import Hakyll.Core.Util.Parser import Hakyll.Core.Util.String - +import Hakyll.Core.Identifier.Pattern -------------------------------------------------------------------------------- loadMetadata :: Provider -> Identifier -> IO (Metadata, Maybe String) @@ -42,7 +44,9 @@ loadMetadata p identifier = do Nothing -> return M.empty Just mi' -> loadMetadataFile $ resourceFilePath p mi' - return (M.union md emd, body) + gmd <- loadGlobalMetadata p fp + + return (M.unions [md, emd, gmd], body) where normal = setVersion Nothing identifier fp = resourceFilePath p identifier @@ -133,3 +137,40 @@ page = do metadata' <- P.option [] metadataBlock body <- P.many P.anyChar return (metadata', body) + + +-------------------------------------------------------------------------------- +-- | Load directory-wise metadata +loadGlobalMetadata :: Provider -> FilePath -> IO (M.Map String String) +loadGlobalMetadata p fp = do + let dir = takeDirectory fp + liftM M.fromList $ loadgm dir + where + loadgm :: FilePath -> IO [(String, String)] + loadgm dir | dir == providerDirectory p = return [] + | otherwise = do + let mfp = combine dir "metadata" + md <- if M.member (fromFilePath $ normalise mfp) (providerFiles p) + then loadOne mfp dir + else return [] + others <- loadgm (takeDirectory dir) + return $ others ++ md + loadOne mfp dir = do + contents <- IO.readFile mfp + return $ case P.parse namedMetadata mfp contents of + Left err -> error (show err) + Right mds -> findMetadata mds dir + findMetadata mds dir = + concatMap snd $ filter (flip matches (fromFilePath fp) . fromGlob . combine dir . fst) mds + +namedMetadata :: Parser [(String, [(String, String)])] +namedMetadata = P.many namedMetadataBlock + +namedMetadataBlock :: Parser (String, [(String, String)]) +namedMetadataBlock = do + name <- P.many1 (P.char '-') *> P.many inlineSpace *> P.manyTill P.anyChar newline + metadata' <- metadata + P.skipMany P.space + return (name, metadata') + + -- cgit v1.2.3 From e71bbd90a8fb4b5de7796498aed4b95bb7abaebb Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Sat, 12 Oct 2013 23:33:08 +0400 Subject: Fix loadGlobalMetadata on Windows --- src/Hakyll/Core/Provider/Metadata.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Hakyll/Core/Provider') diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index 6404205..fcc1d6c 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -21,7 +21,7 @@ import Text.Parsec (()) import qualified Text.Parsec as P import Text.Parsec.String (Parser) import System.FilePath.Posix -import Control.Monad (liftM) +import Control.Monad (liftM) -------------------------------------------------------------------------------- @@ -44,7 +44,7 @@ loadMetadata p identifier = do Nothing -> return M.empty Just mi' -> loadMetadataFile $ resourceFilePath p mi' - gmd <- loadGlobalMetadata p fp + gmd <- loadGlobalMetadata p $ toFilePath identifier return (M.unions [md, emd, gmd], body) where @@ -150,13 +150,13 @@ loadGlobalMetadata p fp = do loadgm dir | dir == providerDirectory p = return [] | otherwise = do let mfp = combine dir "metadata" - md <- if M.member (fromFilePath $ normalise mfp) (providerFiles p) + md <- if M.member (fromFilePath mfp) (providerFiles p) then loadOne mfp dir else return [] others <- loadgm (takeDirectory dir) return $ others ++ md loadOne mfp dir = do - contents <- IO.readFile mfp + contents <- IO.readFile $ resourceFilePath p $ fromFilePath mfp return $ case P.parse namedMetadata mfp contents of Left err -> error (show err) Right mds -> findMetadata mds dir -- cgit v1.2.3 From a6ae4eb99dd5f9957adc3b5fe4011e69b9681e4c Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Tue, 15 Oct 2013 03:59:16 +0400 Subject: Fix root directory in directory-wise metadata and beautify code --- src/Hakyll/Core/Provider/Metadata.hs | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'src/Hakyll/Core/Provider') diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index fcc1d6c..8f39f22 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -142,26 +142,20 @@ page = do -------------------------------------------------------------------------------- -- | Load directory-wise metadata loadGlobalMetadata :: Provider -> FilePath -> IO (M.Map String String) -loadGlobalMetadata p fp = do - let dir = takeDirectory fp - liftM M.fromList $ loadgm dir - where - loadgm :: FilePath -> IO [(String, String)] - loadgm dir | dir == providerDirectory p = return [] - | otherwise = do - let mfp = combine dir "metadata" - md <- if M.member (fromFilePath mfp) (providerFiles p) - then loadOne mfp dir - else return [] - others <- loadgm (takeDirectory dir) - return $ others ++ md - loadOne mfp dir = do - contents <- IO.readFile $ resourceFilePath p $ fromFilePath mfp - return $ case P.parse namedMetadata mfp contents of - Left err -> error (show err) - Right mds -> findMetadata mds dir - findMetadata mds dir = - concatMap snd $ filter (flip matches (fromFilePath fp) . fromGlob . combine dir . fst) mds +loadGlobalMetadata p fp = liftM M.fromList $ loadgm $ takeDirectory fp where + loadgm :: FilePath -> IO [(String, String)] + loadgm dir | dir == "." = return [] + | otherwise = do + let mfp = fromFilePath $ combine dir "metadata" + md <- if resourceExists p mfp then loadOne mfp dir else return [] + others <- loadgm (takeDirectory dir) + return $ others ++ md + loadOne mfp dir = + let path = resourceFilePath p mfp + -- TODO: It might be better to print warning and continue + in either (error.show) (findMetadata dir) . P.parse namedMetadata path <$> readFile path + findMetadata dir = + concatMap snd . filter (flip matches (fromFilePath fp) . fromGlob . combine dir . fst) namedMetadata :: Parser [(String, [(String, String)])] namedMetadata = P.many namedMetadataBlock -- cgit v1.2.3 From 712ffa39b5857fdc5cdae1db38a177267a430b11 Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Wed, 16 Oct 2013 01:39:04 +0400 Subject: Track metadata dependencies I had to prepend some Rules to global Rules set. This might be possible to replaced by a correct Store.set call. I also had to prepend some Compile rules. --- hakyll.cabal | 1 + src/Hakyll/Core/Metadata.hs | 10 ++++++++++ src/Hakyll/Core/Provider/Metadata.hs | 20 ++++++++------------ src/Hakyll/Core/Rules/Default.hs | 23 +++++++++++++++++++++++ src/Hakyll/Core/Runtime.hs | 5 +++-- 5 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/Hakyll/Core/Rules/Default.hs (limited to 'src/Hakyll/Core/Provider') diff --git a/hakyll.cabal b/hakyll.cabal index b014f23..7af36b6 100644 --- a/hakyll.cabal +++ b/hakyll.cabal @@ -133,6 +133,7 @@ Library Hakyll.Core.Provider.Metadata Hakyll.Core.Provider.MetadataCache Hakyll.Core.Rules.Internal + Hakyll.Core.Rules.Default Hakyll.Core.Runtime Hakyll.Core.Store Hakyll.Core.Util.File diff --git a/src/Hakyll/Core/Metadata.hs b/src/Hakyll/Core/Metadata.hs index 7902b94..a330636 100644 --- a/src/Hakyll/Core/Metadata.hs +++ b/src/Hakyll/Core/Metadata.hs @@ -5,6 +5,7 @@ module Hakyll.Core.Metadata , getMetadataField , getMetadataField' , makePatternDependency + , metadataFiles ) where @@ -12,6 +13,7 @@ module Hakyll.Core.Metadata import Control.Monad (forM) import Data.Map (Map) import qualified Data.Map as M +import System.FilePath.Posix ((), takeDirectory) -------------------------------------------------------------------------------- @@ -61,3 +63,11 @@ makePatternDependency :: MonadMetadata m => Pattern -> m Dependency makePatternDependency pattern = do matches' <- getMatches pattern return $ PatternDependency pattern matches' + +-------------------------------------------------------------------------------- +-- | Returns a list of all directory-wise metadata files, subdir first, global last +metadataFiles :: Identifier -> [Identifier] +metadataFiles = go . takeDirectory . toFilePath where + go "." = [fromFilePath "metadata"] + go dir = fromFilePath (dir "metadata") : go (takeDirectory dir) + diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index 8f39f22..4331861 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -44,7 +44,7 @@ loadMetadata p identifier = do Nothing -> return M.empty Just mi' -> loadMetadataFile $ resourceFilePath p mi' - gmd <- loadGlobalMetadata p $ toFilePath identifier + gmd <- loadGlobalMetadata p identifier return (M.unions [md, emd, gmd], body) where @@ -141,21 +141,17 @@ page = do -------------------------------------------------------------------------------- -- | Load directory-wise metadata -loadGlobalMetadata :: Provider -> FilePath -> IO (M.Map String String) -loadGlobalMetadata p fp = liftM M.fromList $ loadgm $ takeDirectory fp where - loadgm :: FilePath -> IO [(String, String)] - loadgm dir | dir == "." = return [] - | otherwise = do - let mfp = fromFilePath $ combine dir "metadata" - md <- if resourceExists p mfp then loadOne mfp dir else return [] - others <- loadgm (takeDirectory dir) - return $ others ++ md - loadOne mfp dir = +loadGlobalMetadata :: Provider -> Identifier -> IO Metadata +loadGlobalMetadata p fp = liftM M.fromList $ loadgm fp where + loadgm :: Identifier -> IO [(String, String)] + loadgm = liftM concat . mapM loadOne . reverse . filter (resourceExists p) . metadataFiles + loadOne mfp = let path = resourceFilePath p mfp + dir = takeDirectory $ toFilePath mfp -- TODO: It might be better to print warning and continue in either (error.show) (findMetadata dir) . P.parse namedMetadata path <$> readFile path findMetadata dir = - concatMap snd . filter (flip matches (fromFilePath fp) . fromGlob . combine dir . fst) + concatMap snd . filter (flip matches fp . fromGlob . combine dir . fst) namedMetadata :: Parser [(String, [(String, String)])] namedMetadata = P.many namedMetadataBlock diff --git a/src/Hakyll/Core/Rules/Default.hs b/src/Hakyll/Core/Rules/Default.hs new file mode 100644 index 0000000..ccca1bd --- /dev/null +++ b/src/Hakyll/Core/Rules/Default.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE OverloadedStrings #-} +module Hakyll.Core.Rules.Default + ( internalRules + , addMetadataDependencies + ) +where +import Hakyll.Core.Rules +import Hakyll.Core.Compiler +import Hakyll.Core.Compiler.Internal (compilerTellDependencies) +import Hakyll.Core.Metadata (getMatches, metadataFiles) +import Hakyll.Core.Identifier.Pattern(fromList) + +internalRules :: Rules () +internalRules = do + match "metadata" $ compile $ makeItem () + match "**/metadata" $ compile $ makeItem () + +-------------------------------------------------------------------------------- +addMetadataDependencies :: Compiler () +addMetadataDependencies = + compilerTellDependencies . map IdentifierDependency =<< getMatches . fromList =<< fmap metadataFiles getUnderlying + + diff --git a/src/Hakyll/Core/Runtime.hs b/src/Hakyll/Core/Runtime.hs index 824d11b..4ef30df 100644 --- a/src/Hakyll/Core/Runtime.hs +++ b/src/Hakyll/Core/Runtime.hs @@ -35,6 +35,7 @@ import qualified Hakyll.Core.Logger as Logger import Hakyll.Core.Provider import Hakyll.Core.Routes import Hakyll.Core.Rules.Internal +import Hakyll.Core.Rules.Default import Hakyll.Core.Store (Store) import qualified Hakyll.Core.Store as Store import Hakyll.Core.Util.File @@ -53,7 +54,7 @@ run config verbosity rules = do provider <- newProvider store (shouldIgnoreFile config) $ providerDirectory config Logger.message logger "Running rules..." - ruleSet <- runRules rules provider + ruleSet <- runRules (internalRules >> rules) provider -- Get old facts mOldFacts <- Store.get store factsKey @@ -186,7 +187,7 @@ chase trail id' config <- runtimeConfiguration <$> ask Logger.debug logger $ "Processing " ++ show id' - let compiler = todo M.! id' + let compiler = addMetadataDependencies >> todo M.! id' read' = CompilerRead { compilerConfig = config , compilerUnderlying = id' -- cgit v1.2.3 From 46b6f78da8ed814bef6972f4712d8b13e76c1122 Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Fri, 18 Oct 2013 01:23:14 +0400 Subject: Added test case for global metadata --- src/Hakyll/Core/Provider/Metadata.hs | 2 +- tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs | 31 ++++++++++++++++++++++ tests/TestSuite.hs | 2 ++ tests/data/metadata | 27 +++++++++++++++++++ tests/data/posts/2013-10-18-metadata-test.md | 4 +++ .../posts/2013-10-18-metadata-test.md.metadata | 2 ++ tests/data/posts/metadata | 18 +++++++++++++ 7 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs create mode 100644 tests/data/metadata create mode 100644 tests/data/posts/2013-10-18-metadata-test.md create mode 100644 tests/data/posts/2013-10-18-metadata-test.md.metadata create mode 100644 tests/data/posts/metadata (limited to 'src/Hakyll/Core/Provider') diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index 4331861..a54b84a 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -151,7 +151,7 @@ loadGlobalMetadata p fp = liftM M.fromList $ loadgm fp where -- TODO: It might be better to print warning and continue in either (error.show) (findMetadata dir) . P.parse namedMetadata path <$> readFile path findMetadata dir = - concatMap snd . filter (flip matches fp . fromGlob . combine dir . fst) + concatMap snd . filter (flip matches fp . fromGlob . normalise . combine dir . fst) namedMetadata :: Parser [(String, [(String, String)])] namedMetadata = P.many namedMetadataBlock diff --git a/tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs b/tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs new file mode 100644 index 0000000..289e2ac --- /dev/null +++ b/tests/Hakyll/Core/Provider/GlobalMetadata/Tests.hs @@ -0,0 +1,31 @@ +-------------------------------------------------------------------------------- +{-# LANGUAGE OverloadedStrings #-} +module Hakyll.Core.Provider.GlobalMetadata.Tests + ( tests + ) where + +-------------------------------------------------------------------------------- +import qualified Data.Map as M +import Control.Monad (forM_) +import Test.Framework (Test, testGroup) +import Test.HUnit (Assertion, (@=?)) + + +-------------------------------------------------------------------------------- +import Hakyll.Core.Provider (resourceMetadata) +import TestSuite.Util + +-------------------------------------------------------------------------------- +tests :: Test +tests = testGroup "Hakyll.Core.Provider.GlobalMetadata.Tests" $ + fromAssertions "page" [testPage] + +testPage :: Assertion +testPage = do + store <- newTestStore + provider <- newTestProvider store + + metadata <- resourceMetadata provider "posts/2013-10-18-metadata-test.md" + forM_ ["1", "2", "3", "4", "5", "6", "7", "8"] $ \a -> + Just a @=? M.lookup ('a':a) metadata + diff --git a/tests/TestSuite.hs b/tests/TestSuite.hs index 3622301..9cc446e 100644 --- a/tests/TestSuite.hs +++ b/tests/TestSuite.hs @@ -12,6 +12,7 @@ import Test.Framework (defaultMain) import qualified Hakyll.Core.Dependencies.Tests import qualified Hakyll.Core.Identifier.Tests import qualified Hakyll.Core.Provider.Metadata.Tests +import qualified Hakyll.Core.Provider.GlobalMetadata.Tests import qualified Hakyll.Core.Provider.Tests import qualified Hakyll.Core.Routes.Tests import qualified Hakyll.Core.Rules.Tests @@ -32,6 +33,7 @@ main = defaultMain [ Hakyll.Core.Dependencies.Tests.tests , Hakyll.Core.Identifier.Tests.tests , Hakyll.Core.Provider.Metadata.Tests.tests + , Hakyll.Core.Provider.GlobalMetadata.Tests.tests , Hakyll.Core.Provider.Tests.tests , Hakyll.Core.Routes.Tests.tests , Hakyll.Core.Rules.Tests.tests diff --git a/tests/data/metadata b/tests/data/metadata new file mode 100644 index 0000000..1145976 --- /dev/null +++ b/tests/data/metadata @@ -0,0 +1,27 @@ +--- posts/2013-10-18-metadata-test.md +a1: 8 +a2: 8 +a3: 8 +a4: 8 +a5: 8 +a6: 8 +a7: 8 +a8: 8 + +--- posts/* +a1: 7 +a2: 7 +a3: 7 +a4: 7 +a5: 7 +a6: 7 +a7: 7 + +--- ** +a1: 6 +a2: 6 +a3: 6 +a4: 6 +a5: 6 +a6: 6 + diff --git a/tests/data/posts/2013-10-18-metadata-test.md b/tests/data/posts/2013-10-18-metadata-test.md new file mode 100644 index 0000000..86a3e67 --- /dev/null +++ b/tests/data/posts/2013-10-18-metadata-test.md @@ -0,0 +1,4 @@ +--- +a1: 1 +--- +Nothing interesting here. diff --git a/tests/data/posts/2013-10-18-metadata-test.md.metadata b/tests/data/posts/2013-10-18-metadata-test.md.metadata new file mode 100644 index 0000000..7ee78b6 --- /dev/null +++ b/tests/data/posts/2013-10-18-metadata-test.md.metadata @@ -0,0 +1,2 @@ +a1: 2 +a2: 2 diff --git a/tests/data/posts/metadata b/tests/data/posts/metadata new file mode 100644 index 0000000..e5cd153 --- /dev/null +++ b/tests/data/posts/metadata @@ -0,0 +1,18 @@ +--- ** +a1: 5 +a2: 5 +a3: 5 +a4: 5 +a5: 5 + +--- * +a1: 4 +a2: 4 +a3: 4 +a4: 4 + +--- 2013-10-18-metadata-test.md +a1: 3 +a2: 3 +a3: 3 + -- cgit v1.2.3 From dbabe0325b9d7bca3b14c2b33e686b8151fa79aa Mon Sep 17 00:00:00 2001 From: Alexey Kreshchuk Date: Wed, 22 Jan 2014 00:11:51 +0400 Subject: Unified code for global and external metadata Didn't clean up unused code though --- src/Hakyll/Core/Metadata.hs | 3 ++- src/Hakyll/Core/Provider/Metadata.hs | 12 +++++++----- src/Hakyll/Core/Rules/Default.hs | 1 + tests/data/posts/metadata | 2 ++ 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/Hakyll/Core/Provider') diff --git a/src/Hakyll/Core/Metadata.hs b/src/Hakyll/Core/Metadata.hs index a330636..a123c18 100644 --- a/src/Hakyll/Core/Metadata.hs +++ b/src/Hakyll/Core/Metadata.hs @@ -67,7 +67,8 @@ makePatternDependency pattern = do -------------------------------------------------------------------------------- -- | Returns a list of all directory-wise metadata files, subdir first, global last metadataFiles :: Identifier -> [Identifier] -metadataFiles = go . takeDirectory . toFilePath where +metadataFiles identifier = local : go (takeDirectory $ toFilePath identifier) where go "." = [fromFilePath "metadata"] go dir = fromFilePath (dir "metadata") : go (takeDirectory dir) + local = fromFilePath $ toFilePath identifier ++ ".metadata" diff --git a/src/Hakyll/Core/Provider/Metadata.hs b/src/Hakyll/Core/Provider/Metadata.hs index a54b84a..889291f 100644 --- a/src/Hakyll/Core/Provider/Metadata.hs +++ b/src/Hakyll/Core/Provider/Metadata.hs @@ -46,7 +46,7 @@ loadMetadata p identifier = do gmd <- loadGlobalMetadata p identifier - return (M.unions [md, emd, gmd], body) + return (M.unions [md, gmd], body) where normal = setVersion Nothing identifier fp = resourceFilePath p identifier @@ -154,11 +154,13 @@ loadGlobalMetadata p fp = liftM M.fromList $ loadgm fp where concatMap snd . filter (flip matches fp . fromGlob . normalise . combine dir . fst) namedMetadata :: Parser [(String, [(String, String)])] -namedMetadata = P.many namedMetadataBlock +namedMetadata = liftA2 (:) (namedMetadataBlock False) $ P.many $ namedMetadataBlock True -namedMetadataBlock :: Parser (String, [(String, String)]) -namedMetadataBlock = do - name <- P.many1 (P.char '-') *> P.many inlineSpace *> P.manyTill P.anyChar newline +namedMetadataBlock :: Bool -> Parser (String, [(String, String)]) +namedMetadataBlock isNamed = do + name <- if isNamed + then P.many1 (P.char '-') *> P.many inlineSpace *> P.manyTill P.anyChar newline + else pure "**" metadata' <- metadata P.skipMany P.space return (name, metadata') diff --git a/src/Hakyll/Core/Rules/Default.hs b/src/Hakyll/Core/Rules/Default.hs index ccca1bd..fee78c5 100644 --- a/src/Hakyll/Core/Rules/Default.hs +++ b/src/Hakyll/Core/Rules/Default.hs @@ -14,6 +14,7 @@ internalRules :: Rules () internalRules = do match "metadata" $ compile $ makeItem () match "**/metadata" $ compile $ makeItem () + match "**.metadata" $ compile $ makeItem () -------------------------------------------------------------------------------- addMetadataDependencies :: Compiler () diff --git a/tests/data/posts/metadata b/tests/data/posts/metadata index e5cd153..4a1be71 100644 --- a/tests/data/posts/metadata +++ b/tests/data/posts/metadata @@ -16,3 +16,5 @@ a1: 3 a2: 3 a3: 3 +--- nonexistent +a3: 0 -- cgit v1.2.3