packages feed

synapse-cc-0.1.0.0: src/SynapseCC/Discover.hs

-- | Tool discovery - finding synapse and hub-codegen executables
module SynapseCC.Discover
  ( discoverTools
  , findTool
  , toolPathToFilePath
  ) where

import Control.Monad (filterM)
import Data.Text (Text)
import qualified Data.Text as T
import System.Directory (doesFileExist, findExecutable, getHomeDirectory)
import System.FilePath ((</>))

import SynapseCC.Types

-- ============================================================================
-- Tool Discovery
-- ============================================================================

-- | Discover all required tools
discoverTools :: Bool -> IO (Either SynapseCCError ToolLocations)
discoverTools debug = do
  when debug $ putStrLn "[*] Discovering tools..."

  synapsePath <- findTool debug "synapse" synapsePaths
  case synapsePath of
    Nothing -> pure $ Left $ ToolNotFound "synapse" synapseSuggestions
    Just synapseToolPath -> do
      when debug $ putStrLn $ "  [+] Found synapse at " ++ toolPathToFilePath synapseToolPath

      hubCodegenPath <- findTool debug "hub-codegen" hubCodegenPaths
      case hubCodegenPath of
        Nothing -> pure $ Left $ ToolNotFound "hub-codegen" hubCodegenSuggestions
        Just hubCodegenToolPath -> do
          when debug $ putStrLn $ "  [+] Found hub-codegen at " ++ toolPathToFilePath hubCodegenToolPath

          pure $ Right $ ToolLocations
            { toolSynapse = synapseToolPath
            , toolHubCodegen = hubCodegenToolPath
            }

-- | Find a tool by checking multiple locations
findTool :: Bool -> String -> (FilePath -> [FilePath]) -> IO (Maybe ToolPath)
findTool debug name pathsF = do
  home <- getHomeDirectory
  let paths = pathsF home

  when debug $ putStrLn $ "  Searching for " ++ name ++ "..."

  -- Try to find the tool in priority order
  tryPaths paths
  where
    tryPaths [] = do
      -- Last resort: check system PATH
      mbSystemPath <- findExecutable name
      case mbSystemPath of
        Just path -> do
          when debug $ putStrLn $ "    Found in PATH: " ++ path
          pure $ Just $ SystemPath path
        Nothing -> pure Nothing

    tryPaths (path:rest) = do
      exists <- doesFileExist path
      if exists
        then do
          when debug $ putStrLn $ "    Found: " ++ path
          pure $ Just $ classifyPath path
        else tryPaths rest

-- | Classify a path based on where it was found
classifyPath :: FilePath -> ToolPath
classifyPath path
  | "dist-newstyle" `elem` splitPath path = LocalDev path
  | "target" `elem` splitPath path = LocalDev path
  | ".plexus/bin" `elem` splitPath path = PlexusBin path
  | otherwise = SystemPath path
  where
    splitPath = filter (not . null) . wordsBy (== '/')
    wordsBy p s = case dropWhile p s of
      [] -> []
      s' -> let (w, s'') = break p s' in w : wordsBy p s''

-- | Convert ToolPath to FilePath
toolPathToFilePath :: ToolPath -> FilePath
toolPathToFilePath = \case
  LocalDev path   -> path
  SystemPath path -> path
  PlexusBin path  -> path

-- ============================================================================
-- Search Paths
-- ============================================================================

-- | Search paths for synapse (in priority order)
synapsePaths :: FilePath -> [FilePath]
synapsePaths home =
  [ -- Local development builds
    "../synapse/dist-newstyle/build/aarch64-linux/ghc-9.4.8/plexus-synapse-0.2.0.0/x/synapse/build/synapse/synapse"
  , "../synapse/dist-newstyle/build/x86_64-linux/ghc-9.4.8/plexus-synapse-0.2.0.0/x/synapse/build/synapse/synapse"
  , "../../synapse/dist-newstyle/build/aarch64-linux/ghc-9.4.8/plexus-synapse-0.2.0.0/x/synapse/build/synapse/synapse"
  , "../../synapse/dist-newstyle/build/x86_64-linux/ghc-9.4.8/plexus-synapse-0.2.0.0/x/synapse/build/synapse/synapse"
    -- Plexus bin directory
  , home </> ".plexus/bin/synapse"
    -- Cabal install location
  , home </> ".cabal/bin/synapse"
  ]

-- | Search paths for hub-codegen (in priority order)
hubCodegenPaths :: FilePath -> [FilePath]
hubCodegenPaths home =
  [ -- Local development builds
    "../hub-codegen/target/release/hub-codegen"
  , "../hub-codegen/target/debug/hub-codegen"
  , "../../hub-codegen/target/release/hub-codegen"
  , "../../hub-codegen/target/debug/hub-codegen"
    -- Plexus bin directory
  , home </> ".plexus/bin/hub-codegen"
    -- Cargo install location
  , home </> ".cargo/bin/hub-codegen"
  ]

-- ============================================================================
-- Suggestions
-- ============================================================================

-- | Suggestions for installing synapse
synapseSuggestions :: [Text]
synapseSuggestions =
  [ "Build synapse: cd ../synapse && cabal build"
  , "Install synapse: cabal install synapse"
  , "Add to PATH: export PATH=\"$PATH:~/.plexus/bin\""
  ]

-- | Suggestions for installing hub-codegen
hubCodegenSuggestions :: [Text]
hubCodegenSuggestions =
  [ "Build hub-codegen: cd ../hub-codegen && cargo build --release"
  , "Install hub-codegen: cargo install --path ../hub-codegen"
  , "Add to PATH: export PATH=\"$PATH:~/.plexus/bin\""
  ]

-- ============================================================================
-- Helpers
-- ============================================================================

when :: Applicative f => Bool -> f () -> f ()
when True action = action
when False _ = pure ()