musikr: introduce this abstraction
Gets rid of a lot of the duplicate pin stuff
This commit is contained in:
parent
c8d645c282
commit
0cb1b3a309
11 changed files with 260 additions and 204 deletions
|
@ -1,28 +1,29 @@
|
|||
use super::bridge::CppAudioProperties;
|
||||
use super::this::{RefThis, This};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct AudioProperties<'file_ref> {
|
||||
this: Pin<&'file_ref CppAudioProperties>,
|
||||
this: RefThis<'file_ref, CppAudioProperties>,
|
||||
}
|
||||
|
||||
impl<'file_ref> AudioProperties<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CppAudioProperties>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CppAudioProperties>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn length_in_milliseconds(&self) -> i32 {
|
||||
self.this.as_ref().lengthInMilliseconds()
|
||||
self.this.pin().lengthInMilliseconds()
|
||||
}
|
||||
|
||||
pub fn bitrate(&self) -> i32 {
|
||||
self.this.as_ref().bitrate()
|
||||
self.this.pin().bitrate()
|
||||
}
|
||||
|
||||
pub fn sample_rate(&self) -> i32 {
|
||||
self.this.as_ref().sampleRate()
|
||||
self.this.pin().sampleRate()
|
||||
}
|
||||
|
||||
pub fn channels(&self) -> i32 {
|
||||
self.this.as_ref().channels()
|
||||
self.this.pin().channels()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,19 +5,20 @@ use super::id3v2::ID3v2Tag;
|
|||
use super::mpeg::MPEGFile;
|
||||
use super::ogg::OpusFile;
|
||||
use super::ogg::VorbisFile;
|
||||
use super::this::{RefThisMut, RefThis, This, ThisMut};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct File<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPFile>,
|
||||
this: RefThisMut<'file_ref, CPPFile>
|
||||
}
|
||||
|
||||
impl<'file_ref> File<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPFile>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPFile>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn audio_properties(&self) -> Option<AudioProperties<'file_ref>> {
|
||||
let props_ptr = self.this.as_ref().audioProperties();
|
||||
let props_ptr = self.this.pin().audioProperties();
|
||||
let props_ref = unsafe {
|
||||
// SAFETY:
|
||||
// - This points to a C++ FFI type ensured to be aligned by cxx's codegen.
|
||||
|
@ -27,8 +28,8 @@ impl<'file_ref> File<'file_ref> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
props_ptr.as_ref()
|
||||
};
|
||||
let props_pin = props_ref.map(|props| unsafe { Pin::new_unchecked(props) });
|
||||
props_pin.map(|props| AudioProperties::new(props))
|
||||
let props_this = props_ref.map(|props| unsafe { RefThis::new(props) });
|
||||
props_this.map(|this| AudioProperties::new(this))
|
||||
}
|
||||
|
||||
pub fn as_opus(&mut self) -> Option<OpusFile<'file_ref>> {
|
||||
|
@ -37,7 +38,7 @@ impl<'file_ref> File<'file_ref> {
|
|||
// This FFI function will be a simple C++ dynamic_cast, which checks if
|
||||
// the file can be cased down to an opus file. If the cast fails, a null
|
||||
// pointer is returned, which will be handled by as_ref's null checking.
|
||||
bridge::File_asOpus(self.this.as_mut().get_unchecked_mut() as *mut CPPFile)
|
||||
bridge::File_asOpus(self.this.ptr_mut() as *mut CPPFile)
|
||||
};
|
||||
let opus_ref = unsafe {
|
||||
// SAFETY:
|
||||
|
@ -48,8 +49,8 @@ impl<'file_ref> File<'file_ref> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
opus_file.as_mut()
|
||||
};
|
||||
let opus_pin = opus_ref.map(|opus| unsafe { Pin::new_unchecked(opus) });
|
||||
opus_pin.map(|opus| OpusFile::new(opus))
|
||||
let opus_this = opus_ref.map(|opus| unsafe { RefThisMut::new(opus) });
|
||||
opus_this.map(|this| OpusFile::new(this))
|
||||
}
|
||||
|
||||
pub fn as_vorbis(&mut self) -> Option<VorbisFile<'file_ref>> {
|
||||
|
@ -58,7 +59,7 @@ impl<'file_ref> File<'file_ref> {
|
|||
// This FFI function will be a simple C++ dynamic_cast, which checks if
|
||||
// the file can be cased down to an opus file. If the cast fails, a null
|
||||
// pointer is returned, which will be handled by as_ref's null checking.
|
||||
bridge::File_asVorbis(self.this.as_mut().get_unchecked_mut() as *mut CPPFile)
|
||||
bridge::File_asVorbis(self.this.ptr_mut() as *mut CPPFile)
|
||||
};
|
||||
let vorbis_ref = unsafe {
|
||||
// SAFETY:
|
||||
|
@ -69,8 +70,8 @@ impl<'file_ref> File<'file_ref> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
vorbis_file.as_mut()
|
||||
};
|
||||
let vorbis_pin = vorbis_ref.map(|vorbis| unsafe { Pin::new_unchecked(vorbis) });
|
||||
vorbis_pin.map(|vorbis| VorbisFile::new(vorbis))
|
||||
let vorbis_this = vorbis_ref.map(|vorbis| unsafe { RefThisMut::new(vorbis) });
|
||||
vorbis_this.map(|this| VorbisFile::new(this))
|
||||
}
|
||||
|
||||
pub fn as_flac(&mut self) -> Option<FLACFile<'file_ref>> {
|
||||
|
@ -79,7 +80,7 @@ impl<'file_ref> File<'file_ref> {
|
|||
// This FFI function will be a simple C++ dynamic_cast, which checks if
|
||||
// the file can be cased down to an opus file. If the cast fails, a null
|
||||
// pointer is returned, which will be handled by as_ref's null checking.
|
||||
bridge::File_asFLAC(self.this.as_mut().get_unchecked_mut() as *mut CPPFile)
|
||||
bridge::File_asFLAC(self.this.ptr_mut() as *mut CPPFile)
|
||||
};
|
||||
let flac_ref = unsafe {
|
||||
// SAFETY:
|
||||
|
@ -90,8 +91,8 @@ impl<'file_ref> File<'file_ref> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
flac_file.as_mut()
|
||||
};
|
||||
let flac_pin = flac_ref.map(|flac| unsafe { Pin::new_unchecked(flac) });
|
||||
flac_pin.map(|flac| FLACFile::new(flac))
|
||||
let flac_this = flac_ref.map(|flac| unsafe { RefThisMut::new(flac) });
|
||||
flac_this.map(|this| FLACFile::new(this))
|
||||
}
|
||||
|
||||
pub fn as_mpeg(&mut self) -> Option<MPEGFile<'file_ref>> {
|
||||
|
@ -100,7 +101,7 @@ impl<'file_ref> File<'file_ref> {
|
|||
// This FFI function will be a simple C++ dynamic_cast, which checks if
|
||||
// the file can be cased down to an MPEG file. If the cast fails, a null
|
||||
// pointer is returned, which will be handled by as_ref's null checking.
|
||||
bridge::File_asMPEG(self.this.as_mut().get_unchecked_mut() as *mut CPPFile)
|
||||
bridge::File_asMPEG(self.this.ptr_mut() as *mut CPPFile)
|
||||
};
|
||||
let mpeg_ref = unsafe {
|
||||
// SAFETY:
|
||||
|
@ -111,7 +112,7 @@ impl<'file_ref> File<'file_ref> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
mpeg_file.as_mut()
|
||||
};
|
||||
let mpeg_pin = mpeg_ref.map(|mpeg| unsafe { Pin::new_unchecked(mpeg) });
|
||||
mpeg_pin.map(|mpeg| MPEGFile::new(mpeg))
|
||||
let mpeg_this = mpeg_ref.map(|mpeg| unsafe { RefThisMut::new(mpeg) });
|
||||
mpeg_this.map(|this| MPEGFile::new(this))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::bridge::{self, CPPFileRef};
|
||||
use super::file::File;
|
||||
use super::iostream::{BridgedIOStream, IOStream};
|
||||
use super::this::RefThisMut;
|
||||
use cxx::UniquePtr;
|
||||
use std::pin::Pin;
|
||||
|
||||
|
@ -47,8 +48,8 @@ impl<'io> FileRef<'io> {
|
|||
// to this, ensuring that it will not be mutated as per the aliasing rules.
|
||||
file.as_mut()
|
||||
});
|
||||
let file_pin = file_ref.map(|file| unsafe { Pin::new_unchecked(file) });
|
||||
file_pin.map(|file| File::new(file))
|
||||
let file_this = file_ref.map(|file| unsafe { RefThisMut::new(file) });
|
||||
file_this.map(|this| File::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,63 +1,51 @@
|
|||
pub use super::bridge::CPPFLACFile;
|
||||
pub use super::bridge::CPPFLACPicture;
|
||||
use super::bridge::{CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data};
|
||||
use super::tk::ByteVector;
|
||||
use super::bridge::{CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data, CPPByteVector};
|
||||
use super::tk::{ByteVector, OwnedByteVector};
|
||||
pub use super::xiph::XiphComment;
|
||||
use super::this::{OwnedThis, RefThisMut, RefThis, This, ThisMut};
|
||||
use cxx::UniquePtr;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct FLACFile<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPFLACFile>,
|
||||
this: RefThisMut<'file_ref, CPPFLACFile>
|
||||
}
|
||||
|
||||
impl<'file_ref> FLACFile<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPFLACFile>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPFLACFile>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn xiph_comments(&mut self) -> Option<XiphComment<'file_ref>> {
|
||||
let this = self.this.as_mut();
|
||||
let tag = this.xiphComment(false);
|
||||
let tag = self.this.pin_mut().xiphComment(false);
|
||||
let tag_ref = unsafe {
|
||||
// SAFETY: This pointer is a valid type, and can only used and accessed
|
||||
// via this function and thus cannot be mutated, satisfying the aliasing rules.
|
||||
tag.as_mut()
|
||||
};
|
||||
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) });
|
||||
tag_pin.map(|tag| XiphComment::new(tag))
|
||||
let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
|
||||
tag_this.map(|this| XiphComment::new(this))
|
||||
}
|
||||
|
||||
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
|
||||
let pictures = FLACFile_pictureList(self.this.as_mut());
|
||||
PictureList::new(pictures)
|
||||
pub fn picture_list(&mut self) -> Option<PictureList<'file_ref>> {
|
||||
let pictures = FLACFile_pictureList(self.this.pin_mut());
|
||||
let this = unsafe { OwnedThis::new(pictures) };
|
||||
this.map(|this| PictureList::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PictureList<'file_ref> {
|
||||
// PictureList is implicitly tied to the lifetime of the file_ref, despite us technically
|
||||
// """""owning"""" it.
|
||||
_data: PhantomData<&'file_ref CPPFLACPicture>,
|
||||
// Only in a UniquePtr because we can't marshal over ownership of the PictureList by itself over cxx.
|
||||
this: UniquePtr<CPPPictureList>,
|
||||
this: OwnedThis<'file_ref, CPPPictureList>,
|
||||
}
|
||||
|
||||
impl<'file_ref> PictureList<'file_ref> {
|
||||
pub(super) fn new(this: UniquePtr<CPPPictureList>) -> Self {
|
||||
Self {
|
||||
_data: PhantomData,
|
||||
this,
|
||||
}
|
||||
pub(super) fn new(this: OwnedThis<'file_ref, CPPPictureList>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<Picture<'file_ref>> {
|
||||
let pictures = PictureList_to_vector(unsafe {
|
||||
// SAFETY: This pin is only used in this unsafe scope.
|
||||
// The pin is used as a C++ this pointer in the ffi call, which does
|
||||
// not change address by C++ semantics.
|
||||
Pin::new_unchecked(self.this.as_ref().unwrap())
|
||||
});
|
||||
|
||||
let pictures = PictureList_to_vector(self.this.pin());
|
||||
let mut result = Vec::new();
|
||||
for picture_ptr in pictures.iter() {
|
||||
let picture_ptr = picture_ptr.get();
|
||||
|
@ -66,24 +54,25 @@ impl<'file_ref> PictureList<'file_ref> {
|
|||
// via this function and thus cannot be mutated, satisfying the aliasing rules.
|
||||
picture_ptr.as_ref().unwrap()
|
||||
};
|
||||
let picture_pin = unsafe { Pin::new_unchecked(picture_ref) };
|
||||
result.push(Picture::new(picture_pin));
|
||||
let picture_this = unsafe { RefThis::new(picture_ref) };
|
||||
result.push(Picture::new(picture_this));
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Picture<'file_ref> {
|
||||
this: Pin<&'file_ref CPPFLACPicture>,
|
||||
this: RefThis<'file_ref, CPPFLACPicture>,
|
||||
}
|
||||
|
||||
impl<'file_ref> Picture<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPFLACPicture>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CPPFLACPicture>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn data(&self) -> ByteVector<'file_ref> {
|
||||
let data = Picture_data(self.this);
|
||||
ByteVector::new(data)
|
||||
pub fn data(&self) -> Option<OwnedByteVector<'file_ref>> {
|
||||
let data = Picture_data(self.this.pin());
|
||||
let this = unsafe { OwnedThis::new(data) };
|
||||
this.map(|this| ByteVector::new(this))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,129 +1,126 @@
|
|||
use super::bridge::{
|
||||
self, CPPID3v2AttachedPictureFrame, CPPID3v2Frame, CPPID3v2FrameList, CPPID3v2Tag,
|
||||
CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame,
|
||||
CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame, CPPStringList, CPPByteVector,
|
||||
};
|
||||
use super::tk::{ByteVector, StringList};
|
||||
use cxx::UniquePtr;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use super::tk::{ByteVector, StringList, OwnedByteVector, OwnedStringList};
|
||||
use super::this::{OwnedThis, RefThisMut, RefThis, This};
|
||||
|
||||
pub struct ID3v2Tag<'file_ref> {
|
||||
this: Pin<&'file_ref CPPID3v2Tag>,
|
||||
}
|
||||
this: RefThisMut<'file_ref, CPPID3v2Tag>,
|
||||
}
|
||||
|
||||
impl<'file_ref> ID3v2Tag<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPID3v2Tag>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPID3v2Tag>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn frames(&self) -> FrameList<'file_ref> {
|
||||
let frames = bridge::Tag_frameList(self.this.as_ref());
|
||||
FrameList::new(frames)
|
||||
pub fn frames(&self) -> Option<FrameList<'file_ref>> {
|
||||
let frames = bridge::Tag_frameList(self.this.pin());
|
||||
let this = unsafe { OwnedThis::new(frames) };
|
||||
this.map(|this| FrameList::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FrameList<'file_ref> {
|
||||
_data: PhantomData<&'file_ref CPPID3v2FrameList>,
|
||||
this: UniquePtr<CPPID3v2FrameList>,
|
||||
this: OwnedThis<'file_ref, CPPID3v2FrameList>,
|
||||
}
|
||||
|
||||
impl<'file_ref> FrameList<'file_ref> {
|
||||
pub(super) fn new(this: UniquePtr<CPPID3v2FrameList>) -> Self {
|
||||
Self {
|
||||
_data: PhantomData,
|
||||
this,
|
||||
}
|
||||
pub(super) fn new(this: OwnedThis<'file_ref, CPPID3v2FrameList>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<Frame<'file_ref>> {
|
||||
let this = unsafe { Pin::new_unchecked(self.this.as_ref().unwrap()) };
|
||||
let frames = bridge::FrameList_to_vector(this);
|
||||
let frames = bridge::FrameList_to_vector(self.this.pin());
|
||||
frames
|
||||
.iter()
|
||||
.map(|frame| {
|
||||
let frame_ptr = frame.get();
|
||||
let frame_ref = unsafe { frame_ptr.as_ref().unwrap() };
|
||||
let frame_pin = unsafe { Pin::new_unchecked(frame_ref) };
|
||||
Frame::new(frame_pin)
|
||||
let frame_this = unsafe { RefThis::new(frame_ref) };
|
||||
Frame::new(frame_this)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Frame<'file_ref> {
|
||||
this: Pin<&'file_ref CPPID3v2Frame>,
|
||||
this: RefThis<'file_ref, CPPID3v2Frame>,
|
||||
}
|
||||
|
||||
impl<'file_ref> Frame<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPID3v2Frame>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CPPID3v2Frame>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn as_text_identification(&mut self) -> Option<TextIdentificationFrame<'file_ref>> {
|
||||
let frame = unsafe { bridge::Frame_asTextIdentification(self.this.as_ref().get_ref()) };
|
||||
let frame = unsafe { bridge::Frame_asTextIdentification(self.this.ptr()) };
|
||||
let frame_ref = unsafe { frame.as_ref() };
|
||||
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) });
|
||||
frame_pin.map(|frame| TextIdentificationFrame::new(frame))
|
||||
let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
|
||||
frame_this.map(|this| TextIdentificationFrame::new(this))
|
||||
}
|
||||
|
||||
pub fn as_user_text_identification(
|
||||
&mut self,
|
||||
) -> Option<UserTextIdentificationFrame<'file_ref>> {
|
||||
let frame = unsafe { bridge::Frame_asUserTextIdentification(self.this.as_ref().get_ref()) };
|
||||
let frame = unsafe { bridge::Frame_asUserTextIdentification(self.this.ptr()) };
|
||||
let frame_ref = unsafe { frame.as_ref() };
|
||||
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) });
|
||||
frame_pin.map(|frame| UserTextIdentificationFrame::new(frame))
|
||||
let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
|
||||
frame_this.map(|this| UserTextIdentificationFrame::new(this))
|
||||
}
|
||||
|
||||
pub fn as_attached_picture(&mut self) -> Option<AttachedPictureFrame<'file_ref>> {
|
||||
let frame = unsafe { bridge::Frame_asAttachedPicture(self.this.as_ref().get_ref()) };
|
||||
let frame = unsafe { bridge::Frame_asAttachedPicture(self.this.ptr()) };
|
||||
let frame_ref = unsafe { frame.as_ref() };
|
||||
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) });
|
||||
frame_pin.map(|frame| AttachedPictureFrame::new(frame))
|
||||
let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
|
||||
frame_this.map(|this| AttachedPictureFrame::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextIdentificationFrame<'file_ref> {
|
||||
this: Pin<&'file_ref CPPID3v2TextIdentificationFrame>,
|
||||
this: RefThis<'file_ref, CPPID3v2TextIdentificationFrame>,
|
||||
}
|
||||
|
||||
impl<'file_ref> TextIdentificationFrame<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPID3v2TextIdentificationFrame>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CPPID3v2TextIdentificationFrame>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn field_list(&self) -> StringList<'file_ref> {
|
||||
let field_list = bridge::TextIdentificationFrame_fieldList(self.this);
|
||||
StringList::owned(field_list)
|
||||
pub fn field_list(&self) -> Option<OwnedStringList<'file_ref>> {
|
||||
let field_list = bridge::TextIdentificationFrame_fieldList(self.this.pin());
|
||||
let this = unsafe { OwnedThis::new(field_list) };
|
||||
this.map(|this| StringList::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UserTextIdentificationFrame<'file_ref> {
|
||||
this: Pin<&'file_ref CPPID3v2UserTextIdentificationFrame>,
|
||||
this: RefThis<'file_ref, CPPID3v2UserTextIdentificationFrame>,
|
||||
}
|
||||
|
||||
impl<'file_ref> UserTextIdentificationFrame<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPID3v2UserTextIdentificationFrame>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CPPID3v2UserTextIdentificationFrame>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn values(&self) -> StringList<'file_ref> {
|
||||
let values = bridge::UserTextIdentificationFrame_fieldList(self.this);
|
||||
StringList::owned(values)
|
||||
pub fn values(&self) -> Option<OwnedStringList<'file_ref>> {
|
||||
let values = bridge::UserTextIdentificationFrame_fieldList(self.this.pin());
|
||||
let this = unsafe { OwnedThis::new(values) };
|
||||
this.map(|this| StringList::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AttachedPictureFrame<'file_ref> {
|
||||
this: Pin<&'file_ref CPPID3v2AttachedPictureFrame>,
|
||||
this: RefThis<'file_ref, CPPID3v2AttachedPictureFrame>,
|
||||
}
|
||||
|
||||
impl<'file_ref> AttachedPictureFrame<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPID3v2AttachedPictureFrame>) -> Self {
|
||||
pub(super) fn new(this: RefThis<'file_ref, CPPID3v2AttachedPictureFrame>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn picture(&self) -> ByteVector<'file_ref> {
|
||||
let picture = bridge::AttachedPictureFrame_picture(self.this.as_ref());
|
||||
ByteVector::new(picture)
|
||||
pub fn picture(&self) -> Option<OwnedByteVector<'file_ref>> {
|
||||
let picture = bridge::AttachedPictureFrame_picture(self.this.pin());
|
||||
let this = unsafe { OwnedThis::new(picture) };
|
||||
this.map(|this| ByteVector::new(this))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,3 +10,4 @@ pub mod mpeg;
|
|||
pub mod ogg;
|
||||
pub mod tk;
|
||||
pub mod xiph;
|
||||
pub mod this;
|
|
@ -1,20 +1,21 @@
|
|||
use super::bridge::{self, CPPMPEGFile};
|
||||
use super::id3v2::ID3v2Tag;
|
||||
use super::this::{RefThisMut, This, ThisMut};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct MPEGFile<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPMPEGFile>,
|
||||
this: RefThisMut<'file_ref, CPPMPEGFile>,
|
||||
}
|
||||
|
||||
impl<'file_ref> MPEGFile<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPMPEGFile>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPMPEGFile>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn id3v2_tag(&mut self) -> Option<ID3v2Tag<'file_ref>> {
|
||||
let tag = self.this.as_mut().ID3v2Tag(false);
|
||||
let tag_ref = unsafe { tag.as_ref() };
|
||||
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) });
|
||||
tag_pin.map(|tag| ID3v2Tag::new(tag))
|
||||
let tag = self.this.pin_mut().ID3v2Tag(false);
|
||||
let tag_ref = unsafe { tag.as_mut() };
|
||||
let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
|
||||
tag_this.map(|this| ID3v2Tag::new(this))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +1,46 @@
|
|||
pub use super::bridge::{CPPOpusFile, CPPVorbisFile};
|
||||
use super::xiph::XiphComment;
|
||||
use super::this::{RefThisMut, RefThis, This};
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct VorbisFile<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPVorbisFile>,
|
||||
this: RefThisMut<'file_ref, CPPVorbisFile>,
|
||||
}
|
||||
|
||||
impl<'file_ref> VorbisFile<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPVorbisFile>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPVorbisFile>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
|
||||
let this = self.this.as_ref();
|
||||
let tag = this.vorbisTag();
|
||||
let tag = self.this.pin().vorbisTag();
|
||||
let tag_ref = unsafe {
|
||||
// SAFETY: This pointer is a valid type, and can only used and accessed
|
||||
// via this function and thus cannot be mutated, satisfying the aliasing rules.
|
||||
tag.as_mut()
|
||||
};
|
||||
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) });
|
||||
tag_pin.map(|tag| XiphComment::new(tag))
|
||||
let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
|
||||
tag_this.map(|this| XiphComment::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OpusFile<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPOpusFile>,
|
||||
this: RefThisMut<'file_ref, CPPOpusFile>,
|
||||
}
|
||||
|
||||
impl<'file_ref> OpusFile<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPOpusFile>) -> Self {
|
||||
pub(super) fn new(this: RefThisMut<'file_ref, CPPOpusFile>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
|
||||
let this = self.this.as_ref();
|
||||
let tag = this.opusTag();
|
||||
let tag = self.this.pin().opusTag();
|
||||
let tag_ref = unsafe {
|
||||
// SAFETY: This pointer is a valid type, and can only used and accessed
|
||||
// via this function and thus cannot be mutated, satisfying the aliasing rules.
|
||||
tag.as_mut()
|
||||
};
|
||||
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) });
|
||||
tag_pin.map(|tag| XiphComment::new(tag))
|
||||
let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
|
||||
tag_this.map(|this| XiphComment::new(this))
|
||||
}
|
||||
}
|
||||
|
|
94
musikr/src/main/jni/src/taglib/this.rs
Normal file
94
musikr/src/main/jni/src/taglib/this.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use cxx::{UniquePtr, memory::UniquePtrTarget};
|
||||
|
||||
pub trait This<'file_ref, T> {
|
||||
fn pin(&self) -> Pin<&T>;
|
||||
}
|
||||
|
||||
pub trait ThisMut<'file_ref, T> : This<'file_ref, T> {
|
||||
fn pin_mut(&mut self) -> Pin<&mut T>;
|
||||
}
|
||||
|
||||
pub struct RefThis<'file_ref, T> {
|
||||
this: &'file_ref T
|
||||
}
|
||||
|
||||
impl<'file_ref, T> RefThis<'file_ref, T> {
|
||||
pub unsafe fn new(this: &'file_ref T) -> Self {
|
||||
// Rough informal contact is that the reference points to a C++ object
|
||||
// that will live and not move for as long as 'file_ref.
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> *const T {
|
||||
self.this as *const T
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref, T> This<'file_ref, T> for RefThis<'file_ref, T> {
|
||||
fn pin(&self) -> Pin<&T> {
|
||||
unsafe { Pin::new_unchecked(self.this) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RefThisMut<'file_ref, T> {
|
||||
this: &'file_ref mut T,
|
||||
}
|
||||
|
||||
impl<'file_ref, T> RefThisMut<'file_ref, T> {
|
||||
pub unsafe fn new(this: &'file_ref mut T) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn ptr(&self) -> *const T {
|
||||
self.this as *const T
|
||||
}
|
||||
|
||||
pub fn ptr_mut(&mut self) -> *mut T {
|
||||
self.this as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref, T> This<'file_ref, T> for RefThisMut<'file_ref, T> {
|
||||
fn pin(&self) -> Pin<&T> {
|
||||
unsafe { Pin::new_unchecked(self.this) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref, T> ThisMut<'file_ref, T> for RefThisMut<'file_ref, T> {
|
||||
fn pin_mut(&mut self) -> Pin<&mut T> {
|
||||
unsafe { Pin::new_unchecked(self.this) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OwnedThis<'file_ref, T : UniquePtrTarget> {
|
||||
_data: PhantomData<&'file_ref ()>,
|
||||
this: UniquePtr<T>,
|
||||
}
|
||||
|
||||
impl<'file_ref, T : UniquePtrTarget> OwnedThis<'file_ref, T> {
|
||||
pub unsafe fn new(this: UniquePtr<T>) -> Option<Self> {
|
||||
if !this.is_null() {
|
||||
Some(Self {
|
||||
_data: PhantomData,
|
||||
this,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref, T : UniquePtrTarget> This<'file_ref, T> for OwnedThis<'file_ref, T> {
|
||||
fn pin(&self) -> Pin<&T> {
|
||||
unsafe { Pin::new_unchecked(self.this.as_ref().unwrap()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref, T : UniquePtrTarget> ThisMut<'file_ref, T> for OwnedThis<'file_ref, T> {
|
||||
fn pin_mut(&mut self) -> Pin<&mut T> {
|
||||
self.this.as_mut().unwrap()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +1,24 @@
|
|||
use super::bridge::{self, CPPByteVector, CPPString, CPPStringList};
|
||||
use super::this::{RefThis, RefThisMut, This, OwnedThis};
|
||||
use cxx::{memory::UniquePtrTarget, UniquePtr};
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::{ffi::CStr, string::ToString};
|
||||
|
||||
enum This<'file_ref, T: UniquePtrTarget> {
|
||||
Owned {
|
||||
data: PhantomData<&'file_ref T>,
|
||||
this: UniquePtr<T>,
|
||||
},
|
||||
Ref {
|
||||
this: Pin<&'file_ref T>,
|
||||
},
|
||||
pub(super) struct String<'file_ref, T: This<'file_ref, CPPString>> {
|
||||
_data: PhantomData<&'file_ref ()>,
|
||||
this: T,
|
||||
}
|
||||
|
||||
pub struct String<'file_ref> {
|
||||
this: Pin<&'file_ref CPPString>,
|
||||
}
|
||||
|
||||
impl<'file_ref> String<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPString>) -> Self {
|
||||
Self { this }
|
||||
impl<'file_ref, T: This<'file_ref, CPPString>> String<'file_ref, T> {
|
||||
pub(super) fn new(this: T) -> Self {
|
||||
Self { _data: PhantomData, this }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'file_ref> ToString for String<'file_ref> {
|
||||
impl<'file_ref, T: This<'file_ref, CPPString>> ToString for String<'file_ref, T> {
|
||||
fn to_string(&self) -> std::string::String {
|
||||
let c_str = self.this.toCString(true);
|
||||
let c_str = self.this.pin().toCString(true);
|
||||
unsafe {
|
||||
// SAFETY:
|
||||
// - This is a C-string returned by a C++ method guaranteed to have
|
||||
|
@ -44,67 +36,47 @@ impl<'file_ref> ToString for String<'file_ref> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct StringList<'file_ref> {
|
||||
this: This<'file_ref, CPPStringList>,
|
||||
pub type OwnedString<'file_ref> = String<'file_ref, OwnedThis<'file_ref, CPPString>>;
|
||||
pub type RefString<'file_ref> = String<'file_ref, RefThis<'file_ref, CPPString>>;
|
||||
pub type RefStringMut<'file_ref> = String<'file_ref, RefThisMut<'file_ref, CPPString>>;
|
||||
pub(super) struct StringList<'file_ref, T: This<'file_ref, CPPStringList>> {
|
||||
_data: PhantomData<&'file_ref ()>,
|
||||
this: T,
|
||||
}
|
||||
|
||||
impl<'file_ref> StringList<'file_ref> {
|
||||
pub(super) fn owned(this: UniquePtr<CPPStringList>) -> Self {
|
||||
Self {
|
||||
this: This::Owned {
|
||||
data: PhantomData,
|
||||
this,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub type OwnedStringList<'file_ref> = StringList<'file_ref, OwnedThis<'file_ref, CPPStringList>>;
|
||||
pub type RefStringList<'file_ref> = StringList<'file_ref, RefThis<'file_ref, CPPStringList>>;
|
||||
pub type RefStringListMut<'file_ref> = StringList<'file_ref, RefThisMut<'file_ref, CPPStringList>>;
|
||||
|
||||
pub(super) fn reference(this: Pin<&'file_ref CPPStringList>) -> Self {
|
||||
Self {
|
||||
this: This::Ref { this },
|
||||
}
|
||||
impl<'file_ref, T: This<'file_ref, CPPStringList>> StringList<'file_ref, T> {
|
||||
pub(super) fn new(this: T) -> Self {
|
||||
Self { _data: PhantomData, this }
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<std::string::String> {
|
||||
let pin = match &self.this {
|
||||
This::Owned { this, .. } => unsafe { Pin::new_unchecked(this.as_ref().unwrap()) },
|
||||
This::Ref { this } => *this,
|
||||
};
|
||||
let cxx_values = bridge::StringList_to_vector(pin);
|
||||
let cxx_values = bridge::StringList_to_vector(self.this.pin());
|
||||
cxx_values
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let this = unsafe { Pin::new_unchecked(value) };
|
||||
let this = unsafe { RefThis::new(value) };
|
||||
String::new(this).to_string()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ByteVector<'file_ref> {
|
||||
// ByteVector is implicitly tied to the lifetime of the parent that owns it,
|
||||
// so we need to track that lifetime. Only reason why it's a UniquePtr is because
|
||||
// we can't marshal over ownership of the ByteVector by itself over cxx.
|
||||
pub struct ByteVector<'file_ref, T: This<'file_ref, CPPByteVector>> {
|
||||
_data: PhantomData<&'file_ref CPPByteVector>,
|
||||
this: UniquePtr<CPPByteVector>,
|
||||
this: T,
|
||||
}
|
||||
|
||||
impl<'file_ref> ByteVector<'file_ref> {
|
||||
pub(super) fn new(this: UniquePtr<CPPByteVector>) -> Self {
|
||||
Self {
|
||||
_data: PhantomData,
|
||||
this,
|
||||
}
|
||||
impl<'file_ref, T: This<'file_ref, CPPByteVector>> ByteVector<'file_ref, T> {
|
||||
pub(super) fn new(this: T) -> Self {
|
||||
Self { _data: PhantomData, this }
|
||||
}
|
||||
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
let this_ref = self.this.as_ref().unwrap();
|
||||
let this = unsafe {
|
||||
// SAFETY:
|
||||
// - This pin is only used in this unsafe scope.
|
||||
// - The pin is used as a C++ this pointer in the ffi call, which does
|
||||
// not change address by C++ semantics.
|
||||
Pin::new_unchecked(this_ref)
|
||||
};
|
||||
let this = self.this.pin();
|
||||
let size = this.size().try_into().unwrap();
|
||||
let data = this.data();
|
||||
// Re-cast to u8
|
||||
|
@ -118,3 +90,7 @@ impl<'file_ref> ByteVector<'file_ref> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type OwnedByteVector<'file_ref> = ByteVector<'file_ref, OwnedThis<'file_ref, CPPByteVector>>;
|
||||
pub type RefByteVector<'file_ref> = ByteVector<'file_ref, RefThis<'file_ref, CPPByteVector>>;
|
||||
pub type RefByteVectorMut<'file_ref> = ByteVector<'file_ref, RefThisMut<'file_ref, CPPByteVector>>;
|
||||
|
|
|
@ -2,36 +2,32 @@ pub use super::bridge::CPPXiphComment;
|
|||
use super::bridge::{CPPFieldListMap, FieldListMap_to_entries, XiphComment_pictureList};
|
||||
pub use super::flac::PictureList;
|
||||
use super::tk;
|
||||
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut, This};
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
|
||||
pub struct XiphComment<'file_ref> {
|
||||
this: Pin<&'file_ref mut CPPXiphComment>,
|
||||
this: RefThisMut<'file_ref, CPPXiphComment>,
|
||||
}
|
||||
|
||||
impl<'file_ref> XiphComment<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref mut CPPXiphComment>) -> Self {
|
||||
pub fn new(this: RefThisMut<'file_ref, CPPXiphComment>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
|
||||
pub fn field_list_map<'slf>(&'slf self) -> FieldListMap<'file_ref> {
|
||||
// To call the method we need, we have to get our mut reference down to an immutable
|
||||
// reference. The safe API can do this, but shortens the lifecycle to at most self, even
|
||||
// though the reference really lives as long as file_ref. Sadly, this requires us to transmute
|
||||
// to extend the lifecycle back. This new pointer is really unsafe (we now have both a mut
|
||||
// and an immutable reference to the same object), but it's dropped after this call.
|
||||
// The value returned is unable to actually mutate this object, so it's safe.
|
||||
let this_ref: &'slf CPPXiphComment = self.this.as_ref().get_ref();
|
||||
let extended_ref: &'file_ref CPPXiphComment = unsafe { std::mem::transmute(this_ref) };
|
||||
let this: Pin<&'file_ref CPPXiphComment> = unsafe { Pin::new_unchecked(extended_ref) };
|
||||
let map = this.fieldListMap();
|
||||
let map_pin = unsafe { Pin::new_unchecked(map) };
|
||||
let map: &'slf CPPFieldListMap = self.this.pin().fieldListMap();
|
||||
// CPPFieldListMap exists for as long as the XiphComment, so we can transmute it
|
||||
// to the file_ref lifetime.
|
||||
let extended_map: &'file_ref CPPFieldListMap = unsafe { std::mem::transmute(map) };
|
||||
let map_pin = unsafe { Pin::new_unchecked(extended_map) };
|
||||
FieldListMap::new(map_pin)
|
||||
}
|
||||
|
||||
pub fn picture_list(&mut self) -> PictureList<'file_ref> {
|
||||
let pictures = XiphComment_pictureList(self.this.as_mut());
|
||||
PictureList::new(pictures)
|
||||
pub fn picture_list(&mut self) -> Option<PictureList<'file_ref>> {
|
||||
let pictures = XiphComment_pictureList(self.this.pin_mut());
|
||||
let pictures_this = unsafe { OwnedThis::new(pictures) };
|
||||
pictures_this.map(|this| PictureList::new(this))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +36,7 @@ pub struct FieldListMap<'file_ref> {
|
|||
}
|
||||
|
||||
impl<'file_ref> FieldListMap<'file_ref> {
|
||||
pub(super) fn new(this: Pin<&'file_ref CPPFieldListMap>) -> Self {
|
||||
pub fn new(this: Pin<&'file_ref CPPFieldListMap>) -> Self {
|
||||
Self { this }
|
||||
}
|
||||
}
|
||||
|
@ -59,11 +55,11 @@ impl<'file_ref> FieldListMap<'file_ref> {
|
|||
// of self.
|
||||
let property_pin = unsafe { Pin::new_unchecked(property) };
|
||||
let key_ref = property_pin.key();
|
||||
let key_pin = unsafe { Pin::new_unchecked(key_ref) };
|
||||
let key = tk::String::new(key_pin).to_string();
|
||||
let key_this = unsafe { RefThis::new(key_ref) };
|
||||
let key = tk::String::new(key_this).to_string();
|
||||
let value_ref = property_pin.value();
|
||||
let value_pin = unsafe { Pin::new_unchecked(value_ref) };
|
||||
let value = tk::StringList::reference(value_pin).to_vec();
|
||||
let value_this = unsafe { RefThis::new(value_ref) };
|
||||
let value = tk::StringList::new(value_this).to_vec();
|
||||
(key, value)
|
||||
})
|
||||
.collect()
|
||||
|
|
Loading…
Reference in a new issue