use std::cell::Cell;
use std::ffi::CString;
use std::sync::{Mutex, MutexGuard};
use vala_sys as ffi;
use crate::object::RawWrapper;
use crate::{CodeContext, Report, SourceFile};
static CONTEXT_STACK: Mutex<()> = Mutex::new(());
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SourceFileType {
None,
Source,
Package,
Fast,
}
impl SourceFileType {
fn to_ffi(self) -> ffi::ValaSourceFileType {
match self {
SourceFileType::None => ffi::VALA_SOURCE_FILE_TYPE_NONE,
SourceFileType::Source => ffi::VALA_SOURCE_FILE_TYPE_SOURCE,
SourceFileType::Package => ffi::VALA_SOURCE_FILE_TYPE_PACKAGE,
SourceFileType::Fast => ffi::VALA_SOURCE_FILE_TYPE_FAST,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Profile {
GObject,
Posix,
}
impl Profile {
fn to_ffi(self) -> ffi::ValaProfile {
match self {
Profile::GObject => ffi::VALA_PROFILE_GOBJECT,
Profile::Posix => ffi::VALA_PROFILE_POSIX,
}
}
}
impl CodeContext {
pub fn new() -> Self {
unsafe {
Self::from_raw_full(ffi::vala_code_context_new())
.expect("vala_code_context_new returned null")
}
}
pub fn with_current<R>(&self, f: impl FnOnce(&Self) -> R) -> R {
assert!(
!IS_CURRENT.get(),
"CodeContext::with_current is not re-entrant: this thread already \
holds libvala's context, and taking it again would deadlock"
);
let guard = CONTEXT_STACK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let _current = Current::push(self, guard);
f(self)
}
pub fn add_source_filename(&self, filename: &str, is_source: bool) -> bool {
let c = CString::new(filename).expect("filename contains NUL");
unsafe {
ffi::vala_code_context_add_source_filename(
self.as_raw(),
c.as_ptr(),
is_source as ffi::gboolean,
glib_sys::GFALSE,
) != glib_sys::GFALSE
}
}
pub fn add_source_file(&self, file: &SourceFile) {
unsafe { ffi::vala_code_context_add_source_file(self.as_raw(), file.as_raw()) }
}
pub fn add_external_package(&self, pkg: &str) -> bool {
let c = CString::new(pkg).expect("package name contains NUL");
unsafe {
ffi::vala_code_context_add_external_package(self.as_raw(), c.as_ptr())
!= glib_sys::GFALSE
}
}
pub fn set_target_profile(&self, profile: Profile, include_stdpkg: bool) {
unsafe {
ffi::vala_code_context_set_target_profile(
self.as_raw(),
profile.to_ffi(),
include_stdpkg as ffi::gboolean,
)
}
}
pub fn add_define(&self, define: &str) {
let c = CString::new(define).expect("define contains NUL");
unsafe { ffi::vala_code_context_add_define(self.as_raw(), c.as_ptr()) }
}
pub fn check(&self) {
unsafe { ffi::vala_code_context_check(self.as_raw()) }
}
pub fn report(&self) -> Report {
unsafe {
Report::from_raw_none(ffi::vala_code_context_get_report(self.as_raw()))
.expect("context report was null")
}
}
pub fn source_files(&self) -> crate::List<SourceFile> {
unsafe {
crate::collections::List::from_raw_none(ffi::vala_code_context_get_source_files(
self.as_raw(),
))
.expect("source files list was null")
}
}
pub fn root(&self) -> crate::Namespace {
unsafe {
crate::Namespace::from_raw_none(ffi::vala_code_context_get_root(self.as_raw()))
.expect("context root namespace was null")
}
}
pub(crate) fn source_file_type(ty: SourceFileType) -> ffi::ValaSourceFileType {
ty.to_ffi()
}
}
impl Default for CodeContext {
fn default() -> Self {
Self::new()
}
}
thread_local! {
static IS_CURRENT: Cell<bool> = const { Cell::new(false) };
}
struct Current<'a> {
_lock: MutexGuard<'a, ()>,
}
impl<'a> Current<'a> {
fn push(ctx: &CodeContext, lock: MutexGuard<'a, ()>) -> Self {
IS_CURRENT.set(true);
unsafe { ffi::vala_code_context_push(ctx.as_raw()) };
Current { _lock: lock }
}
}
impl Drop for Current<'_> {
fn drop(&mut self) {
unsafe { ffi::vala_code_context_pop() };
IS_CURRENT.set(false);
}
}