Skip to content

Commit 6c7deb1

Browse files
committed
Add glib::StrVRef
This type represents a borrowed reference to a `NULL`-terminated array of `NULL`-terminated UTF-8 strings. Signed-off-by: fbrouille <[email protected]>
1 parent efb359d commit 6c7deb1

File tree

3 files changed

+221
-5
lines changed

3 files changed

+221
-5
lines changed

glib/src/collections/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pub mod slist;
1313
pub use slist::SList;
1414

1515
pub mod strv;
16-
pub use strv::StrV;
16+
pub use strv::{StrV, StrVRef};

glib/src/collections/strv.rs

Lines changed: 219 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,26 @@ impl std::borrow::Borrow<[GStringPtr]> for StrV {
114114
}
115115
}
116116

117+
impl AsRef<StrVRef> for StrV {
118+
#[inline]
119+
fn as_ref(&self) -> &StrVRef {
120+
self.into()
121+
}
122+
}
123+
124+
impl std::borrow::Borrow<StrVRef> for StrV {
125+
#[inline]
126+
fn borrow(&self) -> &StrVRef {
127+
self.into()
128+
}
129+
}
130+
117131
impl std::ops::Deref for StrV {
118-
type Target = [GStringPtr];
132+
type Target = StrVRef;
119133

120134
#[inline]
121-
fn deref(&self) -> &[GStringPtr] {
122-
self.as_slice()
135+
fn deref(&self) -> &StrVRef {
136+
self.into()
123137
}
124138
}
125139

@@ -1364,6 +1378,208 @@ impl<const N: usize> IntoStrV for [&'_ String; N] {
13641378
}
13651379
}
13661380

