musikr: introduce this abstraction

Gets rid of a lot of the duplicate pin stuff
This commit is contained in:
Alexander Capehart 2025-02-15 22:18:01 -07:00
parent c8d645c282
commit 0cb1b3a309
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 260 additions and 204 deletions

View file

@ -1,28 +1,29 @@
use super::bridge::CppAudioProperties; use super::bridge::CppAudioProperties;
use super::this::{RefThis, This};
use std::pin::Pin; use std::pin::Pin;
pub struct AudioProperties<'file_ref> { pub struct AudioProperties<'file_ref> {
this: Pin<&'file_ref CppAudioProperties>, this: RefThis<'file_ref, CppAudioProperties>,
} }
impl<'file_ref> AudioProperties<'file_ref> { 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 } Self { this }
} }
pub fn length_in_milliseconds(&self) -> i32 { pub fn length_in_milliseconds(&self) -> i32 {
self.this.as_ref().lengthInMilliseconds() self.this.pin().lengthInMilliseconds()
} }
pub fn bitrate(&self) -> i32 { pub fn bitrate(&self) -> i32 {
self.this.as_ref().bitrate() self.this.pin().bitrate()
} }
pub fn sample_rate(&self) -> i32 { pub fn sample_rate(&self) -> i32 {
self.this.as_ref().sampleRate() self.this.pin().sampleRate()
} }
pub fn channels(&self) -> i32 { pub fn channels(&self) -> i32 {
self.this.as_ref().channels() self.this.pin().channels()
} }
} }

View file

@ -5,19 +5,20 @@ use super::id3v2::ID3v2Tag;
use super::mpeg::MPEGFile; use super::mpeg::MPEGFile;
use super::ogg::OpusFile; use super::ogg::OpusFile;
use super::ogg::VorbisFile; use super::ogg::VorbisFile;
use super::this::{RefThisMut, RefThis, This, ThisMut};
use std::pin::Pin; use std::pin::Pin;
pub struct File<'file_ref> { pub struct File<'file_ref> {
this: Pin<&'file_ref mut CPPFile>, this: RefThisMut<'file_ref, CPPFile>
} }
impl<'file_ref> File<'file_ref> { 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 } Self { this }
} }
pub fn audio_properties(&self) -> Option<AudioProperties<'file_ref>> { 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 { let props_ref = unsafe {
// SAFETY: // SAFETY:
// - This points to a C++ FFI type ensured to be aligned by cxx's codegen. // - 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
props_ptr.as_ref() props_ptr.as_ref()
}; };
let props_pin = props_ref.map(|props| unsafe { Pin::new_unchecked(props) }); let props_this = props_ref.map(|props| unsafe { RefThis::new(props) });
props_pin.map(|props| AudioProperties::new(props)) props_this.map(|this| AudioProperties::new(this))
} }
pub fn as_opus(&mut self) -> Option<OpusFile<'file_ref>> { 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 // 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 // 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. // 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 { let opus_ref = unsafe {
// SAFETY: // 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
opus_file.as_mut() opus_file.as_mut()
}; };
let opus_pin = opus_ref.map(|opus| unsafe { Pin::new_unchecked(opus) }); let opus_this = opus_ref.map(|opus| unsafe { RefThisMut::new(opus) });
opus_pin.map(|opus| OpusFile::new(opus)) opus_this.map(|this| OpusFile::new(this))
} }
pub fn as_vorbis(&mut self) -> Option<VorbisFile<'file_ref>> { 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 // 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 // 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. // 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 { let vorbis_ref = unsafe {
// SAFETY: // 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
vorbis_file.as_mut() vorbis_file.as_mut()
}; };
let vorbis_pin = vorbis_ref.map(|vorbis| unsafe { Pin::new_unchecked(vorbis) }); let vorbis_this = vorbis_ref.map(|vorbis| unsafe { RefThisMut::new(vorbis) });
vorbis_pin.map(|vorbis| VorbisFile::new(vorbis)) vorbis_this.map(|this| VorbisFile::new(this))
} }
pub fn as_flac(&mut self) -> Option<FLACFile<'file_ref>> { 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 // 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 // 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. // 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 { let flac_ref = unsafe {
// SAFETY: // 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
flac_file.as_mut() flac_file.as_mut()
}; };
let flac_pin = flac_ref.map(|flac| unsafe { Pin::new_unchecked(flac) }); let flac_this = flac_ref.map(|flac| unsafe { RefThisMut::new(flac) });
flac_pin.map(|flac| FLACFile::new(flac)) flac_this.map(|this| FLACFile::new(this))
} }
pub fn as_mpeg(&mut self) -> Option<MPEGFile<'file_ref>> { 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 // 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 // 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. // 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 { let mpeg_ref = unsafe {
// SAFETY: // 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
mpeg_file.as_mut() mpeg_file.as_mut()
}; };
let mpeg_pin = mpeg_ref.map(|mpeg| unsafe { Pin::new_unchecked(mpeg) }); let mpeg_this = mpeg_ref.map(|mpeg| unsafe { RefThisMut::new(mpeg) });
mpeg_pin.map(|mpeg| MPEGFile::new(mpeg)) mpeg_this.map(|this| MPEGFile::new(this))
} }
} }

