content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  {-# LANGUAGE TypeApplications #-}
  {-# OPTIONS_GHC -Wno-orphans -Wno-incomplete-uni-patterns #-}
  
  module Glean.Glass.Search.Buck
    ( {- instances -}
    ) where
  
  import Data.Text ( Text )
  import Data.List.Split ( splitOn )
  
  import Glean.Angle as Angle
  
  import Glean.Glass.Search.Class
      ( searchSymbolId, ResultLocation, Search(..), SearchResult(None) )
  import Glean.Glass.Query ( entityLocation )
  
  import qualified Glean.Schema.CodeBuck.Types as Buck
  import qualified Glean.Schema.CodemarkupTypes.Types as Code
  import qualified Glean.Schema.SearchBuck.Types as Buck
  import qualified Glean.Schema.Src.Types as Src
  
  import Glean.Glass.Utils as Utils
  
  
  parse :: [Text] -> Maybe (Maybe Text, Text, Text)
  parse locator = do
    let name : rest = reverse locator
    let [subdir, path] = splitOn ["PATH"] (reverse rest)
    let mSubdir = case subdir of
            [] -> Nothing
            _ -> Just $ Utils.joinFragments subdir
    return (mSubdir, Utils.joinFragments path, name)
  
  
  instance Search (ResultLocation Buck.Entity) where
    symbolSearch toks = case toks of
      ("t" : locator) -> do
        case parse locator of
          Just (subdir, path, name) ->
            searchSymbolId toks $ searchByFQN subdir path name
          Nothing -> invalid_symbol
      ("f" : path) -> do
        searchSymbolId toks $ searchFile (Utils.joinFragments path)
      ("d" : module_name) -> do
        case reverse module_name of
          name : rev_module -> searchSymbolId toks $ searchDefinition
            (Utils.joinFragments $ reverse rev_module) name
          _ -> invalid_symbol
      _ -> invalid_symbol
      where
        invalid_symbol = return $ None "Buck.symbolSearch: invalid symbol"
  
  
  searchByFQN :: Maybe Text -> Text -> Text -> Angle (ResultLocation Buck.Entity)
  searchByFQN mSubdir path name =
    vars $ \(ent :: Angle Buck.Entity) (file :: Angle Src.File)
      (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
    tuple (ent, file, rangespan, lname) `where_` [
      wild .= predicate @Buck.SearchByFQN (
        rec $
          field @"subdir" subDirQ $
          field @"path" (string path) $
          field @"name" (string name) $
          field @"entity" ent
        end),
        entityLocation (alt @"buck" ent) file rangespan lname
    ]
    where
      subDirQ = case mSubdir of
        Just str -> just (string str)
        Nothing -> nothing
  
  searchFile :: Text -> Angle (ResultLocation Buck.Entity)
  searchFile path =
    vars $ \(ent :: Angle Buck.Entity) (file :: Angle Src.File)
      (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
    tuple (ent, file, rangespan, lname) `where_` [
      wild .= predicate @Buck.SearchFile (
        rec $
          field @"file" (string path) $
          field @"entity" ent
        end),
        entityLocation (alt @"buck" ent) file rangespan lname
    ]
  
  searchDefinition :: Text -> Text -> Angle (ResultLocation Buck.Entity)
  searchDefinition module_ name =
    vars $ \(ent :: Angle Buck.Entity) (file :: Angle Src.File)
      (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
    tuple (ent, file, rangespan, lname) `where_` [
      wild .= predicate @Buck.SearchDefinition (
        rec $
          field @"module" (string module_) $
          field @"name" (string name) $
          field @"entity" ent
        end),
        entityLocation (alt @"buck" ent) file rangespan lname
    ]
  