cairo-annotations 0.2.0

Structured annotations for Cairo
Documentation
use assert_fs::fixture::PathCopy;
use assert_fs::TempDir;
use cairo_annotations::trace_data::{CairoExecutionInfo, CasmLevelInfo, VersionedCallTrace};
use cairo_lang_sierra::debug_info::DebugInfo;
use cairo_lang_sierra::program::{ProgramArtifact, VersionedProgram};
use cairo_lang_sierra_to_casm::compiler::{CairoProgramDebugInfo, SierraToCasmConfig};
use cairo_lang_sierra_to_casm::metadata::{calc_metadata, MetadataComputationConfig};
use serde::de::DeserializeOwned;
use snapbox::cmd::Command as SnapboxCommand;
use std::fs;
use std::path::PathBuf;

pub struct TestProject {
    dir: TempDir,
}

impl TestProject {
    pub fn new(test_project_name: &str) -> Self {
        let dir = TempDir::new().unwrap();

        dir.copy_from(
            format!("tests/data/{test_project_name}/"),
            &["*.toml", "*.cairo"],
        )
        .unwrap();

        Self { dir }
    }

    pub fn generate_trace_files(self) -> TestProjectOutput {
        SnapboxCommand::new("snforge")
            .arg("test")
            .arg("--save-trace-data")
            .current_dir(&self.dir)
            .assert()
            .success();
        TestProjectOutput(self)
    }
}

pub struct TestProjectOutput(TestProject);

impl TestProjectOutput {
    pub fn first_trace_file(self) -> TraceFile {
        let trace_path = self.0.dir.path().join("snfoundry_trace");

        let trace_file_path = fs::read_dir(&trace_path)
            .unwrap()
            .next()
            .map(|entry| entry.unwrap().path())
            .unwrap();

        let VersionedCallTrace::V1(call_trace) = read_and_deserialize(&trace_file_path);

        let cairo_execution_info = call_trace.cairo_execution_info.unwrap();
        let VersionedProgram::V1 { program, .. } =
            read_and_deserialize(&cairo_execution_info.source_sierra_path.clone().into());

        let project_dir = self.0.dir.path().canonicalize().unwrap();
        TraceFile {
            project_dir,
            cairo_execution_info,
            program,
        }
    }
}

pub struct TraceFile {
    project_dir: PathBuf,
    cairo_execution_info: CairoExecutionInfo,
    program: ProgramArtifact,
}

impl TraceFile {
    pub fn get_project_dir(&self) -> String {
        self.project_dir.display().to_string()
    }
    pub fn get_casm_level_info(&self) -> &CasmLevelInfo {
        &self.cairo_execution_info.casm_level_info
    }

    pub fn get_debug_info(&self) -> &DebugInfo {
        self.program.debug_info.as_ref().unwrap()
    }

    pub fn get_casm_debug_info(&self) -> CairoProgramDebugInfo {
        cairo_lang_sierra_to_casm::compiler::compile(
            &self.program.program,
            &calc_metadata(&self.program.program, MetadataComputationConfig::default()).unwrap(),
            SierraToCasmConfig {
                gas_usage_check: false,
                max_bytecode_size: usize::MAX,
            },
        )
        .map(|casm| casm.debug_info)
        .unwrap()
    }
}

fn read_and_deserialize<T: DeserializeOwned>(file_path: &PathBuf) -> T {
    let content = fs::read_to_string(file_path).unwrap();
    serde_json::from_str(&content).unwrap()
}