Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsafe & Async Helper Proof of Concept #516

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/nostrum/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ defmodule Nostrum.Api do
alias Nostrum.Struct.Guild.{AuditLog, AuditLogEntry, Member, Role, ScheduledEvent}
alias Nostrum.Shard.{Session, Supervisor}

use Nostrum.UnsafeHelpers,
handler: :bangify,
docs: true

@typedoc """
Represents a failed response from the API.

Expand Down Expand Up @@ -162,6 +166,7 @@ defmodule Nostrum.Api do
- `type` - The type of status to show. 0 (Playing) | 1 (Streaming) | 2 (Listening) | 3 (Watching)
- `stream` - URL of twitch.tv stream
"""
@unsafe {:update_shard_status, [:pid, :status, :game, :type, :stream]}
@spec update_shard_status(pid, status, String.t(), integer, String.t() | nil) :: :ok
def update_shard_status(pid, status, game, type \\ 0, stream \\ nil) do
Session.update_status(pid, to_string(status), game, stream, type)
Expand Down
96 changes: 96 additions & 0 deletions lib/nostrum/helpers/unsafe_helpers.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
defmodule Nostrum.UnsafeHelpers do
@moduledoc false
#############################################################################
## Taken from [`Unsafe`](https://hex.pm/packages/unsafe)
## Copyright (c) 2017 Isaac Whitfield
##

defmacro __using__(options) do
quote location: :keep do
@before_compile unquote(__MODULE__)
@unsafe_options unquote(options)

Module.register_attribute(__MODULE__, :unsafe, accumulate: true)
end
end

defmacro __before_compile__(%{module: module} = env) do
binding = Module.get_attribute(module, :unsafe)

options =
module
|> Module.get_attribute(:unsafe_options)
|> Kernel.||([])

compile!(env, binding, options)
end

@type arities :: arity | [arity]
@type binding :: {atom, arities} | {atom, arities, handler}
@type handler :: atom | {atom, atom}

@spec compile!(Macro.Env.t(), binding | [binding], Keyword.t()) :: Macro.t()
def compile!(env, bindings, options) when is_list(bindings),
do: Enum.map(bindings, &compile!(env, &1, options))

def compile!(env, {name, arity}, options),
do: compile!(env, {name, arity, options[:handler]}, options)

def compile!(env, {name, [head | _] = arity, handler}, options)
when is_integer(head) do
arity
|> Enum.map(&{name, &1, handler})
|> Enum.map(&compile!(env, &1, options))
end

def compile!(env, {name, arity, handler}, options) do
{enum, length, generator} =
if is_list(arity) do
{arity, length(arity), &Macro.var(&1, env.module)}
else
{0..(arity && arity - 1), arity, &Macro.var(:"arg#{&1}", env.module)}
end

params = Enum.map(enum, generator)

result = quote do: apply(unquote(env.module), unquote(name), unquote(params))

handle =
case handler do
func when is_atom(func) and not is_nil(func) ->
quote do: unquote(func)(unquote(result))

{mod, func} ->
quote do: apply(unquote(mod), unquote(func), [unquote(result)])

_fail ->
raise CompileError,
description: "Invalid handler definition for #{name}/#{length}",
file: env.file,
line: env.line
end

ex_docs =
if options[:docs] do
quote do: @doc("Unsafe proxy definition for `#{unquote(name)}/#{unquote(length)}`.")
else
quote do: @doc(false)
end

quote do
unquote(ex_docs)

def unquote(:"#{name}!")(unquote_splicing(params)) do
unquote(handle)
end
end
end

def compile!(env, _invalid, _options),
do:
raise(CompileError,
description: "Invalid function reference provided",
file: env.file,
line: env.line
)
end
Loading