canic_core/
lib.rs

1//! Core Canic library used inside canisters.
2//!
3//! Most users should depend on the `canic` facade crate, which re-exports this crate
4//! under `canic::core` and exposes the common entrypoint macros:
5//! - `canic::build!` / `canic::build_root!` (in `build.rs`) to validate/embed `canic.toml`
6//! - `canic::start!` / `canic::start_root!` (in `lib.rs`) to wire lifecycle hooks and export endpoints
7//!
8//! ## Layering
9//!
10//! Canic is organized to keep endpoint code thin and policies centralized:
11//! - `access/` contains guard/auth/policy helpers for boundary enforcement.
12//! - `model/` owns storage (stable memory) and in-process registries/caches.
13//! - `workflow/` implements orchestration and lifecycle workflows.
14//! - `ops/` provides mechanical, reusable side-effecting operations.
15//! - `macros/` provides public macro entrypoints and endpoint bundles.
16//!
17//! The default flow is: endpoints → ops → model.
18
19// -----------------------------------------------------------------------------
20// Phase 0: path coherence re-exports (no behavior change)
21// -----------------------------------------------------------------------------
22
23pub mod access;
24pub mod api;
25pub mod config;
26pub mod dispatch;
27pub mod dto;
28pub mod ids;
29pub mod infra;
30pub mod lifecycle;
31pub mod log;
32pub mod macros;
33pub(crate) mod model;
34pub mod ops;
35pub mod perf;
36pub mod policy;
37pub mod types;
38pub mod workflow;
39
40pub use ::canic_cdk as cdk;
41pub use ::canic_memory as memory;
42pub use ::canic_memory::{eager_init, eager_static, ic_memory, ic_memory_range};
43pub use ::canic_utils as utils;
44
45/// Internal re-exports required for macro expansion.
46/// Not part of the public API.
47#[doc(hidden)]
48pub mod __reexports {
49    pub use ::ctor;
50}
51
52pub use thiserror::Error as ThisError;
53
54use crate::cdk::{
55    call::{CallFailed, CandidDecodeFailed, Error as CallError},
56    candid::{CandidType, Error as CandidError},
57};
58use serde::Deserialize;
59
60///
61/// Crate Version
62///
63
64pub const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
65pub const VERSION: &str = env!("CARGO_PKG_VERSION");
66
67///
68/// Error
69/// top level error should handle all sub-errors, but not expose the child candid types
70///
71
72#[derive(CandidType, Debug, Deserialize, ThisError)]
73pub enum Error {
74    #[error("{0}")]
75    AccessError(String),
76
77    #[error("{0}")]
78    ConfigError(String),
79
80    #[error("{0}")]
81    CustomError(String),
82
83    #[error("{0}")]
84    InfraError(String),
85
86    #[error("{0}")]
87    ModelError(String),
88
89    #[error("{0}")]
90    OpsError(String),
91
92    #[error("{0}")]
93    PolicyError(String),
94
95    #[error("{0}")]
96    SerializeError(String),
97
98    #[error("{0}")]
99    WorkflowError(String),
100
101    ///
102    /// Http Errors
103    ///
104
105    #[error("http request failed: {0}")]
106    HttpRequest(String),
107
108    #[error("http error status: {0}")]
109    HttpErrorCode(u32),
110
111    #[error("http decode failed: {0}")]
112    HttpDecode(String),
113
114    ///
115    /// Test Error
116    /// as we don't want to import dev-dependencies
117    ///
118
119    #[error("{0}")]
120    TestError(String),
121
122    ///
123    /// Common IC errors
124    ///
125    /// CallError          : should be automatic with ?
126    /// CallFailed         : use this for wrapping <T, String> return values
127    /// CandidError        : for decode_one errors etc.  automatic
128    /// CandidDecodeFailed : automatic for calls like ::candid<T>()
129    ///
130
131    #[error("call error: {0}")]
132    CallError(String),
133
134    #[error("call failed: {0}")]
135    CallFailed(String),
136
137    #[error("candid error: {0}")]
138    CandidError(String),
139
140    #[error("candid decode failed: {0}")]
141    CandidDecodeFailed(String),
142}
143
144macro_rules! from_to_string {
145    ($from:ty, $variant:ident) => {
146        impl From<$from> for Error {
147            fn from(e: $from) -> Self {
148                Error::$variant(e.to_string())
149            }
150        }
151    };
152}
153
154impl Error {
155    /// Build a custom error from a string without defining a new variant.
156    #[must_use]
157    pub fn custom<S: Into<String>>(s: S) -> Self {
158        Self::CustomError(s.into())
159    }
160
161    /// Build a test error to avoid extra dev-only dependencies.
162    #[must_use]
163    pub fn test<S: Into<String>>(s: S) -> Self {
164        Self::TestError(s.into())
165    }
166}
167
168from_to_string!(access::AccessError, AccessError);
169from_to_string!(config::ConfigError, ConfigError);
170from_to_string!(infra::InfraError, InfraError);
171from_to_string!(model::ModelError, ModelError);
172from_to_string!(ops::OpsError, OpsError);
173from_to_string!(policy::PolicyError, PolicyError);
174from_to_string!(workflow::WorkflowError, WorkflowError);
175
176from_to_string!(serde_json::Error, HttpDecode);
177from_to_string!(CallError, CallError);
178from_to_string!(CallFailed, CallFailed);
179from_to_string!(CandidDecodeFailed, CandidDecodeFailed);
180from_to_string!(CandidError, CandidError);