Files
n8n-openai-adapter/lib/elixir/lib/system.ex
T

356 lines
9.6 KiB
Elixir

defmodule System do
defexception NoHomeError,
message: "could not find the user home, please set the HOME environment variable"
defexception NoTmpDirError,
message: "could not get a writable temporary directory, please set the TMPDIR environment variable"
defexception NoAccessCwdError,
message: "could not get a current working directory, the current location is not accessible"
@moduledoc """
The System module provides access to some variables used or
maintained by the VM and to functions that interact strongly
with the VM or the host system.
"""
defp strip_re(iodata, pattern) do
:re.replace(iodata, pattern, "", [return: :binary])
end
defp read_stripped(path) do
case :file.read_file(path) do
{ :ok, binary } ->
strip_re(binary, "^\s+|\s+$")
_ -> ""
end
end
# Read and strip the version from the `VERSION` file.
defmacrop get_version do
case read_stripped("VERSION") do
"" -> raise CompileError, message: "could not read the version number from VERSION"
data -> data
end
end
# Tries to run `git describe --always --tags`. In the case of success returns
# the most recent tag. If that is not available, tries to read the commit hash
# from .git/HEAD. If that fails, returns an empty string.
defmacrop get_describe do
dirpath = ".git"
case :file.read_file_info(dirpath) do
{ :ok, _ } ->
if :os.find_executable('git') do
data = :os.cmd('git describe --always --tags')
strip_re(data, "\n")
else
read_stripped(:filename.join(".git", "HEAD"))
end
_ -> ""
end
end
# Get the date at compilation time.
defmacrop get_date do
list_to_binary :httpd_util.rfc1123_date
end
@doc """
Returns Elixir's version as binary.
"""
@spec version() :: String.t
def version, do: get_version
@doc """
Returns a keywords list with version, git tag info and date.
"""
@spec build_info() :: Keyword.t
def build_info do
[version: version, tag: get_describe, date: get_date]
end
@doc """
Returns the list of command-line arguments passed to the program.
"""
@spec argv() :: [String.t]
def argv do
:elixir_code_server.call :argv
end
@doc """
Changes the list of command-line arguments. Use it with caution,
as it destory any previous argv information.
"""
@spec argv([String.t]) :: :ok
def argv(argv) do
:elixir_code_server.cast({ :argv, argv })
end
@doc """
Returns the current working directory or `nil` if one
is not available.
"""
def cwd do
case :file.get_cwd do
{ :ok, list } -> String.from_char_list!(list)
_ -> nil
end
end
@doc """
Returns the current working directory or raises `System.NoAccessCwdError`.
"""
def cwd! do
cwd || raise NoAccessCwdError
end
@doc """
Returns the user home (platform independent).
It returns `nil` if no user home is set.
"""
def user_home do
case :os.type() do
{ :win32, _ } -> get_windows_home
_ -> get_unix_home
end
end
@doc """
Same as `user_home/0` but raises `System.NoHomeError`
instead of returning `nil` if no user home is set.
"""
def user_home! do
user_home || raise NoHomeError
end
defp get_unix_home do
get_env("HOME")
end
defp get_windows_home do
:filename.absname(
get_env("USERPROFILE") || (
hd = get_env("HOMEDRIVE")
hp = get_env("HOMEPATH")
hd && hp && hd <> hp
)
)
end
@doc %B"""
Returns a writable temporary directory.
It searches for directories in the following order:
1. The directory named by the TMPDIR environment variable
2. The directory named by the TEMP environment variable
3. The directory named by the TMP environment variable
4. `C:\TMP` on Windows or `/tmp` on Unix
5. As a last resort, the current working directory
Returns `nil` if none of the above are writable.
"""
def tmp_dir do
write_env_tmp_dir('TMPDIR') ||
write_env_tmp_dir('TEMP') ||
write_env_tmp_dir('TMP') ||
write_tmp_dir("/tmp") ||
((cwd = cwd()) && write_tmp_dir(cwd))
end
@doc """
Same as `tmp_dir` but raises `System.NoTmpDirError`
instead of returning `nil` if no temp dir is set.
"""
def tmp_dir! do
tmp_dir || raise NoTmpDirError
end
defp write_env_tmp_dir(env) do
case :os.getenv(env) do
false -> nil
tmp -> write_tmp_dir(tmp)
end
end
defp write_tmp_dir(dir) do
case :file.read_file_info(dir) do
{:ok, info} ->
type_index = File.Stat.__record__(:index, :type)
access_index = File.Stat.__record__(:index, :access)
case { elem(info, type_index), elem(info, access_index) } do
{ :directory, access } when access in [:read_write, :write] ->
String.from_char_list!(dir)
_ -> nil
end
{ :error, _ } -> nil
end
end
@doc """
Registers a function that will be invoked
at the end of program execution. Useful for
invoking a hook in a "script" mode.
The function must expect the exit status code
as argument.
"""
def at_exit(fun) when is_function(fun, 1) do
:elixir_code_server.cast { :at_exit, fun }
end
@doc """
Executes `command` in a command shell of the target OS,
captures the standard output of the command and returns
the result as a binary.
If `command` is a char list, a char list is returned.
Returns a binary otherwise.
"""
@spec cmd(binary) :: binary
@spec cmd(char_list) :: char_list
def cmd(command) when is_list(command) do
:os.cmd(command)
end
def cmd(command) when is_binary(command) do
# Notice we don't use unicode for conversion
# because the OS is expecting and returning raw bytes
list_to_binary :os.cmd(binary_to_list(command))
end
@doc """
This function looks up an executable program given
its name using the environment variable PATH on Unix
and Windows. It also considers the proper executable
extension for each OS, so for Windows it will try to
lookup files with `.com`, `.cmd` or similar extensions.
If `program` is a char list, a char list is returned.
Returns a binary otherwise.
"""
@spec find_executable(binary) :: binary | nil
@spec find_executable(char_list) :: char_list | nil
def find_executable(program) when is_list(program) do
:os.find_executable(program) || nil
end
def find_executable(program) when is_binary(program) do
# Notice we don't use unicode for conversion
# because the OS is expecting and returning raw bytes
case :os.find_executable(binary_to_list(program)) do
false -> nil
other -> list_to_binary(other)
end
end
@doc """
Returns a list of all environment variables. Each variable is given as a
`{name, value}` tuple where both `name` and `value` are strings.
"""
@spec get_env() :: [{String.t, String.t}]
def get_env do
Enum.map(:os.getenv, fn var ->
var = String.from_char_list! var
[k, v] = String.split var, "=", global: false
{k, v}
end)
end
@doc """
Returns the value of the environment variable
`varname` as a binary, or `nil` if the environment
variable is undefined.
"""
@spec get_env(binary) :: binary | nil
def get_env(varname) when is_binary(varname) do
case :os.getenv(String.to_char_list!(varname)) do
false -> nil
other -> String.from_char_list!(other)
end
end
@doc """
Returns the process identifier of the current Erlang emulator
in the format most commonly used by the operating system environment.
See http://www.erlang.org/doc/man/os.html#getpid-0 for more info.
"""
@spec get_pid() :: binary
def get_pid, do: list_to_binary(:os.getpid)
@doc """
Sets a new `value` for the environment variable `varname`.
"""
@spec put_env(binary, binary) :: :ok
def put_env(varname, value) when is_binary(varname) and is_binary(value) do
:os.putenv binary_to_list(varname), String.to_char_list!(value)
:ok
end
@doc """
Sets a new value for each environment variable corresponding
to each key in `dict`.
"""
@spec put_env(Dict.t) :: :ok
def put_env(dict) do
Enum.each dict, fn {key, val} -> put_env key, val end
end
@doc """
Gets Elixir's stacktrace.
Notice the Erlang VM (and therefore this function) does not
return the current stacktrace but rather the stacktrace of the
latest exception.
"""
def stacktrace do
filter_stacktrace :erlang.get_stacktrace
end
@doc """
Halts the Erlang runtime system where the first argument status must be a
non-negative integer, the atom `:abort` or any type that can be converted
to a char list.
* If an integer, the runtime system exits with the integer value which
is returned to the Operating System;
* If `:abort`, the runtime system aborts producing a core dump, if that is
enabled in the operating system;
* If a char list, an erlang crash dump is produced with status as slogan,
and then the runtime system exits with status code 1;
Note that on many platforms, only the status codes 0-255 are supported
by the operating system.
For more information, check: http://www.erlang.org/doc/man/erlang.html#halt-1
## Examples
System.halt(0)
System.halt(1)
System.halt(:abort)
"""
@spec halt() :: no_return
@spec halt(non_neg_integer | binary | :abort) :: no_return
def halt(status // 0)
def halt(status) when is_integer(status) or status == :abort do
:erlang.halt(status)
end
def halt(status) when is_binary(status) do
:erlang.halt(binary_to_list(status))
end
## Helpers
defp filter_stacktrace([{ Kernel, :raise, _, _ }|t]), do: t
defp filter_stacktrace(t), do: t
end