use crate::{errors::ManifestError, package::Package};
use serde::Deserialize;
use std::{
borrow::Cow,
convert::TryFrom,
fs::File,
io::{Read, Write},
path::Path,
};
pub const MANIFEST_FILENAME: &str = "Leo.toml";
#[derive(Clone, Deserialize)]
pub struct Remote {
pub author: String,
}
#[derive(Deserialize)]
pub struct Manifest {
pub project: Package,
pub remote: Option<Remote>,
}
impl Manifest {
pub fn new(package_name: &str) -> Self {
Self {
project: Package::new(package_name),
remote: None,
}
}
pub fn filename() -> String {
MANIFEST_FILENAME.to_string()
}
pub fn exists_at(path: &Path) -> bool {
let mut path = Cow::from(path);
if path.is_dir() {
path.to_mut().push(MANIFEST_FILENAME);
}
path.exists()
}
pub fn get_package_name(&self) -> String {
self.project.name.clone()
}
pub fn get_package_version(&self) -> String {
self.project.version.clone()
}
pub fn get_package_description(&self) -> Option<String> {
self.project.description.clone()
}
pub fn get_package_license(&self) -> Option<String> {
self.project.license.clone()
}
pub fn get_package_remote(&self) -> Option<Remote> {
self.remote.clone()
}
pub fn write_to(self, path: &Path) -> Result<(), ManifestError> {
let mut path = Cow::from(path);
if path.is_dir() {
path.to_mut().push(MANIFEST_FILENAME);
}
let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILENAME, error))?;
file.write_all(self.template().as_bytes())
.map_err(|error| ManifestError::Writing(MANIFEST_FILENAME, error))
}
fn template(&self) -> String {
format!(
r#"[project]
name = "{name}"
version = "0.1.0"
description = "The {name} package"
license = "MIT"
[remote]
author = "[AUTHOR]" # Add your Aleo Package Manager username, team's name, or organization's name.
"#,
name = self.project.name
)
}
}
impl TryFrom<&Path> for Manifest {
type Error = ManifestError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
let mut path = Cow::from(path);
if path.is_dir() {
path.to_mut().push(MANIFEST_FILENAME);
}
let mut file = File::open(path.clone()).map_err(|error| ManifestError::Opening(MANIFEST_FILENAME, error))?;
let size = file
.metadata()
.map_err(|error| ManifestError::Metadata(MANIFEST_FILENAME, error))?
.len() as usize;
let mut buffer = String::with_capacity(size);
file.read_to_string(&mut buffer)
.map_err(|error| ManifestError::Reading(MANIFEST_FILENAME, error))?;
let mut old_remote_format: Option<&str> = None;
let mut new_remote_format_exists = false;
let mut final_toml = "".to_owned();
let mut refactored_toml = "".to_owned();
for line in buffer.lines() {
if line.starts_with("remote") {
let remote = line
.split('=') .collect::<Vec<&str>>()[1]; old_remote_format = Some(remote);
if cfg!(not(feature = "manifest_refactor_remote")) {
refactored_toml += line;
refactored_toml += "\n";
}
continue;
}
if line.starts_with("[remote]") {
new_remote_format_exists = true;
}
if line.starts_with("[package]") {
final_toml += "[project]";
match cfg!(feature = "manifest_refactor_project") {
true => refactored_toml += "[project]",
false => refactored_toml += line,
}
} else {
final_toml += line;
refactored_toml += line;
}
final_toml += "\n";
refactored_toml += "\n";
}
if let Some(old_remote) = old_remote_format {
if !new_remote_format_exists {
let remote_author = old_remote
.split('/') .collect::<Vec<&str>>()[0] .replace(&['\"', ' '][..], "");
let new_remote = format!(
r#"
[remote]
author = "{author}"
"#,
author = remote_author
);
final_toml += &new_remote;
if cfg!(feature = "manifest_refactor_remote") {
refactored_toml += &new_remote;
}
}
}
if buffer != refactored_toml {
let mut file = File::create(&path).map_err(|error| ManifestError::Creating(MANIFEST_FILENAME, error))?;
file.write_all(refactored_toml.as_bytes())
.map_err(|error| ManifestError::Writing(MANIFEST_FILENAME, error))?;
}
toml::from_str(&final_toml).map_err(|error| ManifestError::Parsing(MANIFEST_FILENAME, error))
}
}