use ic_host_artifacts::artifact::{ArtifactError, ArtifactIdentity};
use ic_host_fs::read::hash_file;
use ic_host_process::tool::{
AdmittedTool, CompletedExecution, ExecutionContext, OutputLimits, ToolError,
};
use std::{fmt, path::Path};
#[cfg(test)]
mod test_support;
#[cfg(test)]
mod tests;
#[derive(Debug, thiserror::Error)]
pub enum ExtractionError {
#[error("Candid source path must be absolute")]
SourcePath,
#[error("Candid source inspection failed: {0}")]
Input(#[source] ArtifactError),
#[error("{0}")]
Tool(#[source] ToolError),
#[error("Candid source re-inspection failed")]
SourceInspection {
source: ArtifactError,
evidence: Box<CompletedExecution>,
},
#[error("Candid source changed during extraction")]
SourceChanged {
before: ArtifactIdentity,
after: ArtifactIdentity,
evidence: Box<CompletedExecution>,
},
}
pub struct ExtractedCandid {
pub source_identity: ArtifactIdentity,
pub tool_identity: ArtifactIdentity,
pub evidence: CompletedExecution,
}
impl fmt::Debug for ExtractedCandid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExtractedCandid")
.field("source_identity", &self.source_identity)
.field("tool_identity", &self.tool_identity)
.field("evidence", &self.evidence)
.finish()
}
}
pub fn extract(
tool: &AdmittedTool,
source: &Path,
context: &ExecutionContext<'_>,
source_bytes: u64,
output: OutputLimits,
) -> Result<ExtractedCandid, ExtractionError> {
if !source.is_absolute() {
return Err(ExtractionError::SourcePath);
}
let before = hash_file(source, source_bytes).map_err(ExtractionError::Input)?;
let evidence = tool
.run(&[source.as_os_str().to_owned()], context, output)
.and_then(CompletedExecution::require_success)
.and_then(CompletedExecution::require_complete)
.map_err(ExtractionError::Tool)?;
let after = match hash_file(source, source_bytes) {
Ok(identity) => identity,
Err(source) => {
return Err(ExtractionError::SourceInspection {
source,
evidence: Box::new(evidence),
});
}
};
if before != after {
return Err(ExtractionError::SourceChanged {
before,
after,
evidence: Box::new(evidence),
});
}
Ok(ExtractedCandid {
source_identity: before,
tool_identity: tool.identity(),
evidence,
})
}