module FP.API.ModuleName
  ( ModuleName(..)
  , toModulePath
  , mainModule
  , checkModuleName
  , fromModuleName
  , isModulePath
  , fromModulePath
  ) where
import           Prelude
import           Control.Monad (mplus, guard)
import qualified Data.Char as Char (isAlphaNum, isUpper)
import           Data.Maybe (isJust)
import           Data.Text (Text)
import           Data.Text (splitOn)
import qualified Data.Text as T
import           Data.Monoid ((<>))
import           FP.API.Types
toModulePath :: ModuleName -> Text
toModulePath (ModuleName n) = T.intercalate "/" (splitOn "." n) <> ".hs"
mainModule :: ModuleName
mainModule = ModuleName "Main"
checkModuleName :: ModuleName -> Either Text ModuleName
checkModuleName (ModuleName mn) = maybe (Left ("Invalid module name: " <> mn)) Right $ fromModuleName mn
fromModuleName :: Text -> Maybe ModuleName
fromModuleName string
    | all validModuleComponent components && not (null components) = Just $ ModuleName string
    | otherwise                                                    = Nothing
  where
    components = splitOn "." string
isModulePath :: Text -> Bool
isModulePath t = isJust $ T.stripSuffix ".hs" t `mplus` T.stripSuffix ".lhs" t
fromModulePath :: Text -> Maybe ModuleName
fromModulePath s1 = do
    s2 <- T.stripSuffix ".hs" s1
    let components = splitOn "/" s2
    guard $ all validModuleComponent components
    guard $ not (null components)
    return $ ModuleName $ T.intercalate "." components
validModuleChar :: Char -> Bool
validModuleChar c = Char.isAlphaNum c || c == '_' || c == '\''
validModuleComponent :: Text -> Bool
validModuleComponent t =
    case T.uncons t of
        Nothing -> False
        Just (c, t') -> Char.isUpper c && T.all validModuleChar t'