1- import gleam/erlang/atom . { Atom }
21import gleam/dynamic . { Dynamic }
3- import gleam/function . { rescue }
42import gleam/int
5- import gleam/io
63import gleam/list
74import gleam/map
85import gleam/result
@@ -13,7 +10,7 @@ external fn erl_format(String, List(a)) -> Charlist =
1310 "io_lib" "format"
1411
1512/// Return a string representation of any term
16- pub fn format ( term ) {
13+ pub fn format ( term : any ) -> String {
1714 charlist . to_string ( erl_format ( "~p" , [ term ] ) )
1815}
1916
@@ -27,166 +24,20 @@ type Safe {
2724external fn erl_binary_to_term ( BitString , List ( Safe ) ) -> Dynamic =
2825 "erlang" "binary_to_term"
2926
30- pub fn binary_to_term ( binary ) {
27+ pub fn binary_to_term ( binary : BitString ) -> Result ( Dynamic , Nil ) {
3128 case rescue ( fn ( ) { erl_binary_to_term ( binary , [ Safe ] ) } ) {
3229 Ok ( term ) -> Ok ( term )
3330 Error ( _ ) -> Error ( Nil )
3431 }
3532}
3633
37- pub fn unsafe_binary_to_term ( binary ) {
34+ pub fn unsafe_binary_to_term ( binary : BitString ) -> Result ( Dynamic , Nil ) {
3835 case rescue ( fn ( ) { erl_binary_to_term ( binary , [ ] ) } ) {
3936 Ok ( term ) -> Ok ( term )
4037 Error ( _ ) -> Error ( Nil )
4138 }
4239}
4340
44- /// All reasons an that an erlang process might by the runtime.
45- ///
46- /// http://erlang.org/documentation/doc-9.3/doc/reference_manual/errors.html#exit_reasons
47- /// Note `erlang:exit` and `erlang:error` can be called with any term.
48- /// Therefore when captureing errors always make sure to safely cast to this type, i.e. using `cast_exit_reason`
49- pub type ExitReason {
50- Badarg
51- Badarith
52- Badmatch ( Dynamic )
53- FunctionClause
54- CaseClause ( Dynamic )
55- IfClause
56- TryClause ( Dynamic )
57- Undef
58- Badfun ( Dynamic )
59- Badarity ( Dynamic )
60- TimeoutValue
61- Noproc
62- Nocatch ( Dynamic )
63- SystemLimit
64- }
65-
66- /// A stacktrace data structure
67- pub type Stacktrace =
68- List ( # ( Atom , String , Int , String , Int ) )
69-
70- /// Safely transform dynamic to exit reason type
71- pub fn cast_exit_reason ( raw ) {
72- let badarg = dynamic . from ( atom . create_from_string ( "badarg" ) )
73- let badarith = dynamic . from ( atom . create_from_string ( "badarith" ) )
74- let badmatch = dynamic . from ( atom . create_from_string ( "badmatch" ) )
75- let function_clause = dynamic . from ( atom . create_from_string ( "function_clause" ) )
76- let case_clause = dynamic . from ( atom . create_from_string ( "case_clause" ) )
77- let if_clause = dynamic . from ( atom . create_from_string ( "if_clause" ) )
78- let try_clause = dynamic . from ( atom . create_from_string ( "try_clause" ) )
79- let undef = dynamic . from ( atom . create_from_string ( "undef" ) )
80- let badfun = dynamic . from ( atom . create_from_string ( "badfun" ) )
81- let badarity = dynamic . from ( atom . create_from_string ( "badarity" ) )
82- let timeout_value = dynamic . from ( atom . create_from_string ( "timeout_value" ) )
83- let noproc = dynamic . from ( atom . create_from_string ( "noproc" ) )
84- let nocatch = dynamic . from ( atom . create_from_string ( "nocatch" ) )
85- let system_limit = dynamic . from ( atom . create_from_string ( "system_limit" ) )
86-
87- let key =
88- dynamic . element ( raw , 0 )
89- |> result . unwrap ( raw )
90- case key , dynamic . element ( raw , 1 ) {
91- k , Error ( _ ) if k == badarg -> Ok ( Badarg )
92- k , Error ( _ ) if k == badarith -> Ok ( Badarith )
93- k , Ok ( term ) if k == badmatch -> Ok ( Badmatch ( term ) )
94- k , Error ( _ ) if k == function_clause -> Ok ( FunctionClause )
95- k , Ok ( term ) if k == case_clause -> Ok ( CaseClause ( term ) )
96- k , Error ( _ ) if k == if_clause -> Ok ( IfClause )
97- k , Ok ( term ) if k == try_clause -> Ok ( TryClause ( term ) )
98- k , Error ( _ ) if k == undef -> Ok ( Undef )
99- k , Ok ( term ) if k == badfun -> Ok ( Badfun ( term ) )
100- k , Ok ( term ) if k == badarity -> Ok ( Badarity ( term ) )
101- k , Error ( _ ) if k == timeout_value -> Ok ( TimeoutValue )
102- k , Error ( _ ) if k == noproc -> Ok ( Noproc )
103- k , Ok ( term ) if k == nocatch -> Ok ( Nocatch ( term ) )
104- k , Error ( _ ) if k == system_limit -> Ok ( SystemLimit )
105- _ , _ -> Error ( "Not a runtime exit reason" )
106- }
107- }
108-
109- pub type Location {
110- Location ( module : String , function : String , line : Int )
111- }
112-
113- pub type DevReason {
114- Todo ( location : Location , message : String )
115- AssertNotMatched ( location : Location , value : Dynamic )
116- }
117-
118- pub fn cast_dev_reason ( raw ) {
119- try gleam_error = dynamic.field(raw, atom.create_from_string("gleam_error"))
120- try gleam_error = atom.from_dynamic(gleam_error)
121- try module = dynamic.field(raw, atom.create_from_string("module"))
122- try module = dynamic.string(module)
123- try function = dynamic.field(raw, atom.create_from_string("function"))
124- try function = dynamic.string(function)
125- try line = dynamic.field(raw, atom.create_from_string("line"))
126- try line = dynamic.int(line)
127- let location = Location(module, function, line)
128-
129- let todo_atom = atom.create_from_string("todo")
130- let assert_atom = atom.create_from_string("assert")
131-
132- let message =
133- dynamic.field(raw, atom.create_from_string("message"))
134- |> result.then(dynamic.string)
135- let value = dynamic.field(raw, atom.create_from_string("value"))
136- case gleam_error, message, value {
137- e, Ok(message), _ if e == todo_atom -> Todo(location, message)
138- e, _, Ok(value) if e == assert_atom -> AssertNotMatched(location, value)
139- }
140- |> Ok
141- }
142-
143- /// Safely cast a dynamic stacktrace to typed data.
144- pub fn cast_stacktrace(raw) -> Result(Stacktrace, String) {
145- try raw_frames = dynamic.list(raw)
146- list.try_map(raw_frames, cast_stack_frame)
147- }
148-
149- // https://github.com/elixir-lang/elixir/blob/76d245b6081c53228bf99fc1494add5de7872065/lib/elixir/lib/exception.ex#L28
150- // stacktrace is module, function (atom or charlist), args_or_arity, location(keyword list)
151- fn cast_stack_frame(raw) {
152- try module =
153- dynamic.element(raw, 0)
154- |> result.then(atom.from_dynamic)
155-
156- try function_raw = dynamic.element(raw, 1)
157- let function = case atom.from_dynamic(function_raw) {
158- Ok(function) -> atom.to_string(function)
159- Error(_) -> charlist.to_string(dynamic.unsafe_coerce(function_raw))
160- }
161-
162- try arity_raw = dynamic.element(raw, 2)
163- let arity = case dynamic.int(arity_raw) {
164- Ok(arity) -> arity
165- Error(_) -> list.length(dynamic.unsafe_coerce(arity_raw))
166- }
167-
168- try location =
169- dynamic.element(raw, 3)
170- |> result.then(dynamic.typed_list(_, dynamic.tuple2))
171- |> result.map(map.from_list)
172-
173- let file_atom = dynamic.from(atom.create_from_string("file"))
174- let line_atom = dynamic.from(atom.create_from_string("line"))
175-
176- try filename =
177- map.get(location, file_atom)
178- |> result.map_error(fn(_: Nil) { "Missing key 'file'" })
179-
180- let filename = charlist.to_string(dynamic.unsafe_coerce(filename))
181-
182- try line_number =
183- map.get(location, line_atom)
184- |> result.map_error(fn(_: Nil) { "Missing key 'line'" })
185- |> result.then(dynamic.int)
186-
187- Ok(#(module, function, arity, filename, line_number))
188- }
189-
19041/// Error value returned by `get_line` function
19142///
19243pub type GetLineError {
@@ -198,9 +49,44 @@ pub type GetLineError {
19849///
19950/// # Example
20051///
201- /// > io. get_line("Language: ")
52+ /// > get_line("Language: ")
20253/// // -> Language: <- gleam
20354/// Ok("gleam\n")
20455///
20556pub external fn get_line ( prompt : String ) -> Result ( String , GetLineError ) =
20657 "gleam_erlang_ffi" "get_line"
58+
59+ pub type TimeUnit {
60+ Second
61+ Millisecond
62+ Microsecond
63+ Nanosecond
64+ }
65+
66+ /// Returns the current OS system time.
67+ ///
68+ /// https://erlang.org/doc/apps/erts/time_correction.html#OS_System_Time
69+ pub external fn system_time ( TimeUnit ) -> Int =
70+ "os" "system_time"
71+
72+ /// Returns the current OS system time as a tuple of Ints
73+ ///
74+ /// http://erlang.org/doc/man/os.html#timestamp-0
75+ pub external fn erlang_timestamp ( ) -> # ( Int , Int , Int ) =
76+ "os" "timestamp"
77+
78+ /// Gleam doesn't offer any way to raise exceptions, but they may still occur
79+ /// due to bugs when working with unsafe code, such as when calling Erlang
80+ /// function.
81+ ///
82+ /// This function will catch any error thrown and convert it into a result
83+ /// rather than crashing the process.
84+ ///
85+ pub external fn rescue ( fn ( ) -> a) -> Result ( a, Crash ) =
86+ "gleam_erlang_ffi" "rescue"
87+
88+ pub type Crash {
89+ Exited ( Dynamic )
90+ Thrown ( Dynamic )
91+ Errored ( Dynamic )
92+ }
0 commit comments