packages feed

hs-wayland-scanner-0.1.0: src/Graphics/Wayland/Scanner/Solve.hs

{-# LANGUAGE OverloadedStrings #-}
------------------------------------------------------------------------
-- |
-- Module      :  Graphics.Wayland.Scanner.Solve
-- Copyright   :  (c) Andrea Rossato 2026
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  andrea.rossato@unitn.it
-- Stability   :  stable
-- Portability :  portable
--
-- This module exports functions to solve `Protocol` dependencies and
-- `enum` types.
------------------------------------------------------------------------
module Graphics.Wayland.Scanner.Solve where

import Data.List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Set as Set
import qualified Data.Text as T

import Graphics.Wayland.Scanner.Types

type IfaceMap = Map Name Interface

buildIfaceMap :: [Protocol] -> IfaceMap
buildIfaceMap protos =
  Map.fromList
    [ (ifaceName i, i)
    | p <- protos
    , i <- interfaces p
    ]

-- | Solve 'Protocol' dependencies and 'enum' types.
solveProtocol :: IfaceMap -> Protocol -> SolvedProtocol
solveProtocol ifaceMap (Protocol name desc ifaces) =
  let getArg (ArgObject (Typed _ n) _) = Just n
      getArg (ArgNewId  (Typed _ n)  ) = Just n
      getArg  _                        = Nothing
      getDeps (Message _ _ as _)       = mapMaybe getArg as
      collectArgs (Interface _ _ _ evs reqs _ _) =
        concatMap getDeps evs ++ concatMap getDeps reqs
      args = nub $ concatMap collectArgs ifaces
      checkArg iface =
        case Map.lookup iface ifaceMap of
          Just  i -> if ifaceProtocol i /= name then Just $ ifaceProtocol i else Nothing
          Nothing -> error $ "Unsolved external dependency: unknown interface '" <>
                     T.unpack iface <> "' (protocol '" <> T.unpack name <> "')"
  in SolvedProtocol name desc ifaces
     (nub $ mapMaybe checkArg args)
     (Map.unionsWith Set.union $ map collectEnumTypes ifaces)

collectEnumTypes :: Interface -> EnumMap
collectEnumTypes (Interface name _ _ evs reqs _ _) =
  let nsEnum e =
        case T.splitOn "." e of
          [_,_] -> e
          _     -> name <> "." <> e
      getArgEnum (ArgValue _ (TInt  (Just e))) = Just (nsEnum e, Set.singleton EInt )
      getArgEnum (ArgValue _ (TUint (Just e))) = Just (nsEnum e, Set.singleton EUint)
      getArgEnum  _                            = Nothing
      getEnum (Message _ _ as _) = mapMaybe getArgEnum as
      collectedEnums = nub $ concatMap getEnum evs ++ concatMap getEnum reqs
   in Map.fromListWith Set.union collectedEnums