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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::{collections::HashMap, sync::Arc};

use crate::{
    command::{fn_handler, Factory, Handler},
    context::{Context, GroupContext},
    State,
};

#[derive(Default)]
/// A group of commands.
/// Called from Arma using `[group]:[command]`.
pub struct Group {
    commands: HashMap<String, Box<Handler>>,
    children: HashMap<String, Self>,
    state: State,
}

impl Group {
    #[must_use]
    /// Creates a new group
    pub fn new() -> Self {
        Self {
            commands: HashMap::new(),
            children: HashMap::new(),
            state: State::default(),
        }
    }

    #[inline]
    #[must_use]
    /// Add a new state value to the group if it has not be added already
    pub fn state<T>(self, state: T) -> Self
    where
        T: Send + Sync + 'static,
    {
        self.state.set(state);
        self
    }

    #[inline]
    #[must_use]
    /// Freeze the group's state, preventing the state from changing, allowing for faster reads
    pub fn freeze_state(mut self) -> Self {
        self.state.freeze();
        self
    }

    #[inline]
    #[must_use]
    /// Add a command to the group
    pub fn command<S, F, I, R>(mut self, name: S, handler: F) -> Self
    where
        S: Into<String>,
        F: Factory<I, R> + 'static,
    {
        self.commands
            .insert(name.into(), Box::new(fn_handler(handler)));
        self
    }

    #[inline]
    #[must_use]
    /// Add a group to the group
    pub fn group<S>(mut self, name: S, child: Self) -> Self
    where
        S: Into<String>,
    {
        self.children.insert(name.into(), child);
        self
    }
}

pub(crate) struct InternalGroup {
    commands: HashMap<String, Box<Handler>>,
    children: HashMap<String, Self>,
    pub(crate) state: Arc<State>,
}

impl InternalGroup {
    #[allow(clippy::too_many_arguments)]
    pub(crate) fn handle(
        &self,
        context: Context,
        acm: &crate::ArmaContextManager,
        function: &str,
        output: *mut libc::c_char,
        size: libc::size_t,
        args: Option<*mut *mut i8>,
        count: Option<libc::c_int>,
    ) -> libc::c_int {
        if let Some((group, function)) = function.split_once(':') {
            self.children.get(group).map_or(1, |group| {
                group.handle(context, acm, function, output, size, args, count)
            })
        } else if let Some(handler) = self.commands.get(function) {
            (handler.handler)(
                context.with_group(GroupContext::new(self.state.clone())),
                acm,
                output,
                size,
                args,
                count,
            )
        } else {
            1
        }
    }
}

impl From<Group> for InternalGroup {
    fn from(group: Group) -> Self {
        let children = group
            .children
            .into_iter()
            .map(|(name, group)| (name, Self::from(group)))
            .collect();
        Self {
            commands: group.commands,
            children,
            state: Arc::new(group.state),
        }
    }
}