Skip to main content

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}