Skip to content

Commit

Permalink
Remove logger functions for now
Browse files Browse the repository at this point in the history
  • Loading branch information
lpil committed Sep 11, 2021
1 parent dbea224 commit 5ae69f1
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 422 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## v0.1.0 - 2021-09-11

- Initial release
2 changes: 1 addition & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
{project_plugins, [rebar_gleam, rebar3_hex]}.

{deps, [
{gleam_stdlib, "0.16.0"}
{gleam_stdlib, "~> 0.17.0"}
]}.
6 changes: 3 additions & 3 deletions rebar.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{"1.2.0",
[{<<"gleam_stdlib">>,{pkg,<<"gleam_stdlib">>,<<"0.16.0">>},0}]}.
[{<<"gleam_stdlib">>,{pkg,<<"gleam_stdlib">>,<<"0.17.0">>},0}]}.
[
{pkg_hash,[
{<<"gleam_stdlib">>, <<"A4D856EF2AA0503E82CBC9E5E75F466B6D607A3332C6590E39B161B2245E23C5">>}]},
{<<"gleam_stdlib">>, <<"186494B596DB67A1FDAFFF164F988737AE47A5C634893422397A356CA3C3F979">>}]},
{pkg_hash_ext,[
{<<"gleam_stdlib">>, <<"288EC0C4B80AF55CADDF09A890B5800BF12227218C5D17014F8B73D213ACFD00">>}]}
{<<"gleam_stdlib">>, <<"66834E96F3F42609B4E5AAA2B73F9EC45625A422D0AED091B3A704ACAE0420E7">>}]}
].
192 changes: 39 additions & 153 deletions src/gleam/erlang.gleam
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import gleam/erlang/atom.{Atom}
import gleam/dynamic.{Dynamic}
import gleam/function.{rescue}
import gleam/int
import gleam/io
import gleam/list
import gleam/map
import gleam/result
Expand All @@ -13,7 +10,7 @@ external fn erl_format(String, List(a)) -> Charlist =
"io_lib" "format"

