snarkvm_debug/file/
avm.rs1use crate::{
16 prelude::{FromBytes, Network, ProgramID, ToBytes},
17 synthesizer::Program,
18};
19
20use anyhow::{anyhow, ensure, Result};
21use std::{
22 fs::{self, File},
23 io::Write,
24 path::Path,
25};
26
27static AVM_FILE_EXTENSION: &str = "avm";
28
29pub struct AVMFile<N: Network> {
30 file_name: String,
32 program: Program<N>,
34}
35
36impl<N: Network> AVMFile<N> {
37 pub fn create(directory: &Path, program: Program<N>, is_main: bool) -> Result<Self> {
39 ensure!(directory.exists(), "The program directory does not exist: '{}'", directory.display());
41 ensure!(
43 !Program::is_reserved_keyword(program.id().name()),
44 "Program name is invalid (reserved): {}",
45 program.id()
46 );
47
48 let file_name = if is_main { Self::main_file_name() } else { format!("{}.{AVM_FILE_EXTENSION}", program.id()) };
50 let path = directory.join(file_name);
52 File::create(&path)?.write_all(&program.to_bytes_le()?)?;
54
55 Self::from_filepath(&path)
56 }
57
58 pub fn open(directory: &Path, program_id: &ProgramID<N>, is_main: bool) -> Result<Self> {
60 ensure!(directory.exists(), "The build directory does not exist: '{}'", directory.display());
62
63 let file_name = if is_main { Self::main_file_name() } else { format!("{program_id}.{AVM_FILE_EXTENSION}") };
65 let path = directory.join(file_name);
67 ensure!(path.exists(), "The AVM file is missing: '{}'", path.display());
69
70 let avm_file = Self::from_filepath(&path)?;
72
73 Ok(avm_file)
74 }
75
76 pub fn exists_at(&self, file_path: &Path) -> bool {
78 Self::check_path(file_path).is_ok() && file_path.exists()
80 }
81
82 pub fn main_exists_at(directory: &Path) -> bool {
84 let path = directory.join(Self::main_file_name());
86 path.is_file() && path.exists()
88 }
89
90 pub fn main_file_name() -> String {
92 format!("main.{AVM_FILE_EXTENSION}")
93 }
94
95 pub fn file_name(&self) -> &str {
97 &self.file_name
98 }
99
100 pub const fn program(&self) -> &Program<N> {
102 &self.program
103 }
104
105 pub fn remove(&self, path: &Path) -> Result<()> {
107 if !path.exists() {
109 Ok(())
110 } else {
111 Self::check_path(path)?;
113 if path.exists() {
115 fs::remove_file(path)?;
117 }
118 Ok(())
119 }
120 }
121}
122
123impl<N: Network> AVMFile<N> {
124 fn check_path(path: &Path) -> Result<()> {
126 ensure!(path.is_file(), "The path is not a file.");
128
129 let extension = path.extension().ok_or_else(|| anyhow!("File extension not found."))?;
131 ensure!(extension == AVM_FILE_EXTENSION, "File extension is incorrect.");
132
133 Ok(())
134 }
135
136 fn from_filepath(file: &Path) -> Result<Self> {
138 Self::check_path(file)?;
140
141 ensure!(file.exists(), "File does not exist: {}", file.display());
143
144 let file_name = file
146 .file_stem()
147 .ok_or_else(|| anyhow!("File name not found."))?
148 .to_str()
149 .ok_or_else(|| anyhow!("File name not found."))?
150 .to_string();
151
152 let program_bytes = fs::read(file)?;
154 let program = Program::from_bytes_le(&program_bytes)?;
156
157 Ok(Self { file_name, program })
158 }
159
160 pub fn write_to(&self, path: &Path) -> Result<()> {
162 Self::check_path(path)?;
164
165 let file_name = path
167 .file_stem()
168 .ok_or_else(|| anyhow!("File name not found."))?
169 .to_str()
170 .ok_or_else(|| anyhow!("File name not found."))?
171 .to_string();
172 ensure!(file_name == self.file_name, "File name does not match.");
174
175 Ok(File::create(path)?.write_all(&self.program.to_bytes_le()?)?)
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::prelude::Parser;
183
184 type CurrentNetwork = snarkvm_console::network::Testnet3;
185
186 fn temp_dir() -> std::path::PathBuf {
187 tempfile::tempdir().expect("Failed to open temporary directory").into_path()
188 }
189
190 #[test]
191 fn test_from_path() {
192 let directory = temp_dir();
194
195 let program_string = r"
196program token.aleo;
197
198record token:
199 owner as address.private;
200 token_amount as u64.private;
201
202function compute:
203 input r0 as token.record;
204 add r0.token_amount r0.token_amount into r1;
205 output r1 as u64.private;";
206
207 let (string, program) = Program::<CurrentNetwork>::parse(program_string).unwrap();
209 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
210
211 let path = directory.join("token.avm");
213 let mut file = File::create(&path).unwrap();
214 file.write_all(&program.to_bytes_le().unwrap()).unwrap();
215
216 let file = AVMFile::from_filepath(&path).unwrap();
218
219 assert_eq!("token", file.file_name());
220 assert_eq!(&program, file.program());
221 }
222}