View file

@ -1,6 +1,7 @@
use super::bridge::{self, CPPFileRef}; use super::bridge::{self, CPPFileRef};
use super::file::File; use super::file::File;
use super::iostream::{BridgedIOStream, IOStream}; use super::iostream::{BridgedIOStream, IOStream};
use super::this::RefThisMut;
use cxx::UniquePtr; use cxx::UniquePtr;
use std::pin::Pin; 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. // to this, ensuring that it will not be mutated as per the aliasing rules.
file.as_mut() file.as_mut()
}); });
let file_pin = file_ref.map(|file| unsafe { Pin::new_unchecked(file) }); let file_this = file_ref.map(|file| unsafe { RefThisMut::new(file) });
file_pin.map(|file| File::new(file)) file_this.map(|this| File::new(this))
} }
} }

View file

@ -1,63 +1,51 @@
pub use super::bridge::CPPFLACFile; pub use super::bridge::CPPFLACFile;
pub use super::bridge::CPPFLACPicture; pub use super::bridge::CPPFLACPicture;
use super::bridge::{CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data}; use super::bridge::{CPPPictureList, FLACFile_pictureList, PictureList_to_vector, Picture_data, CPPByteVector};
use super::tk::ByteVector; use super::tk::{ByteVector, OwnedByteVector};
pub use super::xiph::XiphComment; pub use super::xiph::XiphComment;
use super::this::{OwnedThis, RefThisMut, RefThis, This, ThisMut};
use cxx::UniquePtr; use cxx::UniquePtr;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
pub struct FLACFile<'file_ref> { pub struct FLACFile<'file_ref> {
this: Pin<&'file_ref mut CPPFLACFile>, this: RefThisMut<'file_ref, CPPFLACFile>
} }
impl<'file_ref> FLACFile<'file_ref> { 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 } Self { this }
} }
pub fn xiph_comments(&mut self) -> Option<XiphComment<'file_ref>> { pub fn xiph_comments(&mut self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_mut(); let tag = self.this.pin_mut().xiphComment(false);
let tag = this.xiphComment(false);
let tag_ref = unsafe { let tag_ref = unsafe {
// SAFETY: This pointer is a valid type, and can only used and accessed // 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. // via this function and thus cannot be mutated, satisfying the aliasing rules.
tag.as_mut() tag.as_mut()
}; };
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) }); let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
tag_pin.map(|tag| XiphComment::new(tag)) tag_this.map(|this| XiphComment::new(this))
} }
pub fn picture_list(&mut self) -> PictureList<'file_ref> { pub fn picture_list(&mut self) -> Option<PictureList<'file_ref>> {
let pictures = FLACFile_pictureList(self.this.as_mut()); let pictures = FLACFile_pictureList(self.this.pin_mut());
PictureList::new(pictures) let this = unsafe { OwnedThis::new(pictures) };
this.map(|this| PictureList::new(this))
} }
} }
pub struct PictureList<'file_ref> { pub struct PictureList<'file_ref> {
// PictureList is implicitly tied to the lifetime of the file_ref, despite us technically this: OwnedThis<'file_ref, CPPPictureList>,
// """""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>,
} }
impl<'file_ref> PictureList<'file_ref> { impl<'file_ref> PictureList<'file_ref> {
pub(super) fn new(this: UniquePtr<CPPPictureList>) -> Self { pub(super) fn new(this: OwnedThis<'file_ref, CPPPictureList>) -> Self {
Self { Self { this }
_data: PhantomData,
this,
}
} }
pub fn to_vec(&self) -> Vec<Picture<'file_ref>> { pub fn to_vec(&self) -> Vec<Picture<'file_ref>> {
let pictures = PictureList_to_vector(unsafe { let pictures = PictureList_to_vector(self.this.pin());
// 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 mut result = Vec::new(); let mut result = Vec::new();
for picture_ptr in pictures.iter() { for picture_ptr in pictures.iter() {
let picture_ptr = picture_ptr.get(); 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. // via this function and thus cannot be mutated, satisfying the aliasing rules.
picture_ptr.as_ref().unwrap() picture_ptr.as_ref().unwrap()
}; };
let picture_pin = unsafe { Pin::new_unchecked(picture_ref) }; let picture_this = unsafe { RefThis::new(picture_ref) };
result.push(Picture::new(picture_pin)); result.push(Picture::new(picture_this));
} }
result result
} }
} }
pub struct Picture<'file_ref> { pub struct Picture<'file_ref> {
this: Pin<&'file_ref CPPFLACPicture>, this: RefThis<'file_ref, CPPFLACPicture>,
} }
impl<'file_ref> Picture<'file_ref> { 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 } Self { this }
} }
pub fn data(&self) -> ByteVector<'file_ref> { pub fn data(&self) -> Option<OwnedByteVector<'file_ref>> {
let data = Picture_data(self.this); let data = Picture_data(self.this.pin());
ByteVector::new(data) let this = unsafe { OwnedThis::new(data) };
this.map(|this| ByteVector::new(this))
} }
} }

