musikr: refined flac pic support

This commit is contained in:
Alexander Capehart 2025-02-15 11:34:47 -07:00
parent 3dfcf0f67a
commit f5de03dfee
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
13 changed files with 275 additions and 233 deletions

View file

@ -119,6 +119,8 @@ fn main() {
.file("shim/iostream_shim.cpp")
.file("shim/file_shim.cpp")
.file("shim/tk_shim.cpp")
.file("shim/picture_shim.cpp")
.file("shim/xiph_shim.cpp")
.include(format!("taglib/pkg/{}/include", target))
.include(".") // Add the current directory to include path
.flag_if_supported("-std=c++14");
@ -129,12 +131,6 @@ fn main() {
builder.compile("taglib_cxx_bindings");
// Rebuild if shim files change
println!("cargo:rerun-if-changed=shim/iostream_shim.hpp");
println!("cargo:rerun-if-changed=shim/iostream_shim.cpp");
println!("cargo:rerun-if-changed=shim/file_shim.hpp");
println!("cargo:rerun-if-changed=shim/file_shim.cpp");
println!("cargo:rerun-if-changed=shim/tk_shim.hpp");
println!("cargo:rerun-if-changed=shim/tk_shim.cpp");
println!("cargo:rerun-if-changed=src/taglib/ffi.rs");
println!("cargo:rerun-if-changed=shim/");
println!("cargo:rerun-if-changed=taglib/");
}

View file

@ -1,6 +1,23 @@
#include "picture_shim.hpp"
#include "taglib/flacfile.h"
namespace taglib_shim {
std::unique_ptr<PictureList> XiphComment_pictureList(TagLib::Ogg::XiphComment& comment) {
return std::make_unique<PictureList>(comment.pictureList());
}
std::unique_ptr<PictureList> FLACFile_pictureList(TagLib::FLAC::File& file) {
return std::make_unique<PictureList>(file.pictureList());
}
std::unique_ptr<std::vector<WrappedPicture>> PictureList_to_vector(const PictureList& list) {
auto result = std::make_unique<std::vector<WrappedPicture>>();
for (const auto* picture : list) {
result->emplace_back(picture);
}
return result;
}
std::unique_ptr<TagLib::String> Picture_mimeType(const TagLib::FLAC::Picture& picture) {
return std::make_unique<TagLib::String>(picture.mimeType());
}

View file

@ -3,10 +3,24 @@
#include "taglib/flacpicture.h"
#include "taglib/tstring.h"
#include "taglib/tbytevector.h"
#include "tk_shim.hpp"
#include <memory>
#include <vector>
namespace taglib_shim {
std::unique_ptr<TagLib::String> Picture_mimeType(const TagLib::FLAC::Picture& picture);
std::unique_ptr<TagLib::String> Picture_description(const TagLib::FLAC::Picture& picture);
using PictureList = TagLib::List<TagLib::FLAC::Picture *>;
class WrappedPicture {
public:
WrappedPicture(const TagLib::FLAC::Picture* picture) : picture(picture) {}
const TagLib::FLAC::Picture* inner() const { return picture; }
private:
const TagLib::FLAC::Picture* picture;
};
std::unique_ptr<PictureList> FLACFile_pictureList(TagLib::FLAC::File& file);
std::unique_ptr<PictureList> XiphComment_pictureList(TagLib::Ogg::XiphComment& comment);
std::unique_ptr<std::vector<WrappedPicture>> PictureList_to_vector(const PictureList& list);
std::unique_ptr<TagLib::ByteVector> Picture_data(const TagLib::FLAC::Picture& picture);
}

View file

@ -32,42 +32,4 @@ namespace taglib_shim
}
return result;
}
std::unique_ptr<std::vector<PictureRef>> FLACFile_pictureList_to_vector(TagLib::FLAC::File &file)
{
std::unique_ptr<std::vector<PictureRef>> result = std::make_unique<std::vector<PictureRef>>();
const auto pictures = file.pictureList();
for (const auto &picture : pictures)
{
result->push_back(PictureRef(picture));
}
return result;
}
std::unique_ptr<std::vector<PictureRef>> XiphComment_pictureList_to_vector(TagLib::Ogg::XiphComment &comment)
{
std::unique_ptr<std::vector<PictureRef>> result = std::make_unique<std::vector<PictureRef>>();
const auto pictures = comment.pictureList();
for (const auto &picture : pictures)
{
result->push_back(PictureRef(picture));
}
return result;
}
rust::String String_to_string(const TagLib::String &str)
{
return rust::String(str.to8Bit());
}
std::unique_ptr<std::vector<uint8_t>> ByteVector_to_bytes(const TagLib::ByteVector &data)
{
auto result = std::make_unique<std::vector<uint8_t>>();
result->reserve(data.size());
for (size_t i = 0; i < data.size(); i++)
{
result->push_back(static_cast<uint8_t>(data[i]));
}
return result;
}
}

