1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Prolog modules.
//!
//! Modules are prolog namespaces. They are created from an
//! atom. Unlike atoms, modules are not reference-counted and are
//! never garbage collected.
//!
//! This module provides functions and types for interacting with
//! prolog modules.
use super::atom::*;
use super::engine::*;
use super::fli::*;

/// A wrapped fora  prolog module.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Module {
    module: module_t,
}

// a `module_t` is a pointer which is normally not send or
// sync. SWI-Prolog however guarantees that this pointer will remain
// valid.
unsafe impl Send for Module {}
unsafe impl Sync for Module {}

impl Module {
    /// Wrap a `module_t`, which is how the SWI-Prolog fli represents modules.
    ///
    /// # Safety
    /// This is unsafe because no check is done to ensure that the
    /// module_t indeed points at a valid module. The caller will have
    /// to ensure that this is the case.
    pub unsafe fn wrap(module: module_t) -> Self {
        Self { module }
    }

    /// Create a new module from the given name.
    ///
    /// This will panic if no prolog engine is active on this thread.
    pub fn new<A: IntoAtom>(name: A) -> Self {
        assert_some_engine_is_active();
        let atom = name.into_atom();
        unsafe { Self::wrap(PL_new_module(atom.atom_ptr())) }
    }

    /// Return the underlying `module_t` which SWI-Prolog uses to refer to the module.
    pub fn module_ptr(&self) -> module_t {
        self.module
    }

    /// Retrieve the name of this module as an atom and pass it into the given function.
    ///
    /// The atom does not outlive this call, and the reference count
    /// is never incremented. This may be slightly faster in some
    /// cases than returning the name directly.
    ///
    /// This will panic if no prolog engine is active on this thread.
    pub fn with_name<F, R>(&self, func: F) -> R
    where
        F: Fn(&Atom) -> R,
    {
        assert_some_engine_is_active();
        let atom = unsafe { Atom::wrap(PL_module_name(self.module)) };

        let result = func(&atom);

        std::mem::forget(atom);

        result
    }

    /// Retrieve the name of this module as an atom.
    ///
    /// This will panic if no prolog engine is active on this thread.
    pub fn name(&self) -> Atom {
        self.with_name(|n| n.clone())
    }

    /// Retrieve the name of this module as a string.
    ///
    /// This will panic if no prolog engine is active on this thread.
    pub fn name_string(&self) -> String {
        self.with_name(|n| n.name())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn create_and_query_module() {
        let engine = Engine::new();
        let _activation = engine.activate();

        let module = Module::new("foo");
        assert_eq!("foo", module.name_string());
    }
}