-- This file is part of Hoppy.
--
-- Copyright 2015-2021 Bryan Gardiner <bog@khumba.net>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Affero General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Affero General Public License for more details.
--
-- You should have received a copy of the GNU Affero General Public License
-- along with this program.  If not, see <http://www.gnu.org/licenses/>.

-- | Utilities for conditional compilation of parts of interfaces.
--
-- This module provides wrappers around 'Maybe' and 'catMaybes' so that you can
-- write code such as:
--
-- > myClass =
-- >   makeClass ...
-- >   [ ...ctors... ] $
-- >   collect
-- >   [ just $ mkMethod "foo" ...
-- >   , test (apiVersion >= [1, 2]) $ mkMethod "bar" ...
-- >   , test featureBaz $ mkMethod "baz" ...
-- >   ]
module Foreign.Hoppy.Generator.Version (
  -- * General filtering
  Filtered,
  collect,
  none,
  just,
  test,
  -- * C++ Standard version
  CppVersion (..),
  defaultCppVersion,
  activeCppVersion,
  ) where

import Data.Maybe (catMaybes)
import System.Environment (lookupEnv)
import System.IO (hPutStrLn, stderr)
import System.IO.Unsafe (unsafePerformIO)

-- | Placeholder 'Maybe'-like type that may be more general in the future.
type Filtered = Maybe

-- | Filters a list of 'Filtered' values down to the elements that are actually
-- present.
collect :: [Filtered a] -> [a]
collect :: [Filtered a] -> [a]
collect = [Filtered a] -> [a]
forall a. [Maybe a] -> [a]
catMaybes

-- | A 'Filtered' value that is always absent.
none :: Filtered a
none :: Filtered a
none = Filtered a
forall a. Maybe a
Nothing

-- | Returns a 'Filtered' value that is always present.
just :: a -> Filtered a
just :: a -> Filtered a
just = a -> Filtered a
forall a. a -> Maybe a
Just

-- | Returns a 'Filtered' value that is only present if the boolean is true.
test :: Bool -> a -> Filtered a
test :: Bool -> a -> Filtered a
test Bool
True = a -> Filtered a
forall a. a -> Maybe a
Just
test Bool
False = Filtered a -> a -> Filtered a
forall a b. a -> b -> a
const Filtered a
forall a. Maybe a
Nothing