View file

@ -27,17 +27,7 @@ namespace taglib_shim
TagLib::StringList value_;
};
struct PictureRef {
PictureRef(const TagLib::FLAC::Picture* picture) : picture_(picture) {}
const TagLib::FLAC::Picture* get() const { return picture_; }
private:
const TagLib::FLAC::Picture* picture_;
};
std::unique_ptr<std::vector<Property>> SimplePropertyMap_to_vector(const TagLib::SimplePropertyMap &map);
std::unique_ptr<std::vector<TagLib::String>> StringList_to_vector(const TagLib::StringList &list);
std::unique_ptr<std::vector<PictureRef>> FLACFile_pictureList_to_vector(TagLib::FLAC::File &file);
std::unique_ptr<std::vector<PictureRef>> XiphComment_pictureList_to_vector(TagLib::Ogg::XiphComment &comment);
rust::String String_to_string(const TagLib::String &str);
std::unique_ptr<std::vector<uint8_t>> ByteVector_to_bytes(const TagLib::ByteVector &data);
}

View file

@ -0,0 +1,26 @@
#include "xiph_shim.hpp"
namespace taglib_shim
{
FieldListEntry::FieldListEntry(TagLib::String key, TagLib::StringList value) : key_(key), value_(value) {}
const TagLib::String &FieldListEntry::key() const
{
return key_;
}
const TagLib::StringList &FieldListEntry::value() const
{
return value_;
}
std::unique_ptr<std::vector<FieldListEntry>> FieldListMap_to_entries(const TagLib::SimplePropertyMap &map)
{
std::unique_ptr<std::vector<FieldListEntry>> result = std::make_unique<std::vector<FieldListEntry>>();
for (const auto &pair : map)
{
result->push_back(FieldListEntry(pair.first, pair.second));
}
return result;
}
}

View file

@ -0,0 +1,22 @@
#include "taglib/tpropertymap.h"
#include "taglib/xiphcomment.h"
namespace taglib_shim
{
using FieldListMap = TagLib::SimplePropertyMap;
struct FieldListEntry
{
FieldListEntry(TagLib::String key, TagLib::StringList value);
const TagLib::String &key() const;
const TagLib::StringList &value() const;
private:
TagLib::String key_;
TagLib::StringList value_;
};
std::unique_ptr<std::vector<FieldListEntry>> FieldListMap_to_entries(const FieldListMap &map);
std::unique_ptr<std::vector<TagLib::String>> StringList_to_vector(const TagLib::StringList &list);
}

View file

