rpm-ostree/rust/src/cxxrsutil.rs
Luca BRUNO 21be64b3d6 libpriv/passwd: move RPM layering logic to Rust
This moves to Rust the RPM layering logic for users and groups
databases.
2021-01-19 19:32:27 -05:00

153 lines
4.6 KiB
Rust

//! Wrappers that bridge the world of cxx.rs and GObject-introspect based crate bindings.
//! See https://github.com/dtolnay/cxx/issues/544
//! While cxx.rs supports including externally-bound types (like ostree::Repo),
//! two things make this more complicated. First, cxx.rs requires implementing
//! a trait, and due to the orphan rule we can't implement foreign traits on
//! foreign types. Second, what we *actually* want on the Rust side isn't
//! the *_sys type (e.g. ostree_sys::OstreeRepo) but the `ostree::Repo` type.
//! So for now, we define a `FFIGObjectWrapper` trait that helps with this.
//! In the future though hopefully cxx.rs improves this situation.
use cxx::{type_id, ExternType};
use paste::paste;
/// Map an empty string to a `None`.
pub(crate) fn opt_string(input: &str) -> Option<&str> {
// TODO(lucab): drop this polyfill once cxx-rs starts supporting Option<_>.
Some(input).filter(|s| !s.is_empty())
}
/// A custom trait used to translate a *_sys C type wrapper
/// to its GObject version.
pub trait FFIGObjectWrapper {
type Wrapper;
fn gobj_wrap(&mut self) -> Self::Wrapper;
}
/// Implement FFIGObjectWrapper given a pair of wrapper type
/// and sys type.
macro_rules! impl_wrap {
($w:ident, $bound:path) => {
impl FFIGObjectWrapper for $w {
type Wrapper = $bound;
fn gobj_wrap(&mut self) -> Self::Wrapper {
unsafe { glib::translate::from_glib_none(&mut self.0 as *mut _) }
}
}
};
}
/// Custom macro to bind an OSTree GObject type.
macro_rules! bind_ostree_obj {
($w:ident) => {
paste! {
#[repr(transparent)]
pub struct [<FFIOstree $w>](ostree_sys::[<Ostree $w>]);
unsafe impl ExternType for [<FFIOstree $w>] {
type Id = type_id!(rpmostreecxx::[<Ostree $w>]);
type Kind = cxx::kind::Trivial;
}
impl_wrap!([<FFIOstree $w>], ostree::$w);
}
};
}
// When extending this list, also update rpmostree-cxxrs-prelude.h and lib.rs
// This macro is special to ostree types currently.
bind_ostree_obj!(Sysroot);
bind_ostree_obj!(Repo);
bind_ostree_obj!(Deployment);
// List of non-ostree types we want to bind; if you need to extend this list
// try to instead create a bind_gio_obj!() macro or so.
#[repr(transparent)]
pub struct FFIGCancellable(gio_sys::GCancellable);
unsafe impl ExternType for FFIGCancellable {
type Id = type_id!(rpmostreecxx::GCancellable);
type Kind = cxx::kind::Trivial;
}
impl_wrap!(FFIGCancellable, gio::Cancellable);
// An error type helper; separate from the GObject bridging
mod err {
use std::error::Error as StdError;
use std::fmt::Display;
use std::io::Error as IoError;
// See the documentation for CxxResult
#[derive(Debug)]
pub(crate) struct CxxError(String);
impl Display for CxxError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.0.as_str())
}
}
impl StdError for CxxError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
fn cause(&self) -> Option<&dyn StdError> {
None
}
}
impl From<anyhow::Error> for CxxError {
fn from(v: anyhow::Error) -> Self {
Self(format!("{:#}", v))
}
}
impl From<IoError> for CxxError {
fn from(v: IoError) -> Self {
Self(format!("{}", v))
}
}
impl From<nix::Error> for CxxError {
fn from(v: nix::Error) -> Self {
Self(format!("{}", v))
}
}
impl From<glib::error::Error> for CxxError {
fn from(v: glib::error::Error) -> Self {
Self(format!("{}", v))
}
}
// Use this on exit from Rust functions that return to C++ (bridged via cxx-rs).
// This is a workaround for https://github.com/dtolnay/cxx/issues/290#issuecomment-756432907
// which is that cxx-rs only shows the first entry in the cause chain.
pub(crate) type CxxResult<T> = std::result::Result<T, CxxError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn throwchain() {
use anyhow::Context;
fn outer() -> CxxResult<()> {
fn inner() -> anyhow::Result<()> {
anyhow::bail!("inner")
}
Ok(inner().context("Failed in outer")?)
}
assert_eq!(
format!("{}", outer().err().unwrap()),
"Failed in outer: inner"
)
}
}
}
pub(crate) use err::*;