Skip to main content

ic_host_tools/interface/
mod.rs

1//! IC method identities shared by Candid services and optional Wasm export interpretation.
2//!
3//! No expected service, internal-export allowlist, signature validation or
4//! installation policy is implied. Source names are preserved without trimming.
5
6#[cfg(feature = "ic-interface")]
7use ic_host_artifacts::wasm::{ExportKind, WasmFacts};
8
9#[cfg(all(test, feature = "ic-interface"))]
10mod tests;
11
12/// Candid method mode, retaining the distinction between update and oneway.
13#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
14pub enum MethodMode {
15    /// Ordinary update call.
16    Update,
17    /// Query call.
18    Query,
19    /// Composite query call.
20    CompositeQuery,
21    /// Candid oneway update, with no reply.
22    Oneway,
23}
24
25impl MethodMode {
26    /// Project Candid call semantics to the corresponding Wasm export mode.
27    /// Wasm has no separate oneway export namespace.
28    #[must_use]
29    pub const fn export_mode(self) -> Self {
30        match self {
31            Self::Oneway => Self::Update,
32            mode => mode,
33        }
34    }
35}
36
37/// Borrowed method identity for comparing declared and exported interfaces.
38#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
39pub struct ExportedMethod<'a> {
40    /// Complete method name after its IC export prefix, including whitespace.
41    pub name: &'a str,
42    /// Export namespace: update, query or composite query, never oneway.
43    pub mode: MethodMode,
44}
45
46/// Interpret function exports in the three IC method namespaces.
47///
48/// Uses already-inspected, caller-bounded Wasm facts; no source is reparsed or
49/// allocation performed. Lifecycle and non-function exports are omitted. Internal
50/// CDK methods, empty names and the same name in different modes are retained as
51/// facts for explicit caller admission. This does not validate Wasm signatures.
52#[cfg(feature = "ic-interface")]
53pub fn exported_methods<'a>(
54    facts: &'a WasmFacts<'_>,
55) -> impl Iterator<Item = ExportedMethod<'a>> + 'a {
56    facts.exports.iter().filter_map(|(name, export)| {
57        if export.kind != ExportKind::Function {
58            return None;
59        }
60        [
61            ("canister_update ", MethodMode::Update),
62            ("canister_query ", MethodMode::Query),
63            ("canister_composite_query ", MethodMode::CompositeQuery),
64        ]
65        .into_iter()
66        .find_map(|(prefix, mode)| {
67            name.strip_prefix(prefix)
68                .map(|name| ExportedMethod { name, mode })
69        })
70    })
71}