Skip to content
4 changes: 4 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
5 changes: 5 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use Mix.Config

config :id3, ID3.Native,
mode: :release,
path: "native/id3"
16 changes: 3 additions & 13 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -39,16 +38,7 @@ defmodule ID3.MixProject do
defp deps do
[
{:ex_doc, "~> 0.19", only: :dev, runtime: false},
{:rustler, "~> 0.21.0"}
]
end

defp rustler_crates do
[
id3: [
path: "native/id3",
mode: if(Mix.env() == :prod, do: :release, else: :debug)
]
{:rustler, "~> 0.22.0"}
]
end

Expand Down
12 changes: 7 additions & 5 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -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"},
}
2 changes: 1 addition & 1 deletion native/id3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
116 changes: 76 additions & 40 deletions native/id3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,83 +5,119 @@ 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, Encoder, Env, 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"]
/// Struct for passing major tag data to/from Elixir.
struct MajorFrames<'a> {
struct MajorFrames {
// pub comments: Option<String>,
pub year: Option<i32>,
pub date_recorded: Option<NaiveDateTime>,
pub date_released: Option<NaiveDateTime>,
pub artist: Option<&'a str>,
pub album_artist: Option<&'a str>,
pub album: Option<&'a str>,
pub title: Option<&'a str>,
pub artist: Option<String>,
pub album_artist: Option<String>,
pub album: Option<String>,
pub title: Option<String>,
pub duration: Option<u32>,
pub genre: Option<&'a str>,
pub genre: Option<String>,
pub disc: Option<u32>,
pub total_discs: Option<u32>,
pub track: Option<u32>,
pub total_tracks: Option<u32>,
pub pictures: Vec<ID3Picture>,
}

fn major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let path: String = (args[0].decode())?;
enum ReadResult {
Ok(MajorFrames),
Error(),
}

impl ReadResult {
fn ok(frames: MajorFrames) -> ReadResult {
ReadResult::Ok(frames)
}

fn error() -> ReadResult {
ReadResult::Error()
}
}

impl Encoder for ReadResult {
fn encode<'a>(&self, env: Env<'a>) -> Term<'a> {
match self {
ReadResult::Ok(frames) => (atoms::ok(), frames).encode(env),
ReadResult::Error() => (atoms::error(), atoms::file_open_error()).encode(env),
}
}
}

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) {
Ok(tag) => {
let frames = MajorFrames {
// comments:
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(),
total_tracks: tag.total_tracks(),
pictures: tag.pictures().map(ID3Picture::from).collect::<Vec<_>>(),
};
Ok((atoms::ok(), frames).encode(env))
ReadResult::ok(frames)
}
Err(_e) => Ok((atoms::error(), atoms::file_open_error()).encode(env)),
Err(_e) => ReadResult::error(),
}
}

fn write_major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>> {
let frames: MajorFrames = args[0].decode()?;
let path: String = args[1].decode()?;

#[rustler::nif]
fn write_major_frames<'a>(frames: MajorFrames, path: String) -> WriteResult {
match Tag::read_from_path(&path) {
Ok(tag) => {
let mut tag = tag;
Expand Down Expand Up @@ -140,11 +176,11 @@ fn write_major_frames<'a>(env: Env<'a>, args: &[Term<'a>]) -> NifResult<Term<'a>
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()),
}
}

Expand Down
10 changes: 5 additions & 5 deletions native/id3/src/nif_converter/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
}

Expand Down
54 changes: 27 additions & 27 deletions native/id3/src/nif_converter/picture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down