ab_contracts_tooling/
target_specification.rs

1//! Abundance target specification for contracts
2
3use anyhow::Context;
4use dirs::cache_dir;
5use std::fs::{File, create_dir_all};
6use std::io::{Read, Seek, Write};
7use std::path::{Path, PathBuf};
8
9const TARGET_SPECIFICATION: &str = include_str!("riscv64em-unknown-none-abundance.json");
10
11/// Target specification for contracts
12#[derive(Debug)]
13pub struct TargetSpecification {
14    path: PathBuf,
15    _file: File,
16}
17
18impl TargetSpecification {
19    /// Create a target specification instance.
20    ///
21    /// `base_directory` is used to store the target specification JSON file.
22    pub fn create(base_directory: &Path) -> anyhow::Result<Self> {
23        let path = base_directory.join("riscv64em-unknown-none-abundance.json");
24        let mut file = File::options()
25            .read(true)
26            .write(true)
27            .create(true)
28            .truncate(false)
29            .open(&path)
30            .context("Failed to open target specification file")?;
31
32        // Ensure the target specification file has expected content
33        loop {
34            file.lock_shared()
35                .context("Failed to lock target specification file")?;
36
37            let mut actual_target_specification = String::with_capacity(TARGET_SPECIFICATION.len());
38            file.seek(std::io::SeekFrom::Start(0))
39                .context("Failed to seek to start of target specification file")?;
40            file.read_to_string(&mut actual_target_specification)
41                .context("Failed to read target specification file")?;
42
43            if actual_target_specification == TARGET_SPECIFICATION {
44                break;
45            }
46
47            file.unlock()
48                .context("Failed to unlock target specification file")?;
49            file.lock()
50                .context("Failed to lock target specification file")?;
51            file.set_len(0)
52                .context("Failed to truncate target specification file")?;
53            file.seek(std::io::SeekFrom::Start(0))
54                .context("Failed to seek to start of target specification file")?;
55            file.write_all(TARGET_SPECIFICATION.as_bytes())
56                .context("Failed to write target specification file")?;
57            file.sync_all()
58                .context("Failed to sync target specification file")?;
59            file.unlock()
60                .context("Failed to unlock target specification file")?;
61        }
62
63        Ok(Self { path, _file: file })
64    }
65
66    /// Create (if not exists) and return the default base directory used for storing the target
67    /// specifications JSON file
68    pub fn default_base_dir() -> anyhow::Result<PathBuf> {
69        let app_dir = cache_dir()
70            .context("Failed to get cache directory")?
71            .join("ab-contracts");
72        create_dir_all(&app_dir)
73            .with_context(|| format!("Failed to create cache directory {}", app_dir.display()))?;
74
75        Ok(app_dir)
76    }
77
78    /// Get the path to the target specification JSON file
79    pub fn path(&self) -> &Path {
80        &self.path
81    }
82}