musikr: document this contract
This commit is contained in:
parent
65d8959bcf
commit
c4f51b749a
1 changed files with 74 additions and 0 deletions
|
@ -2,25 +2,65 @@ use std::marker::PhantomData;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use cxx::{UniquePtr, memory::UniquePtrTarget};
|
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> {
|
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>;
|
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> {
|
pub trait ThisMut<'file_ref, T> : This<'file_ref, T> {
|
||||||
fn pin_mut(&mut self) -> Pin<&mut 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> {
|
pub struct RefThis<'file_ref, T> {
|
||||||
this: &'file_ref T
|
this: &'file_ref T
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'file_ref, T> RefThis<'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 {
|
pub unsafe fn new(this: &'file_ref T) -> Self {
|
||||||
// Rough informal contact is that the reference points to a C++ object
|
// Rough informal contact is that the reference points to a C++ object
|
||||||
// that will live and not move for as long as 'file_ref.
|
// that will live and not move for as long as 'file_ref.
|
||||||
Self { this }
|
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 {
|
pub fn ptr(&self) -> *const T {
|
||||||
self.this as *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> {
|
pub struct RefThisMut<'file_ref, T> {
|
||||||
this: &'file_ref mut T,
|
this: &'file_ref mut T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'file_ref, T> RefThisMut<'file_ref, 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 {
|
pub unsafe fn new(this: &'file_ref mut T) -> Self {
|
||||||
Self { this }
|
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 {
|
pub fn ptr(&self) -> *const T {
|
||||||
self.this as *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 {
|
pub fn ptr_mut(&mut self) -> *mut T {
|
||||||
self.this as *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> {
|
pub struct OwnedThis<'file_ref, T : UniquePtrTarget> {
|
||||||
_data: PhantomData<&'file_ref ()>,
|
_data: PhantomData<&'file_ref ()>,
|
||||||
this: UniquePtr<T>,
|
this: UniquePtr<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'file_ref, T : UniquePtrTarget> OwnedThis<'file_ref, 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> {
|
pub unsafe fn new(this: UniquePtr<T>) -> Option<Self> {
|
||||||
if !this.is_null() {
|
if !this.is_null() {
|
||||||
Some(Self {
|
Some(Self {
|
||||||
|
|
Loading…
Reference in a new issue