@ -31,12 +31,13 @@ mod bridge_impl {
include!("shim/file_shim.hpp");
include!("shim/tk_shim.hpp");
include!("shim/picture_shim.hpp");
include!("shim/xiph_shim.hpp");
#[namespace = "TagLib"]
#[cxx_name = "IOStream"]
type CPPIOStream;
// Create a RustIOStream from a BridgeStream
unsafe fn wrap_RsIOStream(stream: Pin<&mut DynIOStream>) -> UniquePtr<CPPIOStream>;
fn wrap_RsIOStream(stream: Pin<&mut DynIOStream>) -> UniquePtr<CPPIOStream>;
#[namespace = "TagLib"]
#[cxx_name = "FileRef"]
@ -65,53 +66,68 @@ mod bridge_impl {
#[cxx_name = "sampleRate"]
fn sampleRate(self: Pin<&CppAudioProperties>) -> i32;
#[cxx_name = "channels"]
fn channels(self: Pin<&CppAudioProperties>) -> i32;
fn channels(self: Pin<&CppAudioProperties>) -> i32;
#[namespace = "TagLib::FLAC"]
#[cxx_name = "Picture"]
type CPPFLACPicture;
#[namespace = "taglib_shim"]
fn Picture_mimeType(picture: &CPPFLACPicture) -> UniquePtr<CPPString>;
#[namespace = "taglib_shim"]
fn Picture_description(picture: &CPPFLACPicture) -> UniquePtr<CPPString>;
#[cxx_name = "width"]
fn width(self: Pin<&CPPFLACPicture>) -> i32;
#[cxx_name = "height"]
fn height(self: Pin<&CPPFLACPicture>) -> i32;
#[cxx_name = "colorDepth"]
fn colorDepth(self: Pin<&CPPFLACPicture>) -> i32;
#[cxx_name = "numColors"]
fn numColors(self: Pin<&CPPFLACPicture>) -> i32;
#[namespace = "taglib_shim"]
fn Picture_data(picture: &CPPFLACPicture) -> UniquePtr<CPPByteVector>;
#[namespace = "TagLib"]
#[cxx_name = "ByteVector"]
type CPPByteVector;
fn Picture_data(picture: Pin<&CPPFLACPicture>) -> UniquePtr<CPPByteVector>;
#[namespace = "TagLib::Ogg"]
#[cxx_name = "XiphComment"]
type CPPXiphComment;
#[cxx_name = "fieldListMap"]
fn fieldListMap(self: Pin<&CPPXiphComment>) -> &CPPSimplePropertyMap;
fn fieldListMap(self: Pin<&CPPXiphComment>) -> &CPPFieldListMap;
#[namespace = "TagLib"]
#[cxx_name = "SimplePropertyMap"]
type CPPFieldListMap;
#[namespace = "taglib_shim"]
fn FieldListMap_to_entries(
field_list_map: Pin<&CPPFieldListMap>,
) -> UniquePtr<CxxVector<CPPFieldListEntry>>;
#[namespace = "taglib_shim"]
#[cxx_name = "FieldListEntry"]
type CPPFieldListEntry;
#[cxx_name = "key"]
fn key(self: Pin<&CPPFieldListEntry>) -> &CPPString;
#[cxx_name = "value"]
fn value(self: Pin<&CPPFieldListEntry>) -> &CPPStringList;
#[namespace = "TagLib::Ogg::Vorbis"]
#[cxx_name = "File"]
type CPPVorbisFile;
#[cxx_name = "tag"]
unsafe fn vorbisTag(self: Pin<&CPPVorbisFile>) -> *mut CPPXiphComment;
fn vorbisTag(self: Pin<&CPPVorbisFile>) -> *mut CPPXiphComment;
#[namespace = "taglib_shim"]
fn XiphComment_pictureList(comment: Pin<&mut CPPXiphComment>) -> UniquePtr<CPPPictureList>;
#[namespace = "TagLib::Ogg::Opus"]
#[cxx_name = "File"]
type CPPOpusFile;
#[cxx_name = "tag"]
unsafe fn opusTag(self: Pin<&CPPOpusFile>) -> *mut CPPXiphComment;
fn opusTag(self: Pin<&CPPOpusFile>) -> *mut CPPXiphComment;
#[namespace = "TagLib::FLAC"]
#[cxx_name = "File"]
type CPPFLACFile;
#[cxx_name = "xiphComment"]
unsafe fn xiphComment(self: Pin<&mut CPPFLACFile>, create: bool) -> *mut CPPXiphComment;
fn xiphComment(self: Pin<&mut CPPFLACFile>, create: bool) -> *mut CPPXiphComment;
#[namespace = "taglib_shim"]
fn FLACFile_pictureList(file: Pin<&mut CPPFLACFile>) -> UniquePtr<CPPPictureList>;
#[namespace = "taglib_shim"]
#[cxx_name = "PictureList"]
type CPPPictureList;
#[namespace = "taglib_shim"]
fn PictureList_to_vector(list: Pin<&CPPPictureList>) -> UniquePtr<CxxVector<CPPWrappedPicture>>;
#[namespace = "taglib_shim"]
#[cxx_name = "WrappedPicture"]
type CPPWrappedPicture;
fn inner(self: &CPPWrappedPicture) -> *const CPPFLACPicture;
#[namespace = "TagLib::MPEG"]
#[cxx_name = "File"]
@ -125,14 +141,6 @@ mod bridge_impl {
#[cxx_name = "File"]
type CPPWAVFile;
// #[namespace = "TagLib::WavPack"]
// #[cxx_name = "File"]
// type WavPackFile;
// #[namespace = "TagLib::APE"]
// #[cxx_name = "File"]
// type APEFile;
#[namespace = "taglib_shim"]
unsafe fn File_asVorbis(file: *mut CPPFile) -> *mut CPPVorbisFile;
#[namespace = "taglib_shim"]
@ -146,47 +154,23 @@ mod bridge_impl {
#[namespace = "taglib_shim"]
unsafe fn File_asWAV(file: *mut CPPFile) -> *mut CPPWAVFile;
#[namespace = "TagLib"]
#[cxx_name = "SimplePropertyMap"]
type CPPSimplePropertyMap;
#[namespace = "taglib_shim"]
fn SimplePropertyMap_to_vector(
field_list_map: Pin<&CPPSimplePropertyMap>,
) -> UniquePtr<CxxVector<CPPProperty>>;
#[namespace = "taglib_shim"]
#[cxx_name = "Property"]
type CPPProperty;
#[cxx_name = "key"]
fn key(self: Pin<&CPPProperty>) -> &CPPString;
#[cxx_name = "value"]
unsafe fn value(self: Pin<&CPPProperty>) -> &CPPStringList;
#[namespace = "TagLib"]
#[cxx_name = "String"]
type CPPString;
#[cxx_name = "toCString"]
unsafe fn thisToCString(self: Pin<&CPPString>, unicode: bool) -> *const c_char;
fn toCString(self: Pin<&CPPString>, unicode: bool) -> *const c_char;
#[namespace = "TagLib"]
#[cxx_name = "StringList"]
type CPPStringList;
#[namespace = "taglib_shim"]
fn StringList_to_vector(string_list: Pin<&CPPStringList>) -> UniquePtr<CxxVector<CPPString>>;
#[namespace = "taglib_shim"]
type PictureRef;
fn get(self: &PictureRef) -> *const CPPFLACPicture;
#[namespace = "taglib_shim"]
fn FLACFile_pictureList_to_vector(file: Pin<&mut CPPFLACFile>) -> UniquePtr<CxxVector<PictureRef>>;
#[namespace = "taglib_shim"]
fn XiphComment_pictureList_to_vector(comment: Pin<&mut CPPXiphComment>) -> UniquePtr<CxxVector<PictureRef>>;
#[namespace = "taglib_shim"]
fn String_to_string(str: &CPPString) -> String;
#[namespace = "taglib_shim"]
fn ByteVector_to_bytes(data: &CPPByteVector) -> UniquePtr<CxxVector<u8>>;
#[namespace = "TagLib"]
#[cxx_name = "ByteVector"]
type CPPByteVector;
fn size(self: Pin<&CPPByteVector>) -> u32;
fn data(self: Pin<&CPPByteVector>) -> *const c_char;
}
}

View file

@ -1,7 +1,10 @@
pub use super::bridge::CPPFLACFile;
pub use super::bridge::CPPFLACPicture;
pub use super::xiph::XiphComment;
use super::bridge::{FLACFile_pictureList_to_vector, String_to_string, ByteVector_to_bytes, Picture_mimeType, Picture_description, Picture_data};
use super::bridge::{CPPPictureList, PictureList_to_vector, FLACFile_pictureList, Picture_data};
use super::tk::ByteVector;
use std::marker::PhantomData;
use cxx::UniquePtr;
use std::pin::Pin;
pub struct FLACFile<'a> {
@ -15,15 +18,7 @@ impl<'a> FLACFile<'a> {
pub fn xiph_comments(&mut self) -> Option<XiphComment> {
let this = self.this.as_mut();
let tag = 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.
// - The value is a pointer that does not depend on the address of self.
// SAFETY: This is a C++ FFI function ensured to call correctly.
this.xiphComment(false)
};
let tag = this.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.
@ -33,11 +28,35 @@ impl<'a> FLACFile<'a> {
tag_pin.map(|tag| XiphComment::new(tag))
}
pub fn picture_list(&mut self) -> Vec<Picture<'a>> {
let pictures = FLACFile_pictureList_to_vector(self.this.as_mut());
pub fn picture_list(&mut self) -> PictureList {
let pictures = FLACFile_pictureList(self.this.as_mut());
PictureList::new(pictures)
}
}
pub struct PictureList<'a> {
// PictureList is implicitly tied to the lifetime of the parent that owns it,
// so we need to track that lifetime.
_data: PhantomData<&'a CPPFLACPicture>,
this: UniquePtr<CPPPictureList>,
}
impl<'a> PictureList<'a> {
pub(super) fn new(this: UniquePtr<CPPPictureList>) -> Self {
Self { _data: PhantomData, this }
}
pub fn to_vec(&self) -> Vec<Picture> {
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 mut result = Vec::new();
for picture_ref in pictures.iter() {
let picture_ptr = picture_ref.get();
let picture_ptr = picture_ref.inner();
let picture_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.
@ -59,31 +78,8 @@ impl<'a> Picture<'a> {
Self { this }
}
pub fn mime_type(&self) -> String {
String_to_string(Picture_mimeType(self.this.get_ref()).as_ref().unwrap())
}
pub fn description(&self) -> String {
String_to_string(Picture_description(self.this.get_ref()).as_ref().unwrap())
}
pub fn width(&self) -> i32 {
self.this.width()
}
pub fn height(&self) -> i32 {
self.this.height()
}
pub fn color_depth(&self) -> i32 {
self.this.colorDepth()
}
pub fn num_colors(&self) -> i32 {
self.this.numColors()
}
pub fn data(&self) -> Vec<u8> {
ByteVector_to_bytes(Picture_data(self.this.get_ref()).as_ref().unwrap()).iter().map(|b| *b).collect()
pub fn data(&self) -> ByteVector<'a> {
let data = Picture_data(self.this);
ByteVector::new(data)
}
}

View file

@ -18,7 +18,7 @@ pub(super) struct BridgedIOStream<'a> {
impl<'a> BridgedIOStream<'a> {
pub fn new<T : IOStream + 'a>(stream: T) -> Self {
let mut rs_stream = Box::pin(DynIOStream(Box::new(stream)));
let cpp_stream = unsafe { bridge::wrap_RsIOStream(rs_stream.as_mut()) };
let cpp_stream = bridge::wrap_RsIOStream(rs_stream.as_mut());
BridgedIOStream {
rs_stream,
cpp_stream

View file

@ -13,14 +13,7 @@ impl<'a> VorbisFile<'a> {
pub fn xiph_comments(&self) -> Option<XiphComment> {
let this = self.this.as_ref();
let tag = 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.
// - The value is a pointer that does not depend on the address of self.
this.vorbisTag()
};
let tag = this.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.

View file

@ -1,51 +1,22 @@
use std::collections::HashMap;
use super::bridge::{self, CPPByteVector, CPPString, CPPStringList};
use cxx::UniquePtr;
use std::marker::PhantomData;
use std::pin::Pin;
use std::{ffi::CStr, string::ToString};
use super::bridge::{self, CPPSimplePropertyMap, CPPString, CPPStringList};
pub struct SimplePropertyMap<'a> {
this: Pin<&'a CPPSimplePropertyMap>,
pub struct String<'a> {
this: Pin<&'a CPPString>,
}
impl<'a> SimplePropertyMap<'a> {
pub(super) fn new(this: Pin<&'a CPPSimplePropertyMap>) -> Self {
impl<'a> String<'a> {
pub(super) fn new(this: Pin<&'a CPPString>) -> Self {
Self { this }
}
}
impl<'a> SimplePropertyMap<'a> {
pub fn to_hashmap(&self) -> HashMap<String, Vec<String>> {
let cxx_vec = bridge::SimplePropertyMap_to_vector(self.this);
cxx_vec
.iter()
.map(|property| 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.
// - The values returned are copied and thus not dependent on the address
// of self.
let property_pin = Pin::new_unchecked(property);
let key = property_pin.key().to_string();
let value = property_pin.value().to_vec();
(key, value)
})
.collect()
}
}
impl ToString for CPPString {
fn to_string(&self) -> String {
let c_str = 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.
// - The value returned are pointers and thus not dependent on the address
// of self.
let this: Pin<&CPPString> = Pin::new_unchecked(self);
this.thisToCString(true)
};
impl<'a> ToString for String<'a> {
fn to_string(&self) -> std::string::String {
let c_str = self.this.toCString(true);
unsafe {
// SAFETY:
// - This is a C-string returned by a C++ method guaranteed to have
@ -63,18 +34,64 @@ impl ToString for CPPString {
}
}
impl CPPStringList {
pub fn to_vec(&self) -> Vec<String> {
let cxx_values = unsafe {
pub struct StringList<'a> {
this: Pin<&'a CPPStringList>,
}
impl<'a> StringList<'a> {
pub(super) fn new(this: Pin<&'a CPPStringList>) -> Self {
Self { this }
}
pub fn to_vec(&self) -> Vec<std::string::String> {
let cxx_values = bridge::StringList_to_vector(self.this);
cxx_values
.iter()
.map(|value| {
let this = unsafe {
Pin::new_unchecked(value)
};
String::new(this).to_string()
})
.collect()
}
}
pub struct ByteVector<'a> {
// 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<&'a CPPByteVector>,
this: UniquePtr<CPPByteVector>,
}
impl<'a> ByteVector<'a> {
pub(super) fn new(this: UniquePtr<CPPByteVector>) -> 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.
// - The value returned is a unique ptr to a copied vector that is not
// dependent on the address of self.
let this = Pin::new_unchecked(self);
bridge::StringList_to_vector(this)
Pin::new_unchecked(this_ref)
};
cxx_values.iter().map(|value| value.to_string()).collect()
let size = this.size().try_into().unwrap();
let data = this.data();
// Re-cast to u8
let data: *const u8 = data as *const u8;
unsafe {
// SAFETY:
// - data points to a valid buffer of size 'size' owned by the C++ ByteVector
// - we're creating a new Vec and copying the data, not taking ownership
// - the source data won't be modified while we're reading from it
std::slice::from_raw_parts(data, size).to_vec()
}
}
}

View file

@ -1,8 +1,9 @@
pub use super::bridge::CPPXiphComment;
pub use super::flac::Picture;
use super::bridge::XiphComment_pictureList_to_vector;
use super::tk::SimplePropertyMap;
pub use super::flac::PictureList;
use super::bridge::{CPPFieldListMap, FieldListMap_to_entries, XiphComment_pictureList};
use super::tk;
use std::pin::Pin;
use std::collections::HashMap;
pub struct XiphComment<'a> {
this: Pin<&'a mut CPPXiphComment>
@ -13,25 +14,49 @@ impl<'a> XiphComment<'a> {
Self { this }
}
pub fn field_list_map(&'a self) -> SimplePropertyMap<'a> {
pub fn field_list_map(&'a self) -> FieldListMap<'a> {
let map = self.this.as_ref().fieldListMap();
let map_pin = unsafe { Pin::new_unchecked(map) };
SimplePropertyMap::new(map_pin)
FieldListMap::new(map_pin)
}
pub fn picture_list(&mut self) -> Vec<Picture<'a>> {
let pictures = XiphComment_pictureList_to_vector(self.this.as_mut());
let mut result = Vec::new();
for picture_ref in pictures.iter() {
let picture_ptr = picture_ref.get();
let picture_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.
picture_ptr.as_ref().unwrap()
};
let picture_pin = unsafe { Pin::new_unchecked(picture_ref) };
result.push(Picture::new(picture_pin));
}
result
pub fn picture_list(&mut self) -> PictureList<'a> {
let pictures = XiphComment_pictureList(self.this.as_mut());
PictureList::new(pictures)
}
}
pub struct FieldListMap<'a> {
this: Pin<&'a CPPFieldListMap>,
}
impl<'a> FieldListMap<'a> {
pub(super) fn new(this: Pin<&'a CPPFieldListMap>) -> Self {
Self { this }
}
}
impl<'a> FieldListMap<'a> {
pub fn to_hashmap(&self) -> HashMap<String, Vec<String>> {
let cxx_vec = FieldListMap_to_entries(self.this);
cxx_vec
.iter()
.map(|property| {
// 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.
// - The values returned are copied and thus not dependent on the address
// 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 value_ref = property_pin.value();
let value_pin = unsafe { Pin::new_unchecked(value_ref) };
let value = tk::StringList::new(value_pin).to_vec();
(key, value)
})
.collect()
}
}