candid_core/lib.rs
1//! A canonical, validated Candid Contract graph.
2//!
3//! This crate deliberately delegates DID parsing and semantic checking to the
4//! official `candid_parser` engine. It projects the checked result into a
5//! host-neutral JSON model; it does not implement a second Candid parser or
6//! codec.
7//!
8//! # Feature surfaces
9//!
10//! One package, four build surfaces. Every feature is enabled by default, so
11//! an existing `candid-core = "0.1"` dependency is unchanged.
12//!
13//! | Feature | Adds | Dependencies it pulls in |
14//! | --- | --- | --- |
15//! | *(base)* `default-features = false` | `Contract`, `ContractDraft`, `RawContract`, validation, canonicalization, the semantic Contract identities, detached [`artifact_id_with_limits`], `Limits`/`RuntimeContext`, `Diagnostic`, `ContractEnvelope` | `serde`, `serde_json`, `sha2`, `hex` |
16//! | `host-value` | `HostValue` and graph-directed value validation | `ic_principal` |
17//! | `compiler` | `compile_did`, `compile_with_resolver`, `Compilation`, `SourceId`/`SourceResolver`/`MemoryResolver`, `SourceInfo` provenance | `candid`, `candid_parser` |
18//! | `filesystem-compiler` (implies `compiler`) | `WorkspaceResolver`, `compile_did_file`, source materialization, the `candid-core` binary | `cap-std` |
19//!
20//! Items outside the enabled set are absent at compile time rather than
21//! present as failing stubs, so a build error names the feature to turn on.
22//!
23//! # Targets
24//!
25//! The `compiler` surface — self-contained sources through `compile_did` and
26//! imported logical bundles through `compile_with_resolver` — needs no
27//! filesystem and builds *and runs* on `wasm32-unknown-unknown`, which is where
28//! browser WASM lands. `filesystem-compiler` items still compile there (the
29//! `cap-std` capability is target-conditional), but `WorkspaceResolver` has no
30//! directory to open on that target and its construction fails.
31//!
32//! Bare `wasm32-unknown-unknown` has no clock. Cancellation and every
33//! quantitative limit in [`Limits`] behave exactly as they do natively; an
34//! explicit `Limits::deadline_unix_ms` cannot be measured there and therefore
35//! fails closed with `operation_deadline_exceeded` instead of calling an
36//! unsupported clock. No deadline configured stays unbounded, as it is
37//! everywhere else. The crate takes no browser clock dependency to change
38//! that.
39//!
40//! Cargo unifies features across a dependency graph: if anything in a build
41//! also depends on `candid-core` with defaults, the whole surface is compiled
42//! once for every consumer in that build. Feature selection bounds what a
43//! given dependency graph *must* contain, not what a mixed graph produces.
44
45// Two invariants bracket the supported pointer width. Portable wire values
46// (diagnostic counts, span offsets, limit overrides) are fixed-width `u64`,
47// and the crate widens `usize` counters into them with plain casts that are
48// exact only while `usize` fits in 64 bits (so no target wider than 64 bits).
49// The `InteractiveV1` default limit values exceed a 16-bit `usize` (so no
50// target narrower than 32 bits). That leaves 32- and 64-bit targets — which
51// covers every std platform, `wasm32` included. Refuse to compile elsewhere
52// with a clear message rather than silently truncating or overflowing a
53// literal.
54#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
55compile_error!("candid-core supports only 32- and 64-bit targets: portable u64 wire values must represent every usize exactly (usize must not exceed 64 bits), and the InteractiveV1 default limits require a usize of at least 32 bits");
56
57mod artifact_id;
58// `bounded` reads a capped byte count off a `std::io::Read`; only the native
59// filesystem resolver uses it, and only where a real filesystem exists.
60#[cfg(all(feature = "filesystem-compiler", not(target_os = "unknown")))]
61mod bounded;
62mod budget;
63mod canonical;
64#[cfg(feature = "compiler")]
65mod compile;
66mod diagnostics;
67mod envelope;
68mod limits;
69mod model;
70mod name_hash;
71#[cfg(feature = "compiler")]
72mod resolver;
73#[cfg(feature = "compiler")]
74mod source;
75mod validate;
76#[cfg(feature = "host-value")]
77mod value;
78
79pub use artifact_id::{artifact_id_with_context, artifact_id_with_limits, ArtifactKind};
80#[cfg(feature = "compiler")]
81pub use compile::{
82 compile_did, compile_did_with_context, compile_did_with_options, compile_with_resolver,
83 Compilation, CompileOptions,
84};
85#[cfg(feature = "filesystem-compiler")]
86pub use compile::{compile_did_file, compile_did_file_with_context, compile_did_file_with_options};
87#[cfg(feature = "compiler")]
88pub use diagnostics::CompileError;
89pub use diagnostics::{
90 Diagnostic, DiagnosticPhase, RelatedLocation, ResourceLimitInfo, Severity, SourceSpan,
91};
92pub use envelope::ContractEnvelope;
93pub use limits::{
94 CancellationToken, Limits, LimitsConfig, LimitsConfigError, LimitsProfile, RuntimeContext,
95 LIMITS_CONFIG_VERSION,
96};
97pub use model::{
98 Actor, Contract, ContractDraft, ContractIdentities, ContractJsonError, ContractValidationError,
99 ContractViolation, Declaration, Field, MethodMode, PrimitiveType, ProducerInfo, RawContract,
100 ServiceMethod, TypeNode, TypeRef, CANONICALIZATION_PROFILE, CONTRACT_FORMAT, FORMAT_VERSION,
101 SEMANTICS_PROFILE,
102};
103// Provenance is compiler surface: a presented sidecar is authenticated by
104// recompiling its embedded bundle, which is compiler logic.
105#[cfg(feature = "compiler")]
106pub use model::{
107 FieldLabelProvenance, RawSourceInfo, SourceActorInfo, SourceDeclaration, SourceFileInfo,
108 SourceFunctionArgumentDirection, SourceFunctionArgumentInfo, SourceImportInfo,
109 SourceImportKind, SourceInfo, SourceLabel, SourceMethodInfo, SourceOrigin, SOURCE_INFO_VERSION,
110};
111#[cfg(feature = "filesystem-compiler")]
112pub use resolver::WorkspaceResolver;
113#[cfg(feature = "compiler")]
114pub use resolver::{MemoryResolver, ResolveError, ResolvedSource, SourceId, SourceResolver};
115#[cfg(feature = "host-value")]
116pub use value::{
117 validate_host_value, validate_host_value_with_context, ContractMethodRef, ContractTypeRef,
118 HostFieldValue, HostValue, HostValueJsonError, HostValueValidationError, HostValueViolation,
119};
120
121// A disabled feature must remove its API, not replace it with something that
122// compiles and then fails at run time. `tests/model_public_api.rs` proves each
123// surface is *present* when its feature is on; these doctests are the other
124// direction, and they only exist in the configurations where the surface
125// should be gone.
126#[cfg(not(feature = "compiler"))]
127mod compiler_surface_is_absent_without_its_feature {
128 //! ```compile_fail
129 //! let _ = candid_core::compile_did("service : {};");
130 //! ```
131 //!
132 //! ```compile_fail
133 //! let _: candid_core::MemoryResolver = candid_core::MemoryResolver::new();
134 //! ```
135 //!
136 //! ```compile_fail
137 //! fn takes(_: candid_core::SourceInfo) {}
138 //! ```
139 //!
140 //! ```compile_fail
141 //! let _ = candid_core::compile_with_resolver;
142 //! ```
143}
144
145#[cfg(all(feature = "compiler", not(feature = "filesystem-compiler")))]
146mod filesystem_surface_is_absent_without_its_feature {
147 //! ```compile_fail
148 //! let _ = candid_core::compile_did_file("service.did");
149 //! ```
150 //!
151 //! ```compile_fail
152 //! let _ = candid_core::WorkspaceResolver::new(".");
153 //! ```
154 //!
155 //! Imported-bundle compilation is on the other side of this boundary: it
156 //! is `compiler` surface since issue #21, so it must still resolve here.
157 //!
158 //! ```
159 //! let _ = candid_core::compile_with_resolver;
160 //! ```
161}
162
163#[cfg(not(feature = "host-value"))]
164mod host_value_surface_is_absent_without_its_feature {
165 //! ```compile_fail
166 //! let _ = candid_core::HostValue::null();
167 //! ```
168 //!
169 //! ```compile_fail
170 //! fn takes(_: candid_core::HostValueValidationError) {}
171 //! ```
172}