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, ApplicativeDo, AllowAmbiguousTypes #-}
  {-# OPTIONS_GHC -Wno-orphans -Wno-incomplete-uni-patterns #-}
  
  module Glean.Glass.Search.Hack
    ( {- instances -}
    ) where
  
  import Data.Text ( Text )
  
  import Glean.Angle as Angle
  
  import Glean.Glass.Search.Class
  import Glean.Glass.Query ( entityLocation )
  
  import qualified Glean.Schema.CodeHack.Types as Hack
  import qualified Glean.Schema.CodemarkupTypes.Types as Code
  import qualified Glean.Schema.Hack.Types as Hack
  import qualified Glean.Schema.SearchHack.Types as Hack
  import qualified Glean.Schema.Src.Types as Src
  
  instance Search (ResultLocation Hack.Entity) where
    symbolSearch toks =
      fmap (mapResultLocation Hack.Entity_decl) <$> symbolSearch toks
  
  instance Search (ResultLocation Hack.Declaration) where
    symbolSearch t@("ns":[name]) = searchSymbolId t $ searchNamespace [] name
    symbolSearch ("ns":t@(_:_:_)) = do -- 2 or more path elements
      let name:rest = reverse t
      searchSymbolId t $ searchNamespace rest name
  
    symbolSearch ("fun":t@(_:_)) = do
      let name:rest = reverse t
      searchSymbolId t $ searchFunctionInNamespace rest name
  
    symbolSearch t@("module":[name]) = searchSymbolId t $ searchModule name
  
    symbolSearch ("const":t@(_:_)) = do -- 2 or more path elements
      let name:rest = reverse t
      searchSymbolId t $ searchGlobalConstInNamespace rest name
  
    symbolSearch t@[name] = searchSymbolId t $ searchTypeInNamespace [] name
    symbolSearch t@(_:_) = -- 2 or more path elements
      let (name:rest) = reverse t in
      case rest of
        ":prop":context:ns ->
           searchSymbolId t $ searchPropertyInContainer ns context name
        context:ns -> do
          a <- searchSymbolId t $ searchTypeInNamespace rest name
          b <- searchSymbolId t $
            searchInContainerOrEnumNoProperty ns context name
  
          return $ case (a,b) of
            (x@One{}, _) -> x
            (_, y@One{}) -> y
            (x@Many{}, _) -> x
            (_, y@Many{}) -> y
            (x@None{}, None{}) -> x
        [] -> fail "rest has at least one element"
  
    symbolSearch [] = return $ None "Hack.symbolSearch: empty path"
  
  searchTypeInNamespace
    :: [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchTypeInNamespace = searchTypeInNamespace_ False
  
  searchFunctionInNamespace
    :: [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchFunctionInNamespace = searchFunctionInNamespace_ False
  
  searchModule :: Text -> Angle (ResultLocation Hack.Declaration)
  searchModule = searchModule_ False
  
  searchGlobalConstInNamespace
    :: [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchGlobalConstInNamespace = searchGlobalConstInNamespace_ False
  
  searchPropertyInContainer
    :: [Text] -> Text -> Text -> Angle (ResultLocation Hack.Declaration)
  searchPropertyInContainer = searchPropertyInContainer_ False
  
  searchInContainerOrEnumNoProperty
    :: [Text] -> Text -> Text -> Angle (ResultLocation Hack.Declaration)
  searchInContainerOrEnumNoProperty = searchInContainerOrEnumNoProperty_ False
  
  searchFunctionInNamespace_
    :: Bool -> [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchFunctionInNamespace_ isPrefix ns name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl,file,rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchFunctionInNamespace (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"namespace_" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchModule_
    :: Bool -> Text -> Angle (ResultLocation Hack.Declaration)
  searchModule_ isPrefix name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl,file,rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchModule (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchGlobalConstInNamespace_
    :: Bool -> [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchGlobalConstInNamespace_ isPrefix ns name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl,file,rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchGlobalConstInNamespace (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"namespace_" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchTypeInNamespace_
    :: Bool -> [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchTypeInNamespace_ isPrefix ns name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl,file,rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchTypeInNamespace (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"namespace_" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchNamespace :: [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchNamespace = searchNamespace_ False
  
  searchNamespace_
    :: Bool -> [Text] -> Text -> Angle (ResultLocation Hack.Declaration)
  searchNamespace_ isPrefix ns name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl,file,rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchNamespace (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"namespace_" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchInContainerOrEnumNoProperty_
    :: Bool -> [Text] -> Text -> Text -> Angle (ResultLocation Hack.Declaration)
  searchInContainerOrEnumNoProperty_ isPrefix ns context name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl, file, rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchInContainerOrEnumNoProperty (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"contextName" (string context) $
            field @"contextNamespace" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  searchPropertyInContainer_
    :: Bool -> [Text] -> Text -> Text -> Angle (ResultLocation Hack.Declaration)
  searchPropertyInContainer_ isPrefix ns container name =
    vars $ \(decl :: Angle Hack.Declaration) (file :: Angle Src.File)
        (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl, file, rangespan, lname) `where_` [
        wild .= predicate @Hack.SearchPropertyInContainer (
          rec $
            field @"name" (stringOrPrefix isPrefix name) $
            field @"containerName" (string container) $
            field @"containerNamespace" (namespaceQName ns) $
            field @"decl" decl
          end),
        entityLocation (alt @"hack" (alt @"decl" decl)) file rangespan lname
      ]
  
  namespaceQName :: [Text] -> Angle (Maybe Hack.NamespaceQName)
  namespaceQName [] = nothing
  namespaceQName (n:ns) = just $ predicate $
    rec $
      field @"name" (string n) $
      field @"parent" (namespaceQName ns)
    end
  
  stringOrPrefix :: Bool -> Text -> Angle Text
  stringOrPrefix isPrefix
    | isPrefix = stringPrefix
    | otherwise = string
  