-- | Versions of the C++ standard.
data CppVersion =
    Cpp1998
  | Cpp2011
  | Cpp2014
  deriving (CppVersion
CppVersion -> CppVersion -> Bounded CppVersion
forall a. a -> a -> Bounded a
maxBound :: CppVersion
$cmaxBound :: CppVersion
minBound :: CppVersion
$cminBound :: CppVersion
Bounded, Int -> CppVersion
CppVersion -> Int
CppVersion -> [CppVersion]
CppVersion -> CppVersion
CppVersion -> CppVersion -> [CppVersion]
CppVersion -> CppVersion -> CppVersion -> [CppVersion]
(CppVersion -> CppVersion)
-> (CppVersion -> CppVersion)
-> (Int -> CppVersion)
-> (CppVersion -> Int)
-> (CppVersion -> [CppVersion])
-> (CppVersion -> CppVersion -> [CppVersion])
-> (CppVersion -> CppVersion -> [CppVersion])
-> (CppVersion -> CppVersion -> CppVersion -> [CppVersion])
-> Enum CppVersion
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: CppVersion -> CppVersion -> CppVersion -> [CppVersion]
$cenumFromThenTo :: CppVersion -> CppVersion -> CppVersion -> [CppVersion]
enumFromTo :: CppVersion -> CppVersion -> [CppVersion]
$cenumFromTo :: CppVersion -> CppVersion -> [CppVersion]
enumFromThen :: CppVersion -> CppVersion -> [CppVersion]
$cenumFromThen :: CppVersion -> CppVersion -> [CppVersion]
enumFrom :: CppVersion -> [CppVersion]
$cenumFrom :: CppVersion -> [CppVersion]
fromEnum :: CppVersion -> Int
$cfromEnum :: CppVersion -> Int
toEnum :: Int -> CppVersion
$ctoEnum :: Int -> CppVersion
pred :: CppVersion -> CppVersion
$cpred :: CppVersion -> CppVersion
succ :: CppVersion -> CppVersion
$csucc :: CppVersion -> CppVersion
Enum, CppVersion -> CppVersion -> Bool
(CppVersion -> CppVersion -> Bool)
-> (CppVersion -> CppVersion -> Bool) -> Eq CppVersion
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CppVersion -> CppVersion -> Bool
$c/= :: CppVersion -> CppVersion -> Bool
== :: CppVersion -> CppVersion -> Bool
$c== :: CppVersion -> CppVersion -> Bool
Eq, Eq CppVersion
Eq CppVersion
-> (CppVersion -> CppVersion -> Ordering)
-> (CppVersion -> CppVersion -> Bool)
-> (CppVersion -> CppVersion -> Bool)
-> (CppVersion -> CppVersion -> Bool)
-> (CppVersion -> CppVersion -> Bool)
-> (CppVersion -> CppVersion -> CppVersion)
-> (CppVersion -> CppVersion -> CppVersion)
-> Ord CppVersion
CppVersion -> CppVersion -> Bool
CppVersion -> CppVersion -> Ordering
CppVersion -> CppVersion -> CppVersion
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CppVersion -> CppVersion -> CppVersion
$cmin :: CppVersion -> CppVersion -> CppVersion
max :: CppVersion -> CppVersion -> CppVersion
$cmax :: CppVersion -> CppVersion -> CppVersion
>= :: CppVersion -> CppVersion -> Bool
$c>= :: CppVersion -> CppVersion -> Bool
> :: CppVersion -> CppVersion -> Bool
$c> :: CppVersion -> CppVersion -> Bool
<= :: CppVersion -> CppVersion -> Bool
$c<= :: CppVersion -> CppVersion -> Bool
< :: CppVersion -> CppVersion -> Bool
$c< :: CppVersion -> CppVersion -> Bool
compare :: CppVersion -> CppVersion -> Ordering
$ccompare :: CppVersion -> CppVersion -> Ordering
$cp1Ord :: Eq CppVersion
Ord, Int -> CppVersion -> ShowS
[CppVersion] -> ShowS
CppVersion -> String
(Int -> CppVersion -> ShowS)
-> (CppVersion -> String)
-> ([CppVersion] -> ShowS)
-> Show CppVersion
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CppVersion] -> ShowS
$cshowList :: [CppVersion] -> ShowS
show :: CppVersion -> String
$cshow :: CppVersion -> String
showsPrec :: Int -> CppVersion -> ShowS
$cshowsPrec :: Int -> CppVersion -> ShowS
Show)

-- | The 'CppVersion' chosen when one is not explicitly requested.  This is
-- 'Cpp2011'.
defaultCppVersion :: CppVersion
defaultCppVersion :: CppVersion
defaultCppVersion = CppVersion
Cpp2011

-- | The active version of the C++ standard.  This looks to the @HOPPY_CPP_STD@
-- environment variable, and accepts the values @c++98@, @c++11@, and @c++14@,
-- which map to the corresponding 'CppVersion' values.  If a value other than
-- these is set, then a warning is printed and the default is used.  If no value
-- is set, the default is used.
--
-- This uses 'unsafePerformIO' internally and won't cope with a changing
-- environment.
activeCppVersion :: CppVersion
{-# NOINLINE activeCppVersion #-}
activeCppVersion :: CppVersion
activeCppVersion = IO CppVersion -> CppVersion
forall a. IO a -> a
unsafePerformIO (IO CppVersion -> CppVersion) -> IO CppVersion -> CppVersion
forall a b. (a -> b) -> a -> b
$ do
  Maybe String
strMaybe <- String -> IO (Maybe String)
lookupEnv String
"HOPPY_CPP_STD"
  case Maybe String
strMaybe of
    Maybe String
Nothing -> CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
defaultCppVersion
    Just String
"" -> CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
defaultCppVersion
    Just String
"c++98" -> CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
Cpp1998
    Just String
"c++11" -> CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
Cpp2011
    Just String
"c++14" -> CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
Cpp2014
    Just String
str -> do Handle -> String -> IO ()
hPutStrLn Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"Warning: Invalid HOPPY_CPP_STD value " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
forall a. Show a => a -> String
show String
str String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"."
                   CppVersion -> IO CppVersion
forall (m :: * -> *) a. Monad m => a -> m a
return CppVersion
defaultCppVersion