View file

@ -1,129 +1,126 @@
use super::bridge::{ use super::bridge::{
self, CPPID3v2AttachedPictureFrame, CPPID3v2Frame, CPPID3v2FrameList, CPPID3v2Tag, self, CPPID3v2AttachedPictureFrame, CPPID3v2Frame, CPPID3v2FrameList, CPPID3v2Tag,
CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame, CPPID3v2TextIdentificationFrame, CPPID3v2UserTextIdentificationFrame, CPPStringList, CPPByteVector,
}; };
use super::tk::{ByteVector, StringList}; use super::tk::{ByteVector, StringList, OwnedByteVector, OwnedStringList};
use cxx::UniquePtr; use super::this::{OwnedThis, RefThisMut, RefThis, This};
use std::marker::PhantomData;
use std::pin::Pin;
pub struct ID3v2Tag<'file_ref> { pub struct ID3v2Tag<'file_ref> {
this: Pin<&'file_ref CPPID3v2Tag>, this: RefThisMut<'file_ref, CPPID3v2Tag>,
} }
impl<'file_ref> ID3v2Tag<'file_ref> { 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 } Self { this }
} }
pub fn frames(&self) -> FrameList<'file_ref> { pub fn frames(&self) -> Option<FrameList<'file_ref>> {
let frames = bridge::Tag_frameList(self.this.as_ref()); let frames = bridge::Tag_frameList(self.this.pin());
FrameList::new(frames) let this = unsafe { OwnedThis::new(frames) };
this.map(|this| FrameList::new(this))
} }
} }
pub struct FrameList<'file_ref> { pub struct FrameList<'file_ref> {
_data: PhantomData<&'file_ref CPPID3v2FrameList>, this: OwnedThis<'file_ref, CPPID3v2FrameList>,
this: UniquePtr<CPPID3v2FrameList>,
} }
impl<'file_ref> FrameList<'file_ref> { impl<'file_ref> FrameList<'file_ref> {
pub(super) fn new(this: UniquePtr<CPPID3v2FrameList>) -> Self { pub(super) fn new(this: OwnedThis<'file_ref, CPPID3v2FrameList>) -> Self {
Self { Self { this }
_data: PhantomData,
this,
}
} }
pub fn to_vec(&self) -> Vec<Frame<'file_ref>> { 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(self.this.pin());
let frames = bridge::FrameList_to_vector(this);
frames frames
.iter() .iter()
.map(|frame| { .map(|frame| {
let frame_ptr = frame.get(); let frame_ptr = frame.get();
let frame_ref = unsafe { frame_ptr.as_ref().unwrap() }; let frame_ref = unsafe { frame_ptr.as_ref().unwrap() };
let frame_pin = unsafe { Pin::new_unchecked(frame_ref) }; let frame_this = unsafe { RefThis::new(frame_ref) };
Frame::new(frame_pin) Frame::new(frame_this)
}) })
.collect() .collect()
} }
} }
pub struct Frame<'file_ref> { pub struct Frame<'file_ref> {
this: Pin<&'file_ref CPPID3v2Frame>, this: RefThis<'file_ref, CPPID3v2Frame>,
} }
impl<'file_ref> Frame<'file_ref> { 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 } Self { this }
} }
pub fn as_text_identification(&mut self) -> Option<TextIdentificationFrame<'file_ref>> { 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_ref = unsafe { frame.as_ref() };
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) }); let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
frame_pin.map(|frame| TextIdentificationFrame::new(frame)) frame_this.map(|this| TextIdentificationFrame::new(this))
} }
pub fn as_user_text_identification( pub fn as_user_text_identification(
&mut self, &mut self,
) -> Option<UserTextIdentificationFrame<'file_ref>> { ) -> 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_ref = unsafe { frame.as_ref() };
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) }); let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
frame_pin.map(|frame| UserTextIdentificationFrame::new(frame)) frame_this.map(|this| UserTextIdentificationFrame::new(this))
} }
pub fn as_attached_picture(&mut self) -> Option<AttachedPictureFrame<'file_ref>> { 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_ref = unsafe { frame.as_ref() };
let frame_pin = frame_ref.map(|frame| unsafe { Pin::new_unchecked(frame) }); let frame_this = frame_ref.map(|frame| unsafe { RefThis::new(frame) });
frame_pin.map(|frame| AttachedPictureFrame::new(frame)) frame_this.map(|this| AttachedPictureFrame::new(this))
} }
} }
pub struct TextIdentificationFrame<'file_ref> { pub struct TextIdentificationFrame<'file_ref> {
this: Pin<&'file_ref CPPID3v2TextIdentificationFrame>, this: RefThis<'file_ref, CPPID3v2TextIdentificationFrame>,
} }
impl<'file_ref> TextIdentificationFrame<'file_ref> { 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 } Self { this }
} }
pub fn field_list(&self) -> StringList<'file_ref> { pub fn field_list(&self) -> Option<OwnedStringList<'file_ref>> {
let field_list = bridge::TextIdentificationFrame_fieldList(self.this); let field_list = bridge::TextIdentificationFrame_fieldList(self.this.pin());
StringList::owned(field_list) let this = unsafe { OwnedThis::new(field_list) };
this.map(|this| StringList::new(this))
} }
} }
pub struct UserTextIdentificationFrame<'file_ref> { pub struct UserTextIdentificationFrame<'file_ref> {
this: Pin<&'file_ref CPPID3v2UserTextIdentificationFrame>, this: RefThis<'file_ref, CPPID3v2UserTextIdentificationFrame>,
} }
impl<'file_ref> UserTextIdentificationFrame<'file_ref> { 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 } Self { this }
} }
pub fn values(&self) -> StringList<'file_ref> { pub fn values(&self) -> Option<OwnedStringList<'file_ref>> {
let values = bridge::UserTextIdentificationFrame_fieldList(self.this); let values = bridge::UserTextIdentificationFrame_fieldList(self.this.pin());
StringList::owned(values) let this = unsafe { OwnedThis::new(values) };
this.map(|this| StringList::new(this))
} }
} }
pub struct AttachedPictureFrame<'file_ref> { pub struct AttachedPictureFrame<'file_ref> {
this: Pin<&'file_ref CPPID3v2AttachedPictureFrame>, this: RefThis<'file_ref, CPPID3v2AttachedPictureFrame>,
} }
impl<'file_ref> AttachedPictureFrame<'file_ref> { 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 } Self { this }
} }
pub fn picture(&self) -> ByteVector<'file_ref> { pub fn picture(&self) -> Option<OwnedByteVector<'file_ref>> {
let picture = bridge::AttachedPictureFrame_picture(self.this.as_ref()); let picture = bridge::AttachedPictureFrame_picture(self.this.pin());
ByteVector::new(picture) let this = unsafe { OwnedThis::new(picture) };
this.map(|this| ByteVector::new(this))
} }
} }

