use std::{
ffi::{CStr, CString},
fmt::Display,
os::unix::prelude::OsStrExt,
path::Path,
};
use crate::error::{CheckResult, Result};
pub struct Comments {
ptr: *mut crate::ffi::OggOpusComments,
}
impl Drop for Comments {
fn drop(&mut self) {
unsafe { crate::ffi::ope_comments_destroy(self.ptr) };
}
}
impl Comments {
pub fn create() -> Self {
let ptr = unsafe { crate::ffi::ope_comments_create() };
assert!(!ptr.is_null());
Self { ptr }
}
}
impl Comments {
pub fn add(&mut self, tag: impl AsRef<CStr>, val: impl Into<Vec<u8>>) -> Result<&mut Self> {
let val = CString::new(val)?;
unsafe { crate::ffi::ope_comments_add(self.ptr, tag.as_ref().as_ptr(), val.as_ptr()) }
.check_result()?;
Ok(self)
}
pub fn add_string(&mut self, tag_and_val: impl Into<Vec<u8>>) -> Result<&mut Self> {
let tag_and_val = CString::new(tag_and_val)?;
unsafe { crate::ffi::ope_comments_add_string(self.ptr, tag_and_val.as_ptr()) }
.check_result()?;
Ok(self)
}
pub fn add_picture(
&mut self,
filename: impl AsRef<Path>,
picture_type: PictureType,
description: Option<impl Into<Vec<u8>>>,
) -> Result<&mut Self> {
let filename = CString::new(filename.as_ref().as_os_str().as_bytes())?;
let description = description.map(CString::new).transpose()?;
unsafe {
crate::ffi::ope_comments_add_picture(
self.ptr,
filename.as_ptr(),
picture_type as i32,
description.map(|d| d.as_ptr()).unwrap_or(std::ptr::null()),
)
}
.check_result()?;
Ok(self)
}
pub fn add_picture_from_memory(
&mut self,
picture: impl AsRef<[u8]>,
picture_type: PictureType,
description: Option<impl Into<Vec<u8>>>,
) -> Result<&mut Self> {
let picture = picture.as_ref();
let description = description.map(CString::new).transpose()?;
unsafe {
crate::ffi::ope_comments_add_picture_from_memory(
self.ptr,
picture.as_ptr() as *const i8,
picture.len(),
picture_type as i32,
description.map(|d| d.as_ptr()).unwrap_or(std::ptr::null()),
)
}
.check_result()?;
Ok(self)
}
}
impl Comments {
pub fn ptr(&self) -> *mut crate::ffi::OggOpusComments {
self.ptr
}
}
impl Clone for Comments {
fn clone(&self) -> Self {
let ptr = unsafe { crate::ffi::ope_comments_copy(self.ptr) };
assert!(!ptr.is_null());
Self { ptr }
}
}
impl Default for Comments {
fn default() -> Self {
Comments::create()
}
}
#[derive(Copy, Clone, Debug, Default)]
#[repr(i32)]
pub enum PictureType {
Other = 0,
FileIcon32 = 1,
OtherFileIcon = 2,
#[default]
FrontCover = 3,
BackCover = 4,
LeafletPage = 5,
Media = 6,
LeadArtist = 7,
Artist = 8,
Conductor = 9,
BandOrchestra = 10,
Composer = 11,
Lyricist = 12,
Location = 13,
DuringRecording = 14,
DuringPerformance = 15,
ScreenCapture = 16,
BrightColoredFish = 17,
Illustration = 18,
ArtistLogotype = 19,
PublisherLogotype = 20,
}
macro_rules! tag_names {
{
$( #[$enum_attr:meta] )*
$vis:vis enum $enum_name:ident {
$( $(#[$attr:meta] )*
$variant:ident = $val:literal ),* $(,)?
}
} => {
$(#[$enum_attr])*
$vis enum $enum_name {
$(
$( #[$attr] )*
$variant,
)*
}
impl $enum_name {
const fn as_cstr8(&self) -> &'static cstr8::CStr8 {
match self {
$(
$enum_name::$variant => cstr8::cstr8!($val),
)*
}
}
$vis const fn as_str(&self) -> &'static str {
self.as_cstr8().as_str()
}
$vis const fn as_cstr(&self) -> &'static CStr {
self.as_cstr8().as_c_str()
}
}
impl AsRef<str> for $enum_name {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<CStr> for $enum_name {
#[inline]
fn as_ref(&self) -> &CStr {
self.as_cstr()
}
}
impl Display for $enum_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
};
}
tag_names! {
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum RecommendedTag {
Title = "TITLE",
Version = "VERSION",
Album = "ALBUM",
TrackNumber = "TRACKNUMBER",
Artist = "ARTIST",
Performer = "PERFORMER",
Copyright = "COPYRIGHT",
License = "LICENSE",
Organization = "ORGANIZATION",
Description = "DESCRIPTION",
Genre = "GENRE",
Date = "DATE",
Location = "LOCATION",
Contact = "CONTACT",
Isrc = "ISRC",
}
}
tag_names! {
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub enum PicardTag {
AcoustID = "ACOUSTID_ID",
AcoustIDFingerprint = "ACOUSTID_FINGERPRINT",
Album = "ALBUM",
AlbumArtist = "ALBUMARTIST",
AlbumArtistSortOrder = "ALBUMARTISTSORT",
AlbumSortOrder = "ALBUMSORT",
Arranger = "ARRANGER",
Artist = "ARTIST",
ArtistSortOrder = "ARTISTSORT",
Artists = "ARTISTS",
ASIN = "ASIN",
Barcode = "BARCODE",
BPM = "BPM",
CatalogNumber = "CATALOGNUMBER",
Comment = "COMMENT",
Compilation = "COMPILATION",
Composer = "COMPOSER",
ComposerSortOrder = "COMPOSERSORT",
Conductor = "CONDUCTOR",
Copyright = "COPYRIGHT",
Director = "DIRECTOR",
DiscNumber = "DISCNUMBER",
DiscSubtitle = "DISCSUBTITLE",
EncodedBy = "ENCODEDBY",
EncoderSettings = "ENCODERSETTINGS",
Engineer = "ENGINEER",
Genre = "GENRE",
Grouping = "GROUPING",
InitialKey = "KEY",
Isrc = "ISRC",
Language = "LANGUAGE",
License = "LICENSE",
Lyricist = "LYRICIST",
Lyrics = "LYRICS",
Media = "MEDIA",
MixDJ = "DJMIXER",
Mixer = "MIXER",
Mood = "MOOD",
Movement = "MOVEMENTNAME",
MovementCount = "MOVEMENTTOTAL",
MovementNumber = "MOVEMENT",
MusicBrainzArtistID = "MUSICBRAINZ_ARTISTID",
MusicBrainzDiscID = "MUSICBRAINZ_DISCID",
MusicBrainzOriginalArtistID = "MUSICBRAINZ_ORIGINALARTISTID",
MusicBrainzOriginalReleaseID = "MUSICBRAINZ_ORIGINALALBUMID",
MusicBrainzRecordingID = "MUSICBRAINZ_TRACKID",
MusicBrainzReleaseArtistID = "MUSICBRAINZ_ALBUMARTISTID",
MusicBrainzReleaseGroupID = "MUSICBRAINZ_RELEASEGROUPID",
MusicBrainzReleaseID = "MUSICBRAINZ_ALBUMID",
MusicBrainzTrackID = "MUSICBRAINZ_RELEASETRACKID",
MusicBrainzTRMID = "MUSICBRAINZ_TRMID",
MusicBrainzWorkID = "MUSICBRAINZ_WORKID",
MusicIPFingerprint = "FINGERPRINT",
MusicIPPUID = "MUSICIP_PUID",
OriginalFilename = "ORIGINALFILENAME",
OriginalReleaseDate = "ORIGINALDATE",
OriginalReleaseYear = "ORIGINALYEAR",
Performer = "PERFORMER",
Producer = "PRODUCER",
Rating = "RATING",
RecordLabel = "LABEL",
ReleaseCountry = "RELEASECOUNTRY",
ReleaseDate = "DATE",
ReleaseStatus = "RELEASESTATUS",
ReleaseType = "RELEASETYPE",
Remixer = "REMIXER",
ReplayGainAlbumGain = "REPLAYGAIN_ALBUM_GAIN",
ReplayGainAlbumPeak = "REPLAYGAIN_ALBUM_PEAK",
ReplayGainAlbumRange = "REPLAYGAIN_ALBUM_RANGE",
ReplayGainReferenceLoudness = "REPLAYGAIN_REFERENCE_LOUDNESS",
ReplayGainTrackGain = "REPLAYGAIN_TRACK_GAIN",
ReplayGainTrackPeak = "REPLAYGAIN_TRACK_PEAK",
ReplayGainTrackRange = "REPLAYGAIN_TRACK_RANGE",
Script = "SCRIPT",
ShowWorkMovement = "SHOWMOVEMENT",
Subtitle = "SUBTITLE",
TotalDiscs = "TOTALDISCS",
TotalTracks = "TOTALTRACKS",
TrackNumber = "TRACKNUMBER",
TrackTitle = "TITLE",
TrackTitleSortOrder = "TITLESORT",
Website = "WEBSITE",
WorkTitle = "WORK",
Writer = "WRITER",
}
}