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

View file

@ -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))
}
}

View file

@ -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))
}
}

View file

@ -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))
}
}

View file

@ -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))
}
}

View file

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

View file

@ -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))
}
}

View file

@ -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))
}
}

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::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>>;

View file

@ -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()