View file

@ -10,3 +10,4 @@ pub mod mpeg;
pub mod ogg; pub mod ogg;
pub mod tk; pub mod tk;
pub mod xiph; pub mod xiph;
pub mod this;

View file

@ -1,20 +1,21 @@
use super::bridge::{self, CPPMPEGFile}; use super::bridge::{self, CPPMPEGFile};
use super::id3v2::ID3v2Tag; use super::id3v2::ID3v2Tag;
use super::this::{RefThisMut, This, ThisMut};
use std::pin::Pin; use std::pin::Pin;
pub struct MPEGFile<'file_ref> { pub struct MPEGFile<'file_ref> {
this: Pin<&'file_ref mut CPPMPEGFile>, this: RefThisMut<'file_ref, CPPMPEGFile>,
} }
impl<'file_ref> MPEGFile<'file_ref> { 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 } Self { this }
} }
pub fn id3v2_tag(&mut self) -> Option<ID3v2Tag<'file_ref>> { pub fn id3v2_tag(&mut self) -> Option<ID3v2Tag<'file_ref>> {
let tag = self.this.as_mut().ID3v2Tag(false); let tag = self.this.pin_mut().ID3v2Tag(false);
let tag_ref = unsafe { tag.as_ref() }; let tag_ref = unsafe { tag.as_mut() };
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) }); let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
tag_pin.map(|tag| ID3v2Tag::new(tag)) tag_this.map(|this| ID3v2Tag::new(this))
} }
} }

