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}