packages feed

nirum-0.2.0: src/Nirum/Constructs/Annotation.hs

module Nirum.Constructs.Annotation ( Annotation(Annotation)
                                   , AnnotationSet
                                   , Metadata
                                   , NameDuplication(AnnotationNameDuplication)
                                   , annotations
                                   , docs
                                   , empty
                                   , fromList
                                   , insertDocs
                                   , lookup
                                   , lookupDocs
                                   , singleton
                                   , toCode
                                   , toList
                                   , union
                                   ) where

import Prelude hiding (lookup)

import qualified Data.Map.Strict as M
import qualified Data.Set as S

import Nirum.Constructs (Construct (toCode))
import Nirum.Constructs.Annotation.Internal
import Nirum.Constructs.Docs (Docs (Docs), annotationDocsName, toText)
import Nirum.Constructs.Identifier (Identifier)


docs :: Docs -> Annotation
docs (Docs d) = Annotation { name = annotationDocsName, metadata = Just d }

data NameDuplication = AnnotationNameDuplication Identifier
                     deriving (Eq, Ord, Show)

empty :: AnnotationSet
empty = AnnotationSet { annotations = M.empty }

singleton :: Annotation -> AnnotationSet
singleton Annotation { name = name', metadata = metadata' } =
    AnnotationSet { annotations = M.singleton name' metadata' }

fromList :: [Annotation] -> Either NameDuplication AnnotationSet
fromList annotations' =
    case findDup names S.empty of
        Just duplication -> Left (AnnotationNameDuplication duplication)
        _ -> Right $ AnnotationSet ( M.fromList [ (name a, metadata a)
                                                | a <- annotations'
                                                ]
                                   )
  where
    names :: [Identifier]
    names = [name a | a <- annotations']
    findDup :: [Identifier] -> S.Set Identifier -> Maybe Identifier
    findDup identifiers dups =
        case identifiers of
            x:xs -> if x `S.member` dups
                    then Just x
                    else findDup xs $ S.insert x dups
            _ -> Nothing

toList :: AnnotationSet -> [Annotation]
toList AnnotationSet { annotations = annotations' } =
    map fromTuple $ M.assocs annotations'

union :: AnnotationSet -> AnnotationSet -> AnnotationSet
union (AnnotationSet a) (AnnotationSet b) = AnnotationSet $ M.union a b

lookup :: Identifier -> AnnotationSet -> Maybe Annotation
lookup id' (AnnotationSet anno) = do
    metadata' <- M.lookup id' anno
    return Annotation { name = id', metadata = metadata' }

lookupDocs :: AnnotationSet -> Maybe Docs
lookupDocs annotationSet = do
    Annotation _ m <- lookup annotationDocsName annotationSet
    data' <- m
    return $ Docs data'

insertDocs :: (Monad m) => Docs -> AnnotationSet -> m AnnotationSet
insertDocs docs' (AnnotationSet anno) =
    case insertLookup annotationDocsName (Just $ toText docs') anno of
        (Just _ , _    ) -> fail "<duplicated>"
        (Nothing, anno') -> return $ AnnotationSet anno'
  where
    insertLookup :: Ord k => k -> a -> M.Map k a -> (Maybe a, M.Map k a)
    insertLookup = M.insertLookupWithKey (\_ a _ -> a)