diff --git a/gleam.toml b/gleam.toml index 70b914e..775ec6b 100644 --- a/gleam.toml +++ b/gleam.toml @@ -18,6 +18,7 @@ gleam_otp = ">= 1.2.0 and < 2.0.0" gleam_erlang = ">= 1.3.0 and < 2.0.0" simplifile = ">= 2.3.1 and < 3.0.0" gleam_json = ">= 3.1.0 and < 4.0.0" +gleam_time = ">= 1.6.0 and < 2.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml index a94ff87..af21009 100644 --- a/manifest.toml +++ b/manifest.toml @@ -7,6 +7,7 @@ packages = [ { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, { name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" }, + { name = "gleam_time", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "0DF3834D20193F0A38D0EB21F0A78D48F2EC276C285969131B86DF8D4EF9E762" }, { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, { name = "simplifile", version = "2.3.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "957E0E5B75927659F1D2A1B7B75D7B9BA96FAA8D0C53EA71C4AD9CD0C6B848F6" }, ] @@ -16,5 +17,6 @@ gleam_erlang = { version = ">= 1.3.0 and < 2.0.0" } gleam_json = { version = ">= 3.1.0 and < 4.0.0" } gleam_otp = { version = ">= 1.2.0 and < 2.0.0" } gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } +gleam_time = { version = ">= 1.6.0 and < 2.0.0" } gleeunit = { version = ">= 1.0.0 and < 2.0.0" } simplifile = { version = ">= 2.3.1 and < 3.0.0" } diff --git a/src/musicplayer/logging/control.gleam b/src/musicplayer/logging/control.gleam new file mode 100644 index 0000000..00a1220 --- /dev/null +++ b/src/musicplayer/logging/control.gleam @@ -0,0 +1,7 @@ +import gleam/erlang/process.{type Subject} + +pub type Control { + Write(String) + + Exit(reply_to: Subject(Nil)) +} diff --git a/src/musicplayer/logging/logging.gleam b/src/musicplayer/logging/logging.gleam new file mode 100644 index 0000000..26687c8 --- /dev/null +++ b/src/musicplayer/logging/logging.gleam @@ -0,0 +1,58 @@ +import gleam/erlang/process.{type Subject} +import gleam/otp/actor +import gleam/result +import gleam/string +import gleam/time/calendar +import gleam/time/timestamp +import simplifile + +import musicplayer/logging/control.{type Control} + +type State { + State(filepath: String) +} + +pub fn new(filepath: String) -> Result(Subject(Control), String) { + use _ <- result.try( + case simplifile.is_file(filepath) { + Ok(True) -> Ok(Nil) + _ -> simplifile.create_file(filepath) + } + |> result.map_error(fn(e) { + "Could not access or create log file: " <> string.inspect(e) + }), + ) + + actor.new(State(filepath:)) + |> actor.on_message(handle_message) + |> actor.start + |> result.map_error(fn(start_error) { + "Could not start logger: " <> string.inspect(start_error) + }) + |> result.map(fn(started) { started.data }) +} + +fn handle_message(state: State, control: Control) -> actor.Next(State, Control) { + case control { + control.Write(content) -> { + let log_line = + timestamp.system_time() + |> timestamp.to_rfc3339(calendar.utc_offset) + <> ": " + <> content + <> "\n" + + // Ignore any logging errors + let _ = simplifile.append(state.filepath, log_line) + actor.continue(state) + } + control.Exit(reply_to) -> { + process.send(reply_to, Nil) + actor.stop() + } + } +} + +pub fn log(logger: Subject(Control), content: String) -> Nil { + process.send(logger, control.Write(content)) +}