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 #-}
  {-# OPTIONS_GHC -Wno-overlapping-patterns #-}
  
  module Glean.Glass.Search.Thrift
    ( {- instances -}
    ) where
  
  import Data.Text ( Text )
  
  import Glean.Angle as Angle
      ( AngleVars(vars),
        Angle,
        predicate,
        where_,
        (.=),
        string,
        tuple,
        rec,
        alt,
        field,
        end,
        wild,
        asPredicate,
        sig,
        AngleStatement
        )
  
  import Glean.Glass.Search.Class
  import Glean.Glass.Query ( entityLocation )
  import Glean.Glass.Utils ( joinFragments )
  
  import Glean.Angle (Type)
  import qualified Glean.Schema.CodemarkupTypes.Types as Code
  import qualified Glean.Schema.Fbthrift.Types as Thrift
  import qualified Glean.Schema.CodeFbthrift.Types as Thrift
  import qualified Glean.Schema.Src.Types as Src
  import Glean.Haxl.Repos as Glean
  import Data.Typeable (Typeable)
  
  -- Depending on context (e.g. find-references or describe), we want to
  --  decode a symbol id into an entity with our without its location.
  --
  --  This module defines the corresponding Search instances and Angle queries
  
  instance Search (ResultLocation Thrift.Entity) where
    symbolSearch toks =
      fmap (mapResultLocation Thrift.Entity_decl) <$> symbolSearch toks
  
  instance Search Thrift.Entity where
    symbolSearch toks = fmap Thrift.Entity_decl <$> symbolSearch toks
  
  instance Search (ResultLocation Thrift.Declaration) where
    symbolSearch = symbolSearchGen searchQNameWithLoc searchThriftFileWithLoc
      searchMemberDeclWithLoc
  
  instance Search Thrift.Declaration where
    symbolSearch =
      symbolSearchGen searchQName searchThriftFile searchMemberDecl
  
  -- Resolve symbol id tokens into an entity, with or without location.
  symbolSearchGen ::
    (Typeable t, Show t, Type t, QueryRepos u)
    => (Text -> Text -> Angle t)
    -> (Text -> Angle t)
    -> (Text -> Text -> Text -> Angle t)
    -> [Text]
    -> ReposHaxl u v (SearchResult t)
  symbolSearchGen searchQName searchThriftFile searchMemberDecl toks =
    case toks of
    [] -> return $ None "Thrift.symbolSearch: empty query"
    _ -> case (init toks, last toks) of
      (pieces, name) -> do
        let path = joinFragments pieces
        result <- searchSymbolId toks $ searchQName path name
        case result of
          None{} -> case (init pieces, last pieces) of
            (morePieces, serviceName) -> do
              let path = joinFragments morePieces
              moreResult <-
                searchSymbolId toks $ searchMemberDecl path serviceName name
              case moreResult of
                None{} ->
                  searchSymbolId toks $ searchThriftFile (joinFragments toks)
                r -> return r
          r -> return r
  
  thriftFileDecl
    :: Text -> Angle Src.File -> Angle Thrift.File -> Angle Thrift.Declaration
    -> [AngleStatement]
  thriftFileDecl path file thriftFile decl =
      [
        file .= predicate @Src.File (string path),
        thriftFile .= predicate @Thrift.File file,
        decl .= sig (alt @"include_"
                (asPredicate thriftFile) :: Angle Thrift.Declaration)
      ]
  
  searchThriftFileWithLoc :: Text -> Angle (ResultLocation Thrift.Declaration)
  searchThriftFileWithLoc path = vars $ \(file :: Angle Src.File)
    (thriftFile :: Angle Thrift.File) (decl :: Angle Thrift.Declaration)
         (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text) ->
      tuple (decl, file, rangespan, lname) `where_` (
      thriftFileDecl path file thriftFile decl ++ [
        entityLocation (alt @"fbthrift" (alt @"decl" decl)) file rangespan lname
      ])
  
  searchThriftFile :: Text -> Angle Thrift.Declaration
  searchThriftFile path = vars $ \(file :: Angle Src.File)
    (thriftFile :: Angle Thrift.File) (decl :: Angle Thrift.Declaration) ->
      decl `where_` thriftFileDecl path file thriftFile decl
  
  thriftMemberDecl
    :: Text -> Text -> Text -> Angle Src.File -> Angle Thrift.File
    -> Angle Thrift.QualName -> Angle Thrift.Identifier -> Angle Thrift.XRefTarget
    -> [AngleStatement]
  thriftMemberDecl
    path typename name file thriftFile qname thriftIdentifier decl =
   [
      file .= predicate @Src.File (string path),
      thriftFile .= predicate @Thrift.File file,
      thriftIdentifier .= predicate @Thrift.Identifier (string name),
      qname .= predicate @Thrift.QualName (
        rec $
          field @"file" (asPredicate thriftFile) $
          field @"name" (string typename)
        end
      ),
      wild .= predicate @Thrift.DeclarationMember (
        rec $
          field @"qname" (asPredicate qname) $
          field @"member" (asPredicate thriftIdentifier) $
          field @"decl" decl
        end)
    ]
  
  -- Member lookup. This will find fields (struct, exception, union), enum values,
  -- and functions. They are uniquely indentified by a triple
  -- (path, typename, name)
  searchMemberDecl
    :: Text -> Text -> Text -> Angle Thrift.Declaration
  searchMemberDecl path typename name =
    vars $ \(file :: Angle Src.File) (qname :: Angle Thrift.QualName)
       (thriftFile :: Angle Thrift.File) (identifier :: Angle Thrift.Identifier)
       (decl :: Angle Thrift.Declaration) ->
    decl `where_`
      thriftMemberDecl path typename name file thriftFile qname identifier decl
  
  -- Same as searchMemberDecl but with location
  searchMemberDeclWithLoc
    :: Text -> Text -> Text -> Angle (ResultLocation Thrift.Declaration)
  searchMemberDeclWithLoc path typename name =
    vars $ \(file :: Angle Src.File)
       (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text)
       (qname :: Angle Thrift.QualName)
       (thriftFile :: Angle Thrift.File) (identifier :: Angle Thrift.Identifier)
       (decl :: Angle Thrift.Declaration) ->
  
    tuple (decl, file, rangespan, lname) `where_`
    (thriftMemberDecl path typename name file thriftFile qname identifier decl
    ++ [
      entityLocation (alt @"fbthrift" (alt @"decl" decl)) file rangespan lname
    ])
  
  qNameDecl
    :: Text -> Text -> Angle Thrift.File -> Angle Thrift.QualName
    -> Angle Src.File -> Angle Thrift.XRefTarget -> [AngleStatement]
  qNameDecl path name thriftFile qname file decl =
    [ file .= predicate @Src.File (string path),
    thriftFile .= predicate @Thrift.File file,
    qname .= predicate @Thrift.QualName (
      rec $
        field @"file" (asPredicate thriftFile) $
        field @"name" (string name)
      end
    ),
    wild .= predicate @Thrift.DeclarationName (
      rec $
        field @"qname" (asPredicate qname) $
        field @"decl" decl
      end)]
  
  -- Entity lookup: this will find type declarations (union, struct,
  -- typedef), exceptions, constants and service names, which are uniquely
  -- identified by a qualified name (path, name)
  searchQName :: Text -> Text -> Angle Thrift.Declaration
  searchQName path name =
    vars $ \(file :: Angle Src.File) (qname :: Angle Thrift.QualName)
       (thriftFile :: Angle Thrift.File) (decl :: Angle Thrift.Declaration) ->
    decl `where_` qNameDecl path name thriftFile qname file decl
  
  -- Same as searchQName but also get location
  searchQNameWithLoc :: Text -> Text -> Angle (ResultLocation Thrift.Declaration)
  searchQNameWithLoc path name =
    vars $ \(file :: Angle Src.File)
       (rangespan :: Angle Code.RangeSpan) (lname :: Angle Text)
       (qname :: Angle Thrift.QualName)
       (thriftFile :: Angle Thrift.File) (decl :: Angle Thrift.Declaration) ->
  
    tuple (decl, file, rangespan, lname) `where_`
      (qNameDecl path name thriftFile qname file decl ++ [
      entityLocation (alt @"fbthrift" (alt @"decl" decl)) file rangespan lname
    ])
  