Skip to main content

guinea_core/actor/
shape.rs

1//! What an actor declared in `actor!`, kept for devtools.
2//!
3//! Every edge is already known at compile time - `actor!` checks them - so
4//! this is the same list, written down as data.
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum Channel {
8    Send,
9    Bg,
10    Emit,
11    Ask,
12}
13
14/// A type's name as devtools show it: its module and itself.
15pub type Name = fn() -> &'static str;
16
17pub fn name<T: ?Sized>() -> &'static str {
18    super::short_type_name::<T>()
19}
20
21#[derive(Clone, Copy)]
22pub struct Edge {
23    pub channel: Channel,
24    pub target: Name,
25    pub looping: bool,
26}
27
28#[derive(Clone, Copy)]
29pub struct Handles {
30    pub message: Name,
31    /// `None` when the handler declared nothing about what it sends.
32    pub edges: Option<&'static [Edge]>,
33    /// Where the handler was written, when `#[handler]` wrote it.
34    pub declared: Option<Declared>,
35}
36
37/// Where an `actor!` was written.
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39pub struct Declared {
40    /// As `file!()` gives it: relative to wherever the compiler ran, which is
41    /// usually the workspace root.
42    pub file: &'static str,
43    pub line: u32,
44    pub column: u32,
45    /// The declaring crate's manifest directory.
46    pub crate_dir: &'static str,
47}
48
49/// Where the line that calls it was written.
50///
51/// `file!` and friends expand at the call site, so a macro that puts this in
52/// what it generates points at the code the author wrote, not at itself.
53#[macro_export]
54macro_rules! declared {
55    () => {
56        $crate::actor::shape::Declared {
57            file: ::core::file!(),
58            line: ::core::line!(),
59            column: ::core::column!(),
60            crate_dir: ::core::env!("CARGO_MANIFEST_DIR"),
61        }
62    };
63}
64
65impl Declared {
66    /// The file on this machine: `file` itself when absolute, otherwise the
67    /// first of `crate_dir` and its ancestors that has it. `None` when the
68    /// sources are not here.
69    pub fn path(&self) -> Option<std::path::PathBuf> {
70        let file = std::path::Path::new(self.file);
71        if file.is_absolute() {
72            return file.exists().then(|| file.to_path_buf());
73        }
74        std::path::Path::new(self.crate_dir)
75            .ancestors()
76            .map(|dir| dir.join(file))
77            .find(|path| path.exists())
78    }
79}
80
81#[derive(Clone, Copy)]
82pub struct Shape {
83    pub handles: &'static [Handles],
84    pub publishes: &'static [Name],
85    pub subscribes: &'static [Name],
86    pub declared: Option<Declared>,
87}
88
89impl Shape {
90    pub const UNKNOWN: Shape = Shape {
91        handles: &[],
92        publishes: &[],
93        subscribes: &[],
94        declared: None,
95    };
96}
97
98impl std::fmt::Debug for Shape {
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        f.debug_struct("Shape")
101            .field(
102                "handles",
103                &self.handles.iter().map(|h| (h.message)()).collect::<Vec<_>>(),
104            )
105            .field(
106                "publishes",
107                &self.publishes.iter().map(|n| n()).collect::<Vec<_>>(),
108            )
109            .field(
110                "subscribes",
111                &self.subscribes.iter().map(|n| n()).collect::<Vec<_>>(),
112            )
113            .field("declared", &self.declared)
114            .finish()
115    }
116}