musikr: document this contract

This commit is contained in:
Alexander Capehart 2025-02-17 10:10:50 -07:00
parent 65d8959bcf
commit c4f51b749a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47

View file

@ -2,25 +2,65 @@ use std::marker::PhantomData;
use std::pin::Pin;
use cxx::{UniquePtr, memory::UniquePtrTarget};
/// A taglib-FFI-specific trait representing a C++ object returned by the library.
///
/// This trait is used to provide a temporary pin of the object for use in C++
/// member function calls.
///
/// `This` instances must hold the following contract:
/// - This object will remain valid as long as TagLib's FileRef object is valid,
/// and will be dropped when the FileRef is dropped.
/// - This object will not move or be mutated over the FileRef's lifetime, this way
/// it can be temporarily pinned for use as a `this` pointer.
pub trait This<'file_ref, T> {
/// Temporarily pin the object for use in C++ member function calls.
///
/// When C++ member functions are called, the object is temporarily pinned
/// as a `this` pointer. In turn, cxx requires a `Pin<&T>` to be used in it's
/// member function bindings, hence this method.
///
/// This is safe to call assuming the contract of `This` is upheld.
fn pin(&self) -> Pin<&T>;
}
/// A taglib-FFI-specific trait representing a C++ object returned by the library.
///
/// This trait is used to provide a temporary pin of the object for use in C++
/// member function calls.
///
/// `ThisMut` instances must hold the following contract:
/// - This object will remain valid as long as TagLib's FileRef object is valid,
/// and will be dropped when the FileRef is dropped.
/// - This object will not move over the FileRef's lifetime, this way it can be
/// temporarily pinned for use as a `this` pointer.
pub trait ThisMut<'file_ref, T> : This<'file_ref, T> {
fn pin_mut(&mut self) -> Pin<&mut T>;
}
/// A [This] instance that is a reference to a C++ object.
pub struct RefThis<'file_ref, T> {
this: &'file_ref T
}
impl<'file_ref, T> RefThis<'file_ref, T> {
/// Create a new [RefThis] from a reference to a C++ object.
///
/// This is safe to call assuming the contract of [This] is upheld. Since this
/// contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the reference is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
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 }
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [This] is upheld.
pub fn ptr(&self) -> *const T {
self.this as *const T
}
@ -32,19 +72,39 @@ impl<'file_ref, T> This<'file_ref, T> for RefThis<'file_ref, T> {
}
}
/// A [ThisMut] instance that is a reference to a C++ object.
///
/// This is similar to [RefThis], but allows mutating the object.
pub struct RefThisMut<'file_ref, T> {
this: &'file_ref mut T,
}
impl<'file_ref, T> RefThisMut<'file_ref, T> {
/// Create a new [RefThisMut] from a reference to a C++ object.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld. Since
/// this contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the reference is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
pub unsafe fn new(this: &'file_ref mut T) -> Self {
Self { this }
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld.
pub fn ptr(&self) -> *const T {
self.this as *const T
}
/// Get a pointer to the C++ object.
///
/// This can be used to pass the object to FFI functions that take a pointer.
///
/// This is safe to call assuming the contract of [ThisMut] is upheld.
pub fn ptr_mut(&mut self) -> *mut T {
self.this as *mut T
}
@ -62,12 +122,26 @@ impl<'file_ref, T> ThisMut<'file_ref, T> for RefThisMut<'file_ref, T> {
}
}
/// A [This] instance that is "owned" by the caller.
///
/// "Owned" in this context only really means that the object is not a rust reference.
/// In practice, all "owned" taglib objects are actually shared references, and are
/// thus tied to the lifetime of the `'file_ref` parameter.
pub struct OwnedThis<'file_ref, T : UniquePtrTarget> {
_data: PhantomData<&'file_ref ()>,
this: UniquePtr<T>,
}
impl<'file_ref, T : UniquePtrTarget> OwnedThis<'file_ref, T> {
/// Create a new [OwnedThis] from a [UniquePtr].
///
/// This is safe to call assuming the contract of [This] is upheld. Since this
/// contract cannot be enforced by the Rust compiler, it is the caller's
/// responsibility to ensure that the `UniquePtr` is valid for the lifetime of
/// the `'file_ref` parameter. More or less, if it comes from the TagLib FFI
/// interface, it is safe to use this.
///
/// This will return `None` if the `UniquePtr` is `null`.
pub unsafe fn new(this: UniquePtr<T>) -> Option<Self> {
if !this.is_null() {
Some(Self {