/// Return a string representation of any term
pub fn format(term) {
pub fn format(term: any) -> String {
charlist.to_string(erl_format("~p", [term]))
}

Expand All @@ -27,166 +24,20 @@ type Safe {
external fn erl_binary_to_term(BitString, List(Safe)) -> Dynamic =
"erlang" "binary_to_term"

pub fn binary_to_term(binary) {
pub fn binary_to_term(binary: BitString) -> Result(Dynamic, Nil) {
case rescue(fn() { erl_binary_to_term(binary, [Safe]) }) {
Ok(term) -> Ok(term)
Error(_) -> Error(Nil)
}
}

pub fn unsafe_binary_to_term(binary) {
pub fn unsafe_binary_to_term(binary: BitString) -> Result(Dynamic, Nil) {
case rescue(fn() { erl_binary_to_term(binary, []) }) {
Ok(term) -> Ok(term)
Error(_) -> Error(Nil)
}
}

/// All reasons an that an erlang process might by the runtime.
///
/// http://erlang.org/documentation/doc-9.3/doc/reference_manual/errors.html#exit_reasons
/// Note `erlang:exit` and `erlang:error` can be called with any term.
/// Therefore when captureing errors always make sure to safely cast to this type, i.e. using `cast_exit_reason`
pub type ExitReason {
Badarg
Badarith
Badmatch(Dynamic)
FunctionClause
CaseClause(Dynamic)
IfClause
TryClause(Dynamic)
Undef
Badfun(Dynamic)
Badarity(Dynamic)
TimeoutValue
Noproc
Nocatch(Dynamic)
SystemLimit
}

/// A stacktrace data structure
pub type Stacktrace =
List(#(Atom, String, Int, String, Int))

/// Safely transform dynamic to exit reason type
pub fn cast_exit_reason(raw) {
let badarg = dynamic.from(atom.create_from_string("badarg"))
let badarith = dynamic.from(atom.create_from_string("badarith"))
let badmatch = dynamic.from(atom.create_from_string("badmatch"))
let function_clause = dynamic.from(atom.create_from_string("function_clause"))
let case_clause = dynamic.from(atom.create_from_string("case_clause"))
let if_clause = dynamic.from(atom.create_from_string("if_clause"))
let try_clause = dynamic.from(atom.create_from_string("try_clause"))
let undef = dynamic.from(atom.create_from_string("undef"))
let badfun = dynamic.from(atom.create_from_string("badfun"))
let badarity = dynamic.from(atom.create_from_string("badarity"))
let timeout_value = dynamic.from(atom.create_from_string("timeout_value"))
let noproc = dynamic.from(atom.create_from_string("noproc"))
let nocatch = dynamic.from(atom.create_from_string("nocatch"))
let system_limit = dynamic.from(atom.create_from_string("system_limit"))

let key =
dynamic.element(raw, 0)
|> result.unwrap(raw)
case key, dynamic.element(raw, 1) {
k, Error(_) if k == badarg -> Ok(Badarg)
k, Error(_) if k == badarith -> Ok(Badarith)
k, Ok(term) if k == badmatch -> Ok(Badmatch(term))
k, Error(_) if k == function_clause -> Ok(FunctionClause)
k, Ok(term) if k == case_clause -> Ok(CaseClause(term))
k, Error(_) if k == if_clause -> Ok(IfClause)
k, Ok(term) if k == try_clause -> Ok(TryClause(term))
k, Error(_) if k == undef -> Ok(Undef)
k, Ok(term) if k == badfun -> Ok(Badfun(term))
k, Ok(term) if k == badarity -> Ok(Badarity(term))
k, Error(_) if k == timeout_value -> Ok(TimeoutValue)
k, Error(_) if k == noproc -> Ok(Noproc)
k, Ok(term) if k == nocatch -> Ok(Nocatch(term))
k, Error(_) if k == system_limit -> Ok(SystemLimit)
_, _ -> Error("Not a runtime exit reason")
}
}

pub type Location {
Location(module: String, function: String, line: Int)
}

pub type DevReason {
Todo(location: Location, message: String)
AssertNotMatched(location: Location, value: Dynamic)
}

pub fn cast_dev_reason(raw) {
try gleam_error = dynamic.field(raw, atom.create_from_string("gleam_error"))
try gleam_error = atom.from_dynamic(gleam_error)
try module = dynamic.field(raw, atom.create_from_string("module"))
try module = dynamic.string(module)
try function = dynamic.field(raw, atom.create_from_string("function"))
try function = dynamic.string(function)
try line = dynamic.field(raw, atom.create_from_string("line"))
try line = dynamic.int(line)
let location = Location(module, function, line)

let todo_atom = atom.create_from_string("todo")
let assert_atom = atom.create_from_string("assert")

let message =
dynamic.field(raw, atom.create_from_string("message"))
|> result.then(dynamic.string)
let value = dynamic.field(raw, atom.create_from_string("value"))
case gleam_error, message, value {
e, Ok(message), _ if e == todo_atom -> Todo(location, message)
e, _, Ok(value) if e == assert_atom -> AssertNotMatched(location, value)
}
|> Ok
}

/// Safely cast a dynamic stacktrace to typed data.
pub fn cast_stacktrace(raw) -> Result(Stacktrace, String) {
try raw_frames = dynamic.list(raw)
list.try_map(raw_frames, cast_stack_frame)
}

// https://github.com/elixir-lang/elixir/blob/76d245b6081c53228bf99fc1494add5de7872065/lib/elixir/lib/exception.ex#L28
// stacktrace is module, function (atom or charlist), args_or_arity, location(keyword list)
fn cast_stack_frame(raw) {
try module =
dynamic.element(raw, 0)
|> result.then(atom.from_dynamic)

try function_raw = dynamic.element(raw, 1)
let function = case atom.from_dynamic(function_raw) {
Ok(function) -> atom.to_string(function)
Error(_) -> charlist.to_string(dynamic.unsafe_coerce(function_raw))
}

try arity_raw = dynamic.element(raw, 2)
let arity = case dynamic.int(arity_raw) {
Ok(arity) -> arity
Error(_) -> list.length(dynamic.unsafe_coerce(arity_raw))
}

try location =
dynamic.element(raw, 3)
|> result.then(dynamic.typed_list(_, dynamic.tuple2))
|> result.map(map.from_list)

let file_atom = dynamic.from(atom.create_from_string("file"))
let line_atom = dynamic.from(atom.create_from_string("line"))

try filename =
map.get(location, file_atom)
|> result.map_error(fn(_: Nil) { "Missing key 'file'" })

let filename = charlist.to_string(dynamic.unsafe_coerce(filename))

try line_number =
map.get(location, line_atom)
|> result.map_error(fn(_: Nil) { "Missing key 'line'" })
|> result.then(dynamic.int)

Ok(#(module, function, arity, filename, line_number))
}

/// Error value returned by `get_line` function
///
pub type GetLineError {
Expand All @@ -198,9 +49,44 @@ pub type GetLineError {
///
/// # Example
///
/// > io.get_line("Language: ")
/// > get_line("Language: ")
/// // -> Language: <- gleam
/// Ok("gleam\n")
///
pub external fn get_line(prompt: String) -> Result(String, GetLineError) =
"gleam_erlang_ffi" "get_line"

pub type TimeUnit {
Second
Millisecond
Microsecond
Nanosecond
}

/// Returns the current OS system time.
///
/// https://erlang.org/doc/apps/erts/time_correction.html#OS_System_Time
pub external fn system_time(TimeUnit) -> Int =
"os" "system_time"

/// Returns the current OS system time as a tuple of Ints
///
/// http://erlang.org/doc/man/os.html#timestamp-0
pub external fn erlang_timestamp() -> #(Int, Int, Int) =
"os" "timestamp"

/// Gleam doesn't offer any way to raise exceptions, but they may still occur
/// due to bugs when working with unsafe code, such as when calling Erlang
/// function.
///
/// This function will catch any error thrown and convert it into a result
/// rather than crashing the process.
///
pub external fn rescue(fn() -> a) -> Result(a, Crash) =
"gleam_erlang_ffi" "rescue"

pub type Crash {
Exited(Dynamic)
Thrown(Dynamic)
Errored(Dynamic)
}
6 changes: 3 additions & 3 deletions src/gleam/erlang/atom.gleam
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import gleam/dynamic.{Dynamic}
import gleam/dynamic.{DecodeError, Dynamic}

/// Atom is a special string-like data-type that is most commonly used for
/// interfacing with code written in other BEAM languages such as Erlang and
Expand Down Expand Up @@ -73,7 +73,7 @@ pub external fn to_string(Atom) -> String =
/// OK("hello")
///
/// > atom(from(123))
/// Error("Expected an Atom, got `123`")
/// Error(DecodeError(expected: "Int", found: "Int"))
///
pub external fn from_dynamic(from: Dynamic) -> Result(Atom, String) =
pub external fn from_dynamic(from: Dynamic) -> Result(Atom, DecodeError) =
"gleam_erlang_ffi" "atom_from_dynamic"
112 changes: 0 additions & 112 deletions src/gleam/erlang/logger.gleam

This file was deleted.

Loading

0 comments on commit 5ae69f1

Please sign in to comment.