manifest.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaVM library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use crate::{ 17 prelude::{Network, ProgramID}, 18 synthesizer::Program, 19 }; 20 21 use alphavm_circuit::prelude::IndexMap; 22 23 use anyhow::{Result, anyhow, ensure}; 24 use core::str::FromStr; 25 use std::{ 26 fs::{self, File}, 27 io::Write, 28 path::{Path, PathBuf}, 29 }; 30 31 const MANIFEST_FILE_NAME: &str = "program.json"; 32 33 pub struct Manifest<N: Network> { 34 /// The file path. 35 path: PathBuf, 36 /// The program ID. 37 program_id: ProgramID<N>, 38 /// The program editions. 39 editions: IndexMap<ProgramID<N>, u16>, 40 } 41 42 impl<N: Network> Manifest<N> { 43 /// Creates a new manifest file with the given directory path and program ID. 44 pub fn create(directory: &Path, id: &ProgramID<N>) -> Result<Self> { 45 // Ensure the directory path exists. 46 ensure!(directory.exists(), "The program directory does not exist: '{}'", directory.display()); 47 // Ensure the program name is valid. 48 ensure!(!Program::is_reserved_keyword(id.name()), "Program name is invalid (reserved): {id}"); 49 50 // Construct the initial program manifest string. 51 let manifest_string = format!( 52 r#"{{ 53 "program": "{id}", 54 "version": "0.0.0", 55 "description": "", 56 "license": "MIT", 57 "editions": "{{}}" 58 }} 59 "# 60 ); 61 62 // Construct the file path. 63 let path = directory.join(MANIFEST_FILE_NAME); 64 // Ensure the file path does not already exist. 65 ensure!(!path.exists(), "Manifest file already exists: '{}'", path.display()); 66 67 // Write the file. 68 File::create(&path)?.write_all(manifest_string.as_bytes())?; 69 70 // Return the manifest file. 71 Ok(Self { path, program_id: *id, editions: IndexMap::new() }) 72 } 73 74 /// Opens the manifest file for reading. 75 pub fn open(directory: &Path) -> Result<Self> { 76 // Ensure the directory path exists. 77 ensure!(directory.exists(), "The program directory does not exist: '{}'", directory.display()); 78 79 // Construct the file path. 80 let path = directory.join(MANIFEST_FILE_NAME); 81 // Ensure the file path exists. 82 ensure!(path.exists(), "Manifest file is missing: '{}'", path.display()); 83 84 // Read the file to a string. 85 let manifest_string = fs::read_to_string(&path)?; 86 let json: serde_json::Value = serde_json::from_str(&manifest_string)?; 87 88 // Retrieve the program ID. 89 let id_string = json["program"].as_str().ok_or_else(|| anyhow!("Program ID not found."))?; 90 let id = ProgramID::from_str(id_string)?; 91 // Ensure the program name is valid. 92 ensure!(!Program::is_reserved_keyword(id.name()), "Program name is invalid (reserved): {id}"); 93 94 // Initialize storage for the program editions. 95 let mut editions = IndexMap::<ProgramID<N>, u16>::new(); 96 97 // Retrieve the editions in the manifest, if they exist. 98 if let Some(editions_object) = json["editions"].as_object() { 99 for (id_string, value) in editions_object { 100 let id = ProgramID::from_str(id_string)?; 101 let value = u16::try_from(value.as_u64().ok_or_else(|| anyhow!("The edition must be a number."))?)?; 102 editions.insert(id, value); 103 } 104 } 105 106 // Return the manifest file. 107 Ok(Self { path, program_id: id, editions }) 108 } 109 110 /// Returns `true` if the manifest file exists at the given path. 111 pub fn exists_at(directory: &Path) -> bool { 112 // Construct the file path. 113 let path = directory.join(MANIFEST_FILE_NAME); 114 // Return the result. 115 path.is_file() && path.exists() 116 } 117 118 /// Returns the manifest file name. 119 pub const fn file_name() -> &'static str { 120 MANIFEST_FILE_NAME 121 } 122 123 /// Returns the file path. 124 pub const fn path(&self) -> &PathBuf { 125 &self.path 126 } 127 128 /// Returns the program ID. 129 pub const fn program_id(&self) -> &ProgramID<N> { 130 &self.program_id 131 } 132 133 /// Returns the program editions. 134 pub const fn editions(&self) -> &IndexMap<ProgramID<N>, u16> { 135 &self.editions 136 } 137 }