module Data.GI.CodeGen.Fixups
( dropMovedItems
, guessPropertyNullability
, detectGObject
, dropDuplicatedFields
, checkClosureDestructors
, fixSymbolNaming
) where
import Data.Char (generalCategory, GeneralCategory(UppercaseLetter))
import Data.Maybe (isNothing, isJust)
#if !MIN_VERSION_base(4,13,0)
import Data.Monoid ((<>))
#endif
import qualified Data.Set as S
import qualified Data.Text as T
import Data.GI.CodeGen.API
dropMovedItems :: API -> Maybe API
dropMovedItems :: API -> Maybe API
dropMovedItems (APIFunction Function
f) = if Function -> Maybe Text
fnMovedTo Function
f Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Text
forall a. Maybe a
Nothing
then API -> Maybe API
forall a. a -> Maybe a
Just (Function -> API
APIFunction Function
f)
else Maybe API
forall a. Maybe a
Nothing
dropMovedItems (APIInterface Interface
i) =
(API -> Maybe API
forall a. a -> Maybe a
Just (API -> Maybe API) -> (Interface -> API) -> Interface -> Maybe API
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Interface -> API
APIInterface) Interface
i {ifMethods :: [Method]
ifMethods = [Method] -> [Method]
filterMovedMethods (Interface -> [Method]
ifMethods Interface
i)}
dropMovedItems (APIObject Object
o) =
(API -> Maybe API
forall a. a -> Maybe a
Just (API -> Maybe API) -> (Object -> API) -> Object -> Maybe API
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Object -> API
APIObject) Object
o {objMethods :: [Method]
objMethods = [Method] -> [Method]
filterMovedMethods (Object -> [Method]
objMethods Object
o)}
dropMovedItems (APIStruct Struct
s) =
(API -> Maybe API
forall a. a -> Maybe a
Just (API -> Maybe API) -> (Struct -> API) -> Struct -> Maybe API
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Struct -> API
APIStruct) Struct
s {structMethods :: [Method]
structMethods = [Method] -> [Method]
filterMovedMethods (Struct -> [Method]
structMethods Struct
s)}
dropMovedItems (APIUnion Union
u) =
(API -> Maybe API
forall a. a -> Maybe a
Just (API -> Maybe API) -> (Union -> API) -> Union -> Maybe API
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Union -> API
APIUnion) Union
u {unionMethods :: [Method]
unionMethods = [Method] -> [Method]
filterMovedMethods (Union -> [Method]
unionMethods Union
u)}
dropMovedItems API
a = API -> Maybe API
forall a. a -> Maybe a
Just API
a
filterMovedMethods :: [Method] -> [Method]
filterMovedMethods :: [Method] -> [Method]
filterMovedMethods = (Method -> Bool) -> [Method] -> [Method]
forall a. (a -> Bool) -> [a] -> [a]
filter (Maybe Text -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe Text -> Bool) -> (Method -> Maybe Text) -> Method -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Method -> Maybe Text
methodMovedTo)
guessPropertyNullability :: (Name, API) -> (Name, API)
guessPropertyNullability :: (Name, API) -> (Name, API)
guessPropertyNullability (Name
n, APIObject Object
obj) =
(Name
n, Object -> API
APIObject (Object -> Object
guessObjectPropertyNullability Object
obj))
guessPropertyNullability (Name
n, APIInterface Interface
iface) =
(Name
n, Interface -> API
APIInterface (Interface -> Interface
guessInterfacePropertyNullability Interface
iface))
guessPropertyNullability (Name, API)
other = (Name, API)
other
guessObjectPropertyNullability :: Object -> Object
guessObjectPropertyNullability :: Object -> Object
guessObjectPropertyNullability Object
obj =
Object
obj {objProperties :: [Property]
objProperties = (Property -> Property) -> [Property] -> [Property]
forall a b. (a -> b) -> [a] -> [b]
map ([Method] -> Property -> Property
guessNullability (Object -> [Method]
objMethods Object
obj))
(Object -> [Property]
objProperties Object
obj)}
guessInterfacePropertyNullability :: Interface -> Interface
guessInterfacePropertyNullability :: Interface -> Interface
guessInterfacePropertyNullability Interface
iface =
Interface
iface {ifProperties :: [Property]
ifProperties = (Property -> Property) -> [Property] -> [Property]
forall a b. (a -> b) -> [a] -> [b]
map ([Method] -> Property -> Property
guessNullability (Interface -> [Method]
ifMethods Interface
iface))
(Interface -> [Property]
ifProperties Interface
iface)}
guessNullability :: [Method] -> Property -> Property
guessNullability :: [Method] -> Property -> Property
guessNullability [Method]
methods = [Method] -> Property -> Property
guessReadNullability [Method]
methods
(Property -> Property)
-> (Property -> Property) -> Property -> Property
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Method] -> Property -> Property
guessWriteNullability [Method]
methods
guessReadNullability :: [Method] -> Property -> Property
guessReadNullability :: [Method] -> Property -> Property
guessReadNullability [Method]
methods Property
p
| Maybe Bool -> Bool
forall a. Maybe a -> Bool
isJust (Property -> Maybe Bool
propReadNullable Property
p) = Property
p
| Bool
otherwise = Property
p {propReadNullable :: Maybe Bool
propReadNullable = Maybe Bool
nullableGetter}
where
nullableGetter :: Maybe Bool
nullableGetter :: Maybe Bool
nullableGetter =
let prop_name :: Text
prop_name = HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
"-" Text
"_" (Property -> Text
propName Property
p)
in case [Method] -> Text -> Maybe Method
findMethod [Method]
methods (Text
"get_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
prop_name) of
Maybe Method
Nothing -> Maybe Bool
forall a. Maybe a
Nothing
Just Method
m ->
let c :: Callable
c = Method -> Callable
methodCallable Method
m
in if [Arg] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Callable -> [Arg]
args Callable
c) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 Bool -> Bool -> Bool
&&
Callable -> Maybe Type
returnType Callable
c Maybe Type -> Maybe Type -> Bool
forall a. Eq a => a -> a -> Bool
== Type -> Maybe Type
forall a. a -> Maybe a
Just (Property -> Type
propType Property
p) Bool -> Bool -> Bool
&&
Callable -> Transfer
returnTransfer Callable
c Transfer -> Transfer -> Bool
forall a. Eq a => a -> a -> Bool
== Transfer
TransferNothing Bool -> Bool -> Bool
&&
Callable -> Bool
skipReturn Callable
c Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False Bool -> Bool -> Bool
&&
Callable -> Bool
callableThrows Callable
c Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False Bool -> Bool -> Bool
&&
Method -> MethodType
methodType Method
m MethodType -> MethodType -> Bool
forall a. Eq a => a -> a -> Bool
== MethodType
OrdinaryMethod Bool -> Bool -> Bool
&&
Method -> Maybe Text
methodMovedTo Method
m Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Text
forall a. Maybe a
Nothing
then Bool -> Maybe Bool
forall a. a -> Maybe a
Just (Callable -> Bool
returnMayBeNull Callable
c)
else Maybe Bool
forall a. Maybe a
Nothing
guessWriteNullability :: [Method] -> Property -> Property
guessWriteNullability :: [Method] -> Property -> Property
guessWriteNullability [Method]
methods Property
p
| Maybe Bool -> Bool
forall a. Maybe a -> Bool
isJust (Property -> Maybe Bool
propWriteNullable Property
p) = Property
p
| Bool
otherwise = Property
p {propWriteNullable :: Maybe Bool
propWriteNullable = Maybe Bool
nullableSetter}
where
nullableSetter :: Maybe Bool
nullableSetter :: Maybe Bool
nullableSetter =
let prop_name :: Text
prop_name = HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
"-" Text
"_" (Property -> Text
propName Property
p)
in case [Method] -> Text -> Maybe Method
findMethod [Method]
methods (Text
"set_" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
prop_name) of
Maybe Method
Nothing -> Maybe Bool
forall a. Maybe a
Nothing
Just Method
m ->
let c :: Callable
c = Method -> Callable
methodCallable Method
m
in if [Arg] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Callable -> [Arg]
args Callable
c) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
2 Bool -> Bool -> Bool
&&
(Arg -> Type
argType (Arg -> Type) -> (Callable -> Arg) -> Callable -> Type
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Arg] -> Arg
forall a. HasCallStack => [a] -> a
last ([Arg] -> Arg) -> (Callable -> [Arg]) -> Callable -> Arg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Callable -> [Arg]
args) Callable
c Type -> Type -> Bool
forall a. Eq a => a -> a -> Bool
== Property -> Type
propType Property
p Bool -> Bool -> Bool
&&
Callable -> Maybe Type
returnType Callable
c Maybe Type -> Maybe Type -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Type
forall a. Maybe a
Nothing Bool -> Bool -> Bool
&&
(Arg -> Transfer
transfer (Arg -> Transfer) -> (Callable -> Arg) -> Callable -> Transfer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Arg] -> Arg
forall a. HasCallStack => [a] -> a
last ([Arg] -> Arg) -> (Callable -> [Arg]) -> Callable -> Arg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Callable -> [Arg]
args) Callable
c Transfer -> Transfer -> Bool
forall a. Eq a => a -> a -> Bool
== Transfer
TransferNothing Bool -> Bool -> Bool
&&
(Arg -> Direction
direction (Arg -> Direction) -> (Callable -> Arg) -> Callable -> Direction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Arg] -> Arg
forall a. HasCallStack => [a] -> a
last ([Arg] -> Arg) -> (Callable -> [Arg]) -> Callable -> Arg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Callable -> [Arg]
args) Callable
c Direction -> Direction -> Bool
forall a. Eq a => a -> a -> Bool
== Direction
DirectionIn Bool -> Bool -> Bool
&&
Method -> Maybe Text
methodMovedTo Method
m Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Text
forall a. Maybe a
Nothing Bool -> Bool -> Bool
&&
Method -> MethodType
methodType Method
m MethodType -> MethodType -> Bool
forall a. Eq a => a -> a -> Bool
== MethodType
OrdinaryMethod Bool -> Bool -> Bool
&&
Callable -> Bool
callableThrows Callable
c Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False
then Bool -> Maybe Bool
forall a. a -> Maybe a
Just ((Arg -> Bool
mayBeNull (Arg -> Bool) -> (Callable -> Arg) -> Callable -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Arg] -> Arg
forall a. HasCallStack => [a] -> a
last ([Arg] -> Arg) -> (Callable -> [Arg]) -> Callable -> Arg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Callable -> [Arg]
args) Callable
c)
else Maybe Bool
forall a. Maybe a
Nothing
findMethod :: [Method] -> T.Text -> Maybe Method
findMethod :: [Method] -> Text -> Maybe Method
findMethod [Method]
methods Text
n = case (Method -> Bool) -> [Method] -> [Method]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
n) (Text -> Bool) -> (Method -> Text) -> Method -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Name -> Text
name (Name -> Text) -> (Method -> Name) -> Method -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Method -> Name
methodName) [Method]
methods of
[Method
m] -> Method -> Maybe Method
forall a. a -> Maybe a
Just Method
m
[Method]
_ -> Maybe Method
forall a. Maybe a
Nothing
detectGObject :: (Name, API) -> (Name, API)
detectGObject :: (Name, API) -> (Name, API)
detectGObject (Name
n, APIInterface Interface
iface) =
if Bool -> Bool
not ([Property] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Interface -> [Property]
ifProperties Interface
iface) Bool -> Bool -> Bool
&& [Signal] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Interface -> [Signal]
ifSignals Interface
iface))
then let gobject :: Name
gobject = Text -> Text -> Name
Name Text
"GObject" Text
"Object"
in if Name
gobject Name -> [Name] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (Interface -> [Name]
ifPrerequisites Interface
iface)
then (Name
n, Interface -> API
APIInterface Interface
iface)
else (Name
n, Interface -> API
APIInterface (Interface
iface {ifPrerequisites :: [Name]
ifPrerequisites =
Name
gobject Name -> [Name] -> [Name]
forall a. a -> [a] -> [a]
: Interface -> [Name]
ifPrerequisites Interface
iface}))
else (Name
n, Interface -> API
APIInterface Interface
iface)
detectGObject (Name, API)
api = (Name, API)
api
dropDuplicatedEnumFields :: Enumeration -> Enumeration
dropDuplicatedEnumFields :: Enumeration -> Enumeration
dropDuplicatedEnumFields Enumeration
enum =
Enumeration
enum{enumMembers :: [EnumerationMember]
enumMembers = Set Text -> [EnumerationMember] -> [EnumerationMember]
dropDuplicates Set Text
forall a. Set a
S.empty (Enumeration -> [EnumerationMember]
enumMembers Enumeration
enum)}
where dropDuplicates :: S.Set T.Text -> [EnumerationMember] -> [EnumerationMember]
dropDuplicates :: Set Text -> [EnumerationMember] -> [EnumerationMember]
dropDuplicates Set Text
_ [] = []
dropDuplicates Set Text
previous (EnumerationMember
m:[EnumerationMember]
ms) =
if EnumerationMember -> Text
enumMemberName EnumerationMember
m Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
`S.member` Set Text
previous
then Set Text -> [EnumerationMember] -> [EnumerationMember]
dropDuplicates Set Text
previous [EnumerationMember]
ms
else EnumerationMember
m EnumerationMember -> [EnumerationMember] -> [EnumerationMember]
forall a. a -> [a] -> [a]
: Set Text -> [EnumerationMember] -> [EnumerationMember]
dropDuplicates (Text -> Set Text -> Set Text
forall a. Ord a => a -> Set a -> Set a
S.insert (EnumerationMember -> Text
enumMemberName EnumerationMember
m) Set Text
previous) [EnumerationMember]
ms
dropDuplicatedFields :: (Name, API) -> (Name, API)
dropDuplicatedFields :: (Name, API) -> (Name, API)
dropDuplicatedFields (Name
n, APIFlags (Flags Enumeration
enum)) =
(Name
n, Flags -> API
APIFlags (Enumeration -> Flags
Flags (Enumeration -> Flags) -> Enumeration -> Flags
forall a b. (a -> b) -> a -> b
$ Enumeration -> Enumeration
dropDuplicatedEnumFields Enumeration
enum))
dropDuplicatedFields (Name
n, API
api) = (Name
n, API
api)
checkClosureDestructors :: (Name, API) -> (Name, API)
checkClosureDestructors :: (Name, API) -> (Name, API)
checkClosureDestructors (Name
n, APIObject Object
o) =
(Name
n, Object -> API
APIObject (Object
o {objMethods :: [Method]
objMethods = [Method] -> [Method]
checkMethodDestructors (Object -> [Method]
objMethods Object
o)}))
checkClosureDestructors (Name
n, APIInterface Interface
i) =
(Name
n, Interface -> API
APIInterface (Interface
i {ifMethods :: [Method]
ifMethods = [Method] -> [Method]
checkMethodDestructors (Interface -> [Method]
ifMethods Interface
i)}))
checkClosureDestructors (Name
n, APIStruct Struct
s) =
(Name
n, Struct -> API
APIStruct (Struct
s {structMethods :: [Method]
structMethods = [Method] -> [Method]
checkMethodDestructors (Struct -> [Method]
structMethods Struct
s)}))
checkClosureDestructors (Name
n, APIUnion Union
u) =
(Name
n, Union -> API
APIUnion (Union
u {unionMethods :: [Method]
unionMethods = [Method] -> [Method]
checkMethodDestructors (Union -> [Method]
unionMethods Union
u)}))
checkClosureDestructors (Name
n, APIFunction Function
f) =
(Name
n, Function -> API
APIFunction (Function
f {fnCallable :: Callable
fnCallable = Callable -> Callable
checkCallableDestructors (Function -> Callable
fnCallable Function
f)}))
checkClosureDestructors (Name
n, API
api) = (Name
n, API
api)
checkMethodDestructors :: [Method] -> [Method]
checkMethodDestructors :: [Method] -> [Method]
checkMethodDestructors = (Method -> Method) -> [Method] -> [Method]
forall a b. (a -> b) -> [a] -> [b]
map Method -> Method
checkMethod
where checkMethod :: Method -> Method
checkMethod :: Method -> Method
checkMethod Method
m = Method
m {methodCallable :: Callable
methodCallable =
Callable -> Callable
checkCallableDestructors (Method -> Callable
methodCallable Method
m)}
checkCallableDestructors :: Callable -> Callable
checkCallableDestructors :: Callable -> Callable
checkCallableDestructors Callable
c = Callable
c {args :: [Arg]
args = (Arg -> Arg) -> [Arg] -> [Arg]
forall a b. (a -> b) -> [a] -> [b]
map Arg -> Arg
checkArg (Callable -> [Arg]
args Callable
c)}
where checkArg :: Arg -> Arg
checkArg :: Arg -> Arg
checkArg Arg
arg = if Arg -> Int
argDestroy Arg
arg Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
0 Bool -> Bool -> Bool
&& Arg -> Int
argClosure Arg
arg Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== -Int
1
then Arg
arg {argDestroy :: Int
argDestroy = -Int
1}
else Arg
arg
fixSymbolNaming :: (Name, API) -> (Name, API)
fixSymbolNaming :: (Name, API) -> (Name, API)
fixSymbolNaming (Name
n, APIConst Constant
c) = (Name -> Name
fixConstantName Name
n, Constant -> API
APIConst Constant
c)
fixSymbolNaming (Name
n, API
api) = (Name
n, API
api)
fixConstantName :: Name -> Name
fixConstantName :: Name -> Name
fixConstantName (Name Text
ns Text
n)
| Bool -> Bool
not (Text -> Bool
T.null Text
n) Bool -> Bool -> Bool
&& Char -> GeneralCategory
generalCategory (HasCallStack => Text -> Char
Text -> Char
T.head Text
n) GeneralCategory -> GeneralCategory -> Bool
forall a. Eq a => a -> a -> Bool
/= GeneralCategory
UppercaseLetter
= Text -> Text -> Name
Name Text
ns (Text
"C'" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
n)
| Bool
otherwise = Text -> Text -> Name
Name Text
ns Text
n