1381+
// rustdoc-stripper-ignore-next
1382+
/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1383+
///
1384+
/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1385+
/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1386+
#[repr(transparent)]
1387+
pub struct StrVRef {
1388+
inner: [GStringPtr],
1389+
}
1390+
1391+
impl StrVRef {
1392+
// rustdoc-stripper-ignore-next
1393+
/// Borrows a C array.
1394+
/// # Safety
1395+
///
1396+
/// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1397+
/// pass a pointer that does not uphold this condition.
1398+
#[inline]
1399+
pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a StrVRef {
1400+
let slice = StrV::from_glib_borrow(ptr);
1401+
&*(slice as *const [GStringPtr] as *const StrVRef)
1402+
}
1403+
1404+
// rustdoc-stripper-ignore-next
1405+
/// Borrows a C array.
1406+
/// # Safety
1407+
///
1408+
/// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1409+
/// pass a pointer that does not uphold this condition.
1410+
#[inline]
1411+
pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a StrVRef {
1412+
let slice = StrV::from_glib_borrow_num(ptr, len);
1413+
&*(slice as *const [GStringPtr] as *const StrVRef)
1414+
}
1415+
1416+
// rustdoc-stripper-ignore-next
1417+
/// Returns the underlying pointer.
1418+
///
1419+
/// This is guaranteed to be nul-terminated.
1420+
#[inline]
1421+
pub const fn as_ptr(&self) -> *const *const c_char {
1422+
self.inner.as_ptr() as *const *const _
1423+
}
1424+
}
1425+
1426+
impl fmt::Debug for StrVRef {
1427+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1428+
self.inner.fmt(f)
1429+
}
1430+
}
1431+
1432+
unsafe impl Send for StrVRef {}
1433+
1434+
unsafe impl Sync for StrVRef {}
1435+
1436+
impl PartialEq for StrVRef {
1437+
#[inline]
1438+
fn eq(&self, other: &Self) -> bool {
1439+
self.inner == other.inner
1440+
}
1441+
}
1442+
1443+
impl Eq for StrVRef {}
1444+
1445+
impl PartialOrd for StrVRef {
1446+
#[inline]
1447+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1448+
Some(self.cmp(other))
1449+
}
1450+
}
1451+
1452+
impl Ord for StrVRef {
1453+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1454+
self.inner.cmp(&other.inner)
1455+
}
1456+
}
1457+
1458+
impl std::hash::Hash for StrVRef {
1459+
#[inline]
1460+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1461+
self.inner.hash(state)
1462+
}
1463+
}
1464+
1465+
impl PartialEq<[&'_ str]> for StrVRef {
1466+
fn eq(&self, other: &[&'_ str]) -> bool {
1467+
for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1468+
if a != b {
1469+
return false;
1470+
}
1471+
}
1472+
1473+
true
1474+
}
1475+
}
1476+
1477+
impl PartialEq<StrVRef> for [&'_ str] {
1478+
#[inline]
1479+
fn eq(&self, other: &StrVRef) -> bool {
1480+
other.eq(self)
1481+
}
1482+
}
1483+
1484+
impl Default for &StrVRef {
1485+
#[inline]
1486+
fn default() -> Self {
1487+
const SLICE: &[*const c_char] = &[ptr::null()];
1488+
// SAFETY: `SLICE` is indeed a valid nul-terminated array.
1489+
unsafe { StrVRef::from_glib_borrow(SLICE.as_ptr()) }
1490+
}
1491+
}
1492+
1493+
impl std::ops::Deref for StrVRef {
1494+
type Target = [GStringPtr];
1495+
1496+
#[inline]
1497+
fn deref(&self) -> &[GStringPtr] {
1498+
&self.inner
1499+
}
1500+
}
1501+
1502+
impl<'a> std::iter::IntoIterator for &'a StrVRef {
1503+
type Item = &'a GStringPtr;
1504+
type IntoIter = std::slice::Iter<'a, GStringPtr>;
1505+
1506+
#[inline]
1507+
fn into_iter(self) -> Self::IntoIter {
1508+
self.inner.iter()
1509+
}
1510+
}
1511+
1512+
impl<'a> From<&'a StrV> for &'a StrVRef {
1513+
fn from(value: &'a StrV) -> Self {
1514+
let slice = value.as_slice();
1515+
// Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1516+
// therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1517+
// thus it is safe to convert it to `&CStr`.
1518+
unsafe { &*(slice as *const [GStringPtr] as *const StrVRef) }
1519+
}
1520+
}
1521+
1522+
impl FromGlibContainer<*mut c_char, *const *const c_char> for &StrVRef {
1523+
unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1524+
StrVRef::from_glib_borrow_num(ptr, num)
1525+
}
1526+
1527+
unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1528+
unimplemented!();
1529+
}
1530+
1531+
unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1532+
unimplemented!();
1533+
}
1534+
}
1535+
1536+
impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &StrVRef {
1537+
#[inline]
1538+
unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1539+
StrVRef::from_glib_borrow(ptr)
1540+
}
1541+
1542+
unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1543+
unimplemented!();
1544+
}
1545+
1546+
unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1547+
unimplemented!();
1548+
}
1549+
}
1550+
1551+
impl<'a> ToGlibPtr<'a, *const *const c_char> for StrVRef {
1552+
type Storage = PhantomData<&'a Self>;
1553+
1554+
#[inline]
1555+
fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1556+
Stash(self.as_ptr(), PhantomData)
1557+
}
1558+
}
1559+
1560+
impl IntoGlibPtr<*const *const c_char> for &StrVRef {
1561+
#[inline]
1562+
fn into_glib_ptr(self) -> *const *const c_char {
1563+
self.as_ptr()
1564+
}
1565+
}
1566+
1567+
impl StaticType for StrVRef {
1568+
#[inline]
1569+
fn static_type() -> crate::Type {
1570+
<Vec<String>>::static_type()
1571+
}
1572+
}
1573+
1574+
unsafe impl<'a> crate::value::FromValue<'a> for &'a StrVRef {
1575+
type Checker = crate::value::GenericValueTypeChecker<Self>;
1576+
1577+
unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1578+
let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1579+
StrVRef::from_glib_borrow(ptr)
1580+
}
1581+
}
1582+
13671583
#[cfg(test)]
13681584
mod test {
13691585
use super::*;

glib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ mod exit_code;
120120
pub use exit_code::{ExitCode, InvalidExitCode};
121121

122122
pub mod collections;
123-
pub use collections::{List, PtrSlice, SList, Slice, StrV};
123+
pub use collections::{List, PtrSlice, SList, Slice, StrV, StrVRef};
124124

125125
pub use self::auto::*;
126126
#[allow(clippy::too_many_arguments)]

0 commit comments

Comments
 (0)