Last active
October 13, 2020 02:58
-
-
Save garthk/1cd6f930652045892fbf83e829727a5b to your computer and use it in GitHub Desktop.
Smuggle Terms as Self-Extracting Elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Smuggle do | |
@moduledoc """ | |
Make it easier to smuggle data from one system to another. | |
If your target is an `iex` prompt: | |
Smuggle.dump(value) | |
# copy | |
# paste into other iex | |
Pasting it into GitHub issue, comment, or gist? | |
Smuggle.dump(value, wrapper: :gfm) | |
# copy | |
# paste into GitHub | |
""" | |
@typedoc "Encoded terms." | |
@type encoded :: binary() | |
@typedoc "Self-extraction code." | |
@type sfx :: binary() | |
@doc "Encode a term to a compressed binary in Base64." | |
@spec encode(term()) :: encoded() | |
def encode(term) do | |
term | |
|> :erlang.term_to_binary() | |
|> :zlib.gzip() | |
|> Base.encode64() | |
|> Stream.unfold(&String.split_at(&1, 76)) | |
|> Enum.take_while(&(&1 != "")) | |
|> Enum.join("\n") | |
end | |
@doc "Decode a compressed binary in Base64." | |
@spec decode(encoded()) :: term() | |
def decode(encoded) do | |
~R/\s+/ | |
|> Regex.replace(encoded, "") | |
|> Base.decode64!() | |
|> :zlib.gunzip() | |
|> :erlang.binary_to_term() | |
end | |
@doc "Generate self-extraction code." | |
@spec gen_sfx(encoded()) :: sfx() | |
def gen_sfx(encoded) do | |
encoded = if String.ends_with?(encoded, "\n"), do: encoded, else: encoded <> "\n" | |
sigil = { | |
:sigil_S, | |
[delimiter: "\"\"\"", context: Elixir, import: Kernel], | |
[{:<<>>, [], [encoded]}, []] | |
} | |
quote do | |
# v = unquote(sigil) | |
# credo:disable-for-next-line | |
(fn v -> | |
~R/\s+/ | |
|> Regex.replace(v, "") | |
|> Base.decode64!() | |
|> :zlib.gunzip() | |
|> :erlang.binary_to_term() | |
end).(unquote(sigil)) | |
end | |
# credo:disable-for-next-line | |
|> Macro.to_string() | |
end | |
@doc "Dump self-extracted." | |
@spec dump(term(), [{:wrapper, atom()}]) :: :ok | |
def dump(term, opts \\ []) do | |
{wrapper, opts} = Keyword.pop(opts, :wrapper) | |
for {k, _} <- opts, do: raise(ArgumentError, "no such option: #{k}") | |
term |> encode() |> gen_sfx() |> wrap(wrapper) |> IO.puts() | |
end | |
defp wrap(sfx, wrapper) when is_atom(wrapper), do: wrap(sfx, {__MODULE__, wrapper, []}) | |
defp wrap(sfx, {m, f, args}), do: Kernel.apply(m, f, [sfx | args]) | |
@doc "No-op wrapper." | |
@spec noop(sfx()) :: sfx() | |
def noop(sfx), do: sfx | |
@doc "Wrap in a details tag for GitHub-flavoured markdown." | |
@spec gfm(sfx()) :: binary() | |
def gfm(sfx) do | |
IO.iodata_to_binary([ | |
~S""" | |
<details> | |
<summary> | |
Self-extracting gzip base64 to paste at the iex prompt: | |
</summary> | |
```elixir | |
""", | |
sfx, | |
"\n", | |
~S""" | |
``` | |
</details> | |
""" | |
]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example, smuggling
nil
:Result when pasted into this comment:
Self-extracting gzip base64 to paste at the iex prompt: