#!/usr/bin/env python3

# This file is the Semgrep CLI entry point of the Semgrep pip package,
# the Semgrep HomeBrew package, and the Semgrep Docker container.
#
# In the futur we may have different entry points when packaging Semgrep
# with Cargo, Npm, Opam, or even with Docker.
#
# The main purpose of this small wrapper is to dispatch
# either to the legacy pysemgrep (see the pysemgrep script in this
# directory), or to the new osemgrep (accessible via the semgrep-core binary
# under cli/src/semgrep/bin/ or somewhere in the PATH).
#
# It would be faster and cleaner to have a Bash script instead of a Python
# script here, but actually the overhead of Python here is just 0.015s.
# Moreover, it is sometimes hard from a Bash script to find where is installed
# semgrep-core, but it is simple from Python because you can simply use
# importlib.resources. We could also use 'pip show semgrep' from a Bash script
# to find semgrep-core, but will 'pip' be in the PATH? Should we use 'pip' or 'pip3'?
# Again, it is simpler to use a Python script and leverage importlib.resources.
# Another alternative would be to always have semgrep-core in the PATH,
# but when trying to put this binary in cli/bin, setuptools is yelling
# and does not know what to do with it. In the end, it is simpler to use a *Python*
# script when installed via a *Python* package manager (pip).

import os
import sys
import importlib.resources
import shutil

#alt: you can also add '-W ignore::DeprecationWarning' after the python3 above,
# but setuptools and pip adjust this line when installing semgrep so we need
# this instead.
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

# Add the directory containing this script in the PATH, so the pysemgrep
# script will also be in the PATH.
# Some people don't have semgrep in their PATH and call it instead
# explicitely as in /path/to/somewhere/bin/semgrep, but this means
# the execvp further below would fail without the line below.
os.environ["PATH"] += os.pathsep + os.path.dirname(os.path.abspath(__file__))
           
# similar to cli/src/semgrep/semgrep_core.py compute_executable_path()
def find_semgrep_core_path():
    # First, try the packaged binary.
    try:
        # the use of .path causes a DeprecationWarning hence the filterwarnings above
        with importlib.resources.path("semgrep.bin", "semgrep-core") as path:
            if path.is_file():
                return str(path)
    except FileNotFoundError as e:
        pass

    # Second, try in PATH. In certain context such as Homebrew
    # (see https://github.com/Homebrew/homebrew-core/blob/master/Formula/semgrep.rb)
    # or Docker (see ../../Dockerfile), we actually copy semgrep-core in
    # /usr/local/bin (or in a bin/ folder in the PATH). In those cases,
    # there are no /.../site-packages/semgrep-xxx/bin/semgrep-core.
    # In those cases, we want to grab semgrep-core from the PATH instead.
    path = shutil.which("semgrep-core")
    if path is not None:
        return path
 
    print(f"Failed to find semgrep-core (needed for --experimental) in PATH or in the semgrep package.",
          file=sys.stderr)
    # fatal error, see src/osemgrep/core/Exit_code.ml
    sys.exit(2)
       

if "--experimental" in sys.argv:
    #alt: we could  extend osemgrep (and pysemgrep) to accept a new --experimental
    # flag instead of removing the flag below. However, in osemgrep we do a few
    # pattern matching of Sys.argv and if we have 'osemgrep --experimental --help'
    # this would be interpreted as 'osemgrep scan --experimental --help' (because
    # scan is the default command), and we would not get the simple semgrep --help
    # message showing all the subcommands. So simpler to just remove --experimental.
    sys.argv.remove("--experimental")
    # We could have moved the code below in a separate 'osemgrep', like for 'pysemgrep',
    # but we don't want users to be exposed to another command, so better to hide it.
    # We expose 'pysemgrep' because osemgrep itself might need to fallback to pysemgrep
    # and it's better to avoid the possibility of an infinite loop by simply using
    # a different program name. Morever, in case of big problems, we can always
    # tell users to run pysemgrep instead of semgrep and be sure they'll get the
    # old behavior.
    path = find_semgrep_core_path()
    # If you call semgrep-core as osemgrep, then we get osemgrep behavior,
    # see src/main/Main.ml
    sys.argv[0] = "osemgrep"
    os.execvp(str(path), sys.argv)
else:
    os.execvp("pysemgrep", sys.argv)
