From 82b1a21cb011b51474592f7da2abd3e14d77590f Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 30 Aug 2021 17:35:29 +0900 Subject: [PATCH 1/9] update deps (mainly rustler to enable OTP24) --- mix.exs | 2 +- mix.lock | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index 4dce65f..8a0b6df 100644 --- a/mix.exs +++ b/mix.exs @@ -39,7 +39,7 @@ defmodule ID3.MixProject do defp deps do [ {:ex_doc, "~> 0.19", only: :dev, runtime: false}, - {:rustler, "~> 0.21.0"} + {:rustler, "~> 0.21"} ] end diff --git a/mix.lock b/mix.lock index c6a8408..9430a7b 100644 --- a/mix.lock +++ b/mix.lock @@ -1,9 +1,11 @@ %{ "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, - "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, - "makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, - "rustler": {:hex, :rustler, "0.21.0", "68cc4fc015d0b9541865ea78e78e9ef2dd91ee4be80bf543fd15791102a45aca", [:mix], [{:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "e5429378c397f37f1091a35593b153aee1925e197c6842d04648d802edb52f80"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.15", "b29e8e729f4aa4a00436580dcc2c9c5c51890613457c193cc8525c388ccb2f06", [:mix], [], "hexpm", "044523d6438ea19c1b8ec877ec221b008661d3c27e3b848f4c879f500421ca5c"}, + "ex_doc": {:hex, :ex_doc, "0.25.1", "4b736fa38dc76488a937e5ef2944f5474f3eff921de771b25371345a8dc810bc", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3200b0a69ddb2028365281fbef3753ea9e728683863d8cdaa96580925c891f67"}, + "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, + "rustler": {:hex, :rustler, "0.22.0", "e2930f9d6933e910f87526bb0a7f904e32b62a7e838a3ca4a884ee7fdfb957ed", [:mix], [{:toml, "~> 0.5.2", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "01f5989dd511ebec09be481e07d3c59773d5373c5061e09d3ebc3ef61811b49d"}, "toml": {:hex, :toml, "0.5.2", "e471388a8726d1ce51a6b32f864b8228a1eb8edc907a0edf2bb50eab9321b526", [:mix], [], "hexpm", "f1e3dabef71fb510d015fad18c0e05e7c57281001141504c6b69d94e99750a07"}, } From 38f75bd5b97f67d1e6a2cc501afca53c7b04c178 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Tue, 31 Aug 2021 14:15:28 +0900 Subject: [PATCH 2/9] update rustler mod version --- native/id3/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/id3/Cargo.toml b/native/id3/Cargo.toml index f78e81a..2fc9517 100644 --- a/native/id3/Cargo.toml +++ b/native/id3/Cargo.toml @@ -10,6 +10,6 @@ path = "src/lib.rs" crate-type = ["dylib"] [dependencies] -rustler = "0.21" +rustler = "0.22" lazy_static = "1.4" id3 = "0.5" From b8c3de6d53f3793515ad153897e66631ab4fc563 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Tue, 31 Aug 2021 14:20:25 +0900 Subject: [PATCH 3/9] strictly use Rustler 0.22 (to avoid future breaking changes during pre-releases) --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 8a0b6df..acc3f82 100644 --- a/mix.exs +++ b/mix.exs @@ -39,7 +39,7 @@ defmodule ID3.MixProject do defp deps do [ {:ex_doc, "~> 0.19", only: :dev, runtime: false}, - {:rustler, "~> 0.21"} + {:rustler, "~> 0.22.0"} ] end From f9afb8d07458424217c839e1b3fc951767981c6d Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 00:02:36 +0900 Subject: [PATCH 4/9] remove deprecated mix settings --- config/config.exs | 4 ++++ config/prod.exs | 5 +++++ mix.exs | 14 ++------------ 3 files changed, 11 insertions(+), 12 deletions(-) create mode 100644 config/prod.exs diff --git a/config/config.exs b/config/config.exs index 2cf1241..b59617f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -28,3 +28,7 @@ use Mix.Config # here (which is why it is important to import them last). # # import_config "#{Mix.env()}.exs" + +config :id3, ID3.Native, + mode: :debug, + path: "native/id3" diff --git a/config/prod.exs b/config/prod.exs new file mode 100644 index 0000000..dc75212 --- /dev/null +++ b/config/prod.exs @@ -0,0 +1,5 @@ +use Mix.Config + +config :id3, ID3.Native, + mode: :release, + path: "native/id3" diff --git a/mix.exs b/mix.exs index acc3f82..a7bf6b0 100644 --- a/mix.exs +++ b/mix.exs @@ -4,11 +4,10 @@ defmodule ID3.MixProject do def project do [ app: :id3, - version: "1.0.1", + version: "1.0.2", elixir: "~> 1.7", start_permanent: Mix.env() == :prod, - rustler_crates: rustler_crates(), - compilers: [:rustler] ++ Mix.compilers(), + compilers: Mix.compilers(), deps: deps(), # Docs @@ -43,15 +42,6 @@ defmodule ID3.MixProject do ] end - defp rustler_crates do - [ - id3: [ - path: "native/id3", - mode: if(Mix.env() == :prod, do: :release, else: :debug) - ] - ] - end - defp description() do "Read/Write mp3 ID3 tags. Uses Rust NIF." end From 93d6b094e3e9ff10e05fce44b1f88d8583d47472 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 00:17:07 +0900 Subject: [PATCH 5/9] wip: repaced deprecated macros and functions of Rustler --- native/id3/src/lib.rs | 78 +++++++++++++++++------- native/id3/src/nif_converter/datetime.rs | 10 +-- native/id3/src/nif_converter/picture.rs | 54 ++++++++-------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/native/id3/src/lib.rs b/native/id3/src/lib.rs index ef52a2f..7ba09a0 100644 --- a/native/id3/src/lib.rs +++ b/native/id3/src/lib.rs @@ -5,29 +5,20 @@ mod nif_converter; use crate::nif_converter::{ID3Picture, NaiveDateTime, NifBinary}; use id3::{Tag, Version}; -use rustler::{rustler_export_nifs, Encoder, Env, NifResult, NifStruct, Term}; +use rustler::{init, Env, Encoder, NifResult, NifStruct, Term}; mod atoms { - use rustler::rustler_atoms; - - rustler_atoms! { - atom ok; - atom error; - //atom __true__ = "true"; - //atom __false__ = "false"; - atom file_open_error; - atom tag_write_error; + use rustler::atoms; + + atoms! { + ok, + error, + file_open_error, + tag_write_error, } } -rustler_export_nifs! { - "Elixir.ID3.Native", - [ - ("get_major_frames", 1, major_frames), - ("write_major_frames", 2, write_major_frames), - ], - None -} +init!("Elixir.ID3.Native", [major_frames, write_major_frames]); #[derive(NifStruct)] #[module = "ID3.Tag"] @@ -50,9 +41,51 @@ struct MajorFrames<'a> { pub pictures: Vec, } -fn major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult> { - let path: String = (args[0].decode())?; +enum ReadResult<'a> { + Ok(MajorFrames<'a>), + Error() +} +impl ReadResult<'_> { + fn ok<'a>(frames: MajorFrames<'a>) -> ReadResult<'a> { + ReadResult::Ok(frames) + } + + fn error<'a>() -> ReadResult<'a> { + ReadResult::Error() + } +} + +impl<'a> Encoder for ReadResult<'a> { + fn encode<'a>(&'a self, env: Env<'a>) -> Term<'a> { + match self { + ReadResult::Ok(frames) => ( + atoms::ok(), + MajorFrames { + // comments: + year: frames.year, + date_recorded: frames.date_recorded, + date_released: frames.date_released, + artist: frames.artist, + album: frames.album, + album_artist: frames.album_artist, + title: frames.title, + duration: frames.duration, + genre: frames.genre, + disc: frames.disc, + total_discs: frames.total_discs, + track: frames.track, + total_tracks: frames.total_tracks, + pictures: frames.pictures, + } + ).encode(env), + ReadResult::Error() => (atoms::error(), atoms::file_open_error()).encode(env) + } + } +} + +#[rustler::nif(name = "get_major_frames")] +fn major_frames<'a>(path: String) -> ReadResult<'a> { match Tag::read_from_path(path) { Ok(tag) => { let frames = MajorFrames { @@ -72,12 +105,13 @@ fn major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult> { total_tracks: tag.total_tracks(), pictures: tag.pictures().map(ID3Picture::from).collect::>(), }; - Ok((atoms::ok(), frames).encode(env)) + ReadResult::ok(frames) } - Err(_e) => Ok((atoms::error(), atoms::file_open_error()).encode(env)), + Err(_e) => ReadResult::error(), } } +#[rustler::nif] fn write_major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult> { let frames: MajorFrames = args[0].decode()?; let path: String = args[1].decode()?; diff --git a/native/id3/src/nif_converter/datetime.rs b/native/id3/src/nif_converter/datetime.rs index 6d530d5..793eb22 100644 --- a/native/id3/src/nif_converter/datetime.rs +++ b/native/id3/src/nif_converter/datetime.rs @@ -3,12 +3,12 @@ use rustler::types::atom::Atom as NifAtom; use rustler::NifStruct; mod atoms { - use rustler::rustler_atoms; + use rustler::atoms; - rustler_atoms! { - atom ok; - atom error; - atom calendar_iso = "Elixir.Calendar.ISO"; + atoms! { + ok, + error, + calendar_iso = "Elixir.Calendar.ISO", } } diff --git a/native/id3/src/nif_converter/picture.rs b/native/id3/src/nif_converter/picture.rs index d84ba1b..f760648 100644 --- a/native/id3/src/nif_converter/picture.rs +++ b/native/id3/src/nif_converter/picture.rs @@ -4,40 +4,40 @@ use rustler::types::atom::Atom as NifAtom; use rustler::NifStruct; mod atoms { - use rustler::rustler_atoms; + use rustler::atoms; - rustler_atoms! { - atom ok; - atom error; + atoms! { + ok, + error, } } #[allow(non_snake_case)] mod pictype_atoms { - use rustler::rustler_atoms; + use rustler::atoms; - rustler_atoms! { - atom Other; - atom Icon; - atom OtherIcon; - atom CoverFront; - atom CoverBack; - atom Leaflet; - atom Media; - atom LeadArtist; - atom Artist; - atom Conductor; - atom Band; - atom Composer; - atom Lyricist; - atom RecordingLocation; - atom DuringRecording; - atom DuringPerformance; - atom ScreenCapture; - atom BrightFish; - atom Illustration; - atom BandLogo; - atom PublisherLogo; + atoms! { + Other, + Icon, + OtherIcon, + CoverFront, + CoverBack, + Leaflet, + Media, + LeadArtist, + Artist, + Conductor, + Band, + Composer, + Lyricist, + RecordingLocation, + DuringRecording, + DuringPerformance, + ScreenCapture, + BrightFish, + Illustration, + BandLogo, + PublisherLogo, } } From 41083edda1d86aa55efea0e7feac256ee991b755 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 13:42:39 +0900 Subject: [PATCH 6/9] own the Strings to avoid lifetime problems --- native/id3/src/lib.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/native/id3/src/lib.rs b/native/id3/src/lib.rs index 7ba09a0..1246da9 100644 --- a/native/id3/src/lib.rs +++ b/native/id3/src/lib.rs @@ -23,17 +23,17 @@ init!("Elixir.ID3.Native", [major_frames, write_major_frames]); #[derive(NifStruct)] #[module = "ID3.Tag"] /// Struct for passing major tag data to/from Elixir. -struct MajorFrames<'a> { +struct MajorFrames { // pub comments: Option, pub year: Option, pub date_recorded: Option, pub date_released: Option, - pub artist: Option<&'a str>, - pub album_artist: Option<&'a str>, - pub album: Option<&'a str>, - pub title: Option<&'a str>, + pub artist: Option, + pub album_artist: Option, + pub album: Option, + pub title: Option, pub duration: Option, - pub genre: Option<&'a str>, + pub genre: Option, pub disc: Option, pub total_discs: Option, pub track: Option, @@ -41,23 +41,23 @@ struct MajorFrames<'a> { pub pictures: Vec, } -enum ReadResult<'a> { - Ok(MajorFrames<'a>), +enum ReadResult { + Ok(MajorFrames), Error() } -impl ReadResult<'_> { - fn ok<'a>(frames: MajorFrames<'a>) -> ReadResult<'a> { +impl ReadResult { + fn ok(frames: MajorFrames) -> ReadResult { ReadResult::Ok(frames) } - fn error<'a>() -> ReadResult<'a> { + fn error() -> ReadResult { ReadResult::Error() } } -impl<'a> Encoder for ReadResult<'a> { - fn encode<'a>(&'a self, env: Env<'a>) -> Term<'a> { +impl Encoder for ReadResult { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { match self { ReadResult::Ok(frames) => ( atoms::ok(), @@ -85,7 +85,7 @@ impl<'a> Encoder for ReadResult<'a> { } #[rustler::nif(name = "get_major_frames")] -fn major_frames<'a>(path: String) -> ReadResult<'a> { +fn major_frames(path: String) -> ReadResult { match Tag::read_from_path(path) { Ok(tag) => { let frames = MajorFrames { @@ -93,12 +93,12 @@ fn major_frames<'a>(path: String) -> ReadResult<'a> { year: tag.year(), date_recorded: tag.date_recorded().map(NaiveDateTime::from), date_released: tag.date_released().map(NaiveDateTime::from), - artist: tag.artist(), - album: tag.album(), - album_artist: tag.album_artist(), - title: tag.title(), + artist: tag.artist().map(|s| s.to_string()), + album: tag.album().map(|s| s.to_string()), + album_artist: tag.album_artist().map(|s| s.to_string()), + title: tag.title().map(|s| s.to_string()), duration: tag.duration(), - genre: tag.genre(), + genre: tag.genre().map(|s| s.to_string()), disc: tag.disc(), total_discs: tag.total_discs(), track: tag.track(), From c603b70599aebe206620d86f148f28accbca68c0 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 14:24:07 +0900 Subject: [PATCH 7/9] fixup: no more need to copy structure fields so return the original struct --- native/id3/src/lib.rs | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/native/id3/src/lib.rs b/native/id3/src/lib.rs index 1246da9..cb6f8c0 100644 --- a/native/id3/src/lib.rs +++ b/native/id3/src/lib.rs @@ -59,27 +59,8 @@ impl ReadResult { impl Encoder for ReadResult { fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { match self { - ReadResult::Ok(frames) => ( - atoms::ok(), - MajorFrames { - // comments: - year: frames.year, - date_recorded: frames.date_recorded, - date_released: frames.date_released, - artist: frames.artist, - album: frames.album, - album_artist: frames.album_artist, - title: frames.title, - duration: frames.duration, - genre: frames.genre, - disc: frames.disc, - total_discs: frames.total_discs, - track: frames.track, - total_tracks: frames.total_tracks, - pictures: frames.pictures, - } - ).encode(env), - ReadResult::Error() => (atoms::error(), atoms::file_open_error()).encode(env) + ReadResult::Ok(frames) => (atoms::ok(), frames).encode(env), + ReadResult::Error() => (atoms::error(), atoms::file_open_error()).encode(env), } } } From 795c45b51aa505652c87b46c26f3422e83a58146 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 14:25:18 +0900 Subject: [PATCH 8/9] add ok/error tuple implementations for write, and migrate from deprecated formats of old Rustler --- native/id3/src/lib.rs | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/native/id3/src/lib.rs b/native/id3/src/lib.rs index cb6f8c0..07de6f3 100644 --- a/native/id3/src/lib.rs +++ b/native/id3/src/lib.rs @@ -5,7 +5,7 @@ mod nif_converter; use crate::nif_converter::{ID3Picture, NaiveDateTime, NifBinary}; use id3::{Tag, Version}; -use rustler::{init, Env, Encoder, NifResult, NifStruct, Term}; +use rustler::{init, Encoder, Env, NifResult, NifStruct, Term}; mod atoms { use rustler::atoms; @@ -43,7 +43,7 @@ struct MajorFrames { enum ReadResult { Ok(MajorFrames), - Error() + Error(), } impl ReadResult { @@ -65,6 +65,30 @@ impl Encoder for ReadResult { } } +enum WriteResult { + Ok(), + Error(rustler::Atom), +} + +impl WriteResult { + fn ok() -> WriteResult { + WriteResult::Ok() + } + + fn error(reason: rustler::Atom) -> WriteResult { + WriteResult::Error(reason) + } +} + +impl Encoder for WriteResult { + fn encode<'a>(&self, env: Env<'a>) -> Term<'a> { + match self { + WriteResult::Ok() => atoms::ok().encode(env), + WriteResult::Error(reason) => (atoms::error(), reason).encode(env), + } + } +} + #[rustler::nif(name = "get_major_frames")] fn major_frames(path: String) -> ReadResult { match Tag::read_from_path(path) { @@ -93,10 +117,7 @@ fn major_frames(path: String) -> ReadResult { } #[rustler::nif] -fn write_major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult> { - let frames: MajorFrames = args[0].decode()?; - let path: String = args[1].decode()?; - +fn write_major_frames<'a>(frames: MajorFrames, path: String) -> WriteResult { match Tag::read_from_path(&path) { Ok(tag) => { let mut tag = tag; @@ -155,11 +176,11 @@ fn write_major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult set_pictures(&mut tag, frames.pictures); match tag.write_to_path(&path, Version::Id3v24) { - Ok(_) => Ok(atoms::ok().encode(env)), - Err(_) => Ok((atoms::error(), atoms::tag_write_error()).encode(env)), + Ok(_) => WriteResult::ok(), + Err(_) => WriteResult::error(atoms::tag_write_error()), } } - Err(_e) => Ok((atoms::error(), atoms::file_open_error()).encode(env)), + Err(_e) => WriteResult::error(atoms::file_open_error()), } } From 1cbe98f9d9e45e8f271018aa60e9e09822e230f8 Mon Sep 17 00:00:00 2001 From: ndac_todoroki Date: Mon, 27 Sep 2021 14:25:42 +0900 Subject: [PATCH 9/9] remove unused imports --- native/id3/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/id3/src/lib.rs b/native/id3/src/lib.rs index 07de6f3..35eba86 100644 --- a/native/id3/src/lib.rs +++ b/native/id3/src/lib.rs @@ -5,7 +5,7 @@ mod nif_converter; use crate::nif_converter::{ID3Picture, NaiveDateTime, NifBinary}; use id3::{Tag, Version}; -use rustler::{init, Encoder, Env, NifResult, NifStruct, Term}; +use rustler::{init, Encoder, Env, NifStruct, Term}; mod atoms { use rustler::atoms;