View file

@ -1,47 +1,46 @@
pub use super::bridge::{CPPOpusFile, CPPVorbisFile}; pub use super::bridge::{CPPOpusFile, CPPVorbisFile};
use super::xiph::XiphComment; use super::xiph::XiphComment;
use super::this::{RefThisMut, RefThis, This};
use std::pin::Pin; use std::pin::Pin;
pub struct VorbisFile<'file_ref> { pub struct VorbisFile<'file_ref> {
this: Pin<&'file_ref mut CPPVorbisFile>, this: RefThisMut<'file_ref, CPPVorbisFile>,
} }
impl<'file_ref> VorbisFile<'file_ref> { 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 } Self { this }
} }
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> { pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_ref(); let tag = self.this.pin().vorbisTag();
let tag = this.vorbisTag();
let tag_ref = unsafe { let tag_ref = unsafe {
// SAFETY: This pointer is a valid type, and can only used and accessed // 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. // via this function and thus cannot be mutated, satisfying the aliasing rules.
tag.as_mut() tag.as_mut()
}; };
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) }); let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
tag_pin.map(|tag| XiphComment::new(tag)) tag_this.map(|this| XiphComment::new(this))
} }
} }
pub struct OpusFile<'file_ref> { pub struct OpusFile<'file_ref> {
this: Pin<&'file_ref mut CPPOpusFile>, this: RefThisMut<'file_ref, CPPOpusFile>,
} }
impl<'file_ref> OpusFile<'file_ref> { 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 } Self { this }
} }
pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> { pub fn xiph_comments(&self) -> Option<XiphComment<'file_ref>> {
let this = self.this.as_ref(); let tag = self.this.pin().opusTag();
let tag = this.opusTag();
let tag_ref = unsafe { let tag_ref = unsafe {
// SAFETY: This pointer is a valid type, and can only used and accessed // 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. // via this function and thus cannot be mutated, satisfying the aliasing rules.
tag.as_mut() tag.as_mut()
}; };
let tag_pin = tag_ref.map(|tag| unsafe { Pin::new_unchecked(tag) }); let tag_this = tag_ref.map(|tag| unsafe { RefThisMut::new(tag) });
tag_pin.map(|tag| XiphComment::new(tag)) tag_this.map(|this| XiphComment::new(this))
} }
} }

