ic_build/artifacts/candid/
mod.rs1use ic_host_artifacts::artifact::{ArtifactError, ArtifactIdentity};
5use ic_host_fs::read::hash_file;
6use ic_host_process::tool::{
7 AdmittedTool, CompletedExecution, ExecutionContext, OutputLimits, ToolError,
8};
9use std::{fmt, path::Path};
10
11#[cfg(test)]
12mod test_support;
13#[cfg(test)]
14mod tests;
15
16#[derive(Debug, thiserror::Error)]
18pub enum ExtractionError {
19 #[error("Candid source path must be absolute")]
21 SourcePath,
22 #[error("Candid source inspection failed: {0}")]
24 Input(#[source] ArtifactError),
25 #[error("{0}")]
27 Tool(#[source] ToolError),
28 #[error("Candid source re-inspection failed")]
30 SourceInspection {
31 source: ArtifactError,
33 evidence: Box<CompletedExecution>,
35 },
36 #[error("Candid source changed during extraction")]
38 SourceChanged {
39 before: ArtifactIdentity,
41 after: ArtifactIdentity,
43 evidence: Box<CompletedExecution>,
45 },
46}
47
48pub struct ExtractedCandid {
50 pub source_identity: ArtifactIdentity,
52 pub tool_identity: ArtifactIdentity,
54 pub evidence: CompletedExecution,
56}
57
58impl fmt::Debug for ExtractedCandid {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 f.debug_struct("ExtractedCandid")
61 .field("source_identity", &self.source_identity)
62 .field("tool_identity", &self.tool_identity)
63 .field("evidence", &self.evidence)
64 .finish()
65 }
66}
67
68pub fn extract(
82 tool: &AdmittedTool,
83 source: &Path,
84 context: &ExecutionContext<'_>,
85 source_bytes: u64,
86 output: OutputLimits,
87) -> Result<ExtractedCandid, ExtractionError> {
88 if !source.is_absolute() {
89 return Err(ExtractionError::SourcePath);
90 }
91 let before = hash_file(source, source_bytes).map_err(ExtractionError::Input)?;
92 let evidence = tool
93 .run(&[source.as_os_str().to_owned()], context, output)
94 .and_then(CompletedExecution::require_success)
95 .and_then(CompletedExecution::require_complete)
96 .map_err(ExtractionError::Tool)?;
97 let after = match hash_file(source, source_bytes) {
98 Ok(identity) => identity,
99 Err(source) => {
100 return Err(ExtractionError::SourceInspection {
101 source,
102 evidence: Box::new(evidence),
103 });
104 }
105 };
106 if before != after {
107 return Err(ExtractionError::SourceChanged {
108 before,
109 after,
110 evidence: Box::new(evidence),
111 });
112 }
113 Ok(ExtractedCandid {
114 source_identity: before,
115 tool_identity: tool.identity(),
116 evidence,
117 })
118}