View 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()
}
}

View file

@ -1,32 +1,24 @@
use super::bridge::{self, CPPByteVector, CPPString, CPPStringList}; use super::bridge::{self, CPPByteVector, CPPString, CPPStringList};
use super::this::{RefThis, RefThisMut, This, OwnedThis};
use cxx::{memory::UniquePtrTarget, UniquePtr}; use cxx::{memory::UniquePtrTarget, UniquePtr};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::{ffi::CStr, string::ToString}; use std::{ffi::CStr, string::ToString};
enum This<'file_ref, T: UniquePtrTarget> { pub(super) struct String<'file_ref, T: This<'file_ref, CPPString>> {
Owned { _data: PhantomData<&'file_ref ()>,
data: PhantomData<&'file_ref T>, this: T,
this: UniquePtr<T>,
},
Ref {
this: Pin<&'file_ref T>,
},
} }
pub struct String<'file_ref> { impl<'file_ref, T: This<'file_ref, CPPString>> String<'file_ref, T> {
this: Pin<&'file_ref CPPString>, pub(super) fn new(this: T) -> Self {
} Self { _data: PhantomData, this }
impl<'file_ref> String<'file_ref> {
pub(super) fn new(this: Pin<&'file_ref CPPString>) -> Self {
Self { 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 { fn to_string(&self) -> std::string::String {
let c_str = self.this.toCString(true); let c_str = self.this.pin().toCString(true);
unsafe { unsafe {
// SAFETY: // SAFETY:
// - This is a C-string returned by a C++ method guaranteed to have // - 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> { pub type OwnedString<'file_ref> = String<'file_ref, OwnedThis<'file_ref, CPPString>>;
this: This<'file_ref, CPPStringList>, 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 type OwnedStringList<'file_ref> = StringList<'file_ref, OwnedThis<'file_ref, CPPStringList>>;
pub(super) fn owned(this: UniquePtr<CPPStringList>) -> Self { pub type RefStringList<'file_ref> = StringList<'file_ref, RefThis<'file_ref, CPPStringList>>;
Self { pub type RefStringListMut<'file_ref> = StringList<'file_ref, RefThisMut<'file_ref, CPPStringList>>;
this: This::Owned {
data: PhantomData,
this,
},
}
}
pub(super) fn reference(this: Pin<&'file_ref CPPStringList>) -> Self { impl<'file_ref, T: This<'file_ref, CPPStringList>> StringList<'file_ref, T> {
Self { pub(super) fn new(this: T) -> Self {
this: This::Ref { this }, Self { _data: PhantomData, this }
}
} }
pub fn to_vec(&self) -> Vec<std::string::String> { pub fn to_vec(&self) -> Vec<std::string::String> {
let pin = match &self.this { let cxx_values = bridge::StringList_to_vector(self.this.pin());
This::Owned { this, .. } => unsafe { Pin::new_unchecked(this.as_ref().unwrap()) },
This::Ref { this } => *this,
};
let cxx_values = bridge::StringList_to_vector(pin);
cxx_values cxx_values
.iter() .iter()
.map(|value| { .map(|value| {
let this = unsafe { Pin::new_unchecked(value) }; let this = unsafe { RefThis::new(value) };
String::new(this).to_string() String::new(this).to_string()
}) })
.collect() .collect()
} }
} }
pub struct ByteVector<'file_ref> { pub struct ByteVector<'file_ref, T: This<'file_ref, CPPByteVector>> {
// 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.
_data: PhantomData<&'file_ref CPPByteVector>, _data: PhantomData<&'file_ref CPPByteVector>,
this: UniquePtr<CPPByteVector>, this: T,
} }
impl<'file_ref> ByteVector<'file_ref> { impl<'file_ref, T: This<'file_ref, CPPByteVector>> ByteVector<'file_ref, T> {
pub(super) fn new(this: UniquePtr<CPPByteVector>) -> Self { pub(super) fn new(this: T) -> Self {
Self { Self { _data: PhantomData, this }
_data: PhantomData,
this,
}
} }
pub fn to_vec(&self) -> Vec<u8> { pub fn to_vec(&self) -> Vec<u8> {
let this_ref = self.this.as_ref().unwrap(); let this = self.this.pin();
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 size = this.size().try_into().unwrap(); let size = this.size().try_into().unwrap();
let data = this.data(); let data = this.data();
// Re-cast to u8 // 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>>;

View file

@ -2,36 +2,32 @@ pub use super::bridge::CPPXiphComment;
use super::bridge::{CPPFieldListMap, FieldListMap_to_entries, XiphComment_pictureList}; use super::bridge::{CPPFieldListMap, FieldListMap_to_entries, XiphComment_pictureList};
pub use super::flac::PictureList; pub use super::flac::PictureList;
use super::tk; use super::tk;
use super::this::{OwnedThis, RefThis, RefThisMut, ThisMut, This};
use std::collections::HashMap; use std::collections::HashMap;
use std::pin::Pin; use std::pin::Pin;
pub struct XiphComment<'file_ref> { pub struct XiphComment<'file_ref> {
this: Pin<&'file_ref mut CPPXiphComment>, this: RefThisMut<'file_ref, CPPXiphComment>,
} }
impl<'file_ref> XiphComment<'file_ref> { 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 } Self { this }
} }
pub fn field_list_map<'slf>(&'slf self) -> FieldListMap<'file_ref> { 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 let map: &'slf CPPFieldListMap = self.this.pin().fieldListMap();
// reference. The safe API can do this, but shortens the lifecycle to at most self, even // CPPFieldListMap exists for as long as the XiphComment, so we can transmute it
// though the reference really lives as long as file_ref. Sadly, this requires us to transmute // to the file_ref lifetime.
// to extend the lifecycle back. This new pointer is really unsafe (we now have both a mut let extended_map: &'file_ref CPPFieldListMap = unsafe { std::mem::transmute(map) };
// and an immutable reference to the same object), but it's dropped after this call. let map_pin = unsafe { Pin::new_unchecked(extended_map) };
// 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) };
FieldListMap::new(map_pin) FieldListMap::new(map_pin)
} }
pub fn picture_list(&mut self) -> PictureList<'file_ref> { pub fn picture_list(&mut self) -> Option<PictureList<'file_ref>> {
let pictures = XiphComment_pictureList(self.this.as_mut()); let pictures = XiphComment_pictureList(self.this.pin_mut());
PictureList::new(pictures) 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> { 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 } Self { this }
} }
} }
@ -59,11 +55,11 @@ impl<'file_ref> FieldListMap<'file_ref> {
// of self. // of self.
let property_pin = unsafe { Pin::new_unchecked(property) }; let property_pin = unsafe { Pin::new_unchecked(property) };
let key_ref = property_pin.key(); let key_ref = property_pin.key();
let key_pin = unsafe { Pin::new_unchecked(key_ref) }; let key_this = unsafe { RefThis::new(key_ref) };
let key = tk::String::new(key_pin).to_string(); let key = tk::String::new(key_this).to_string();
let value_ref = property_pin.value(); let value_ref = property_pin.value();
let value_pin = unsafe { Pin::new_unchecked(value_ref) }; let value_this = unsafe { RefThis::new(value_ref) };
let value = tk::StringList::reference(value_pin).to_vec(); let value = tk::StringList::new(value_this).to_vec();
(key, value) (key, value)
}) })
.collect() .collect()