finalizes import stabilization

- temporarily disables leo add and leo rm
- lock file is parsed by leo build and used
- error messages improved for some cases
- fetch test is added and improved
This commit is contained in:
damirka 2021-07-18 23:40:26 +03:00
parent 6be4860513
commit c36be2c745
9 changed files with 147 additions and 48 deletions

View File

@ -145,18 +145,18 @@ jobs:
export LEO=/home/circleci/project/project/bin/leo
./project/.circleci/leo-setup.sh
leo-add-remove:
docker:
- image: cimg/rust:1.52.1
resource_class: xlarge
steps:
- attach_workspace:
at: /home/circleci/project/
- run:
name: leo add & remove
command: |
export LEO=/home/circleci/project/project/bin/leo
./project/.circleci/leo-add-remove.sh
# leo-add-remove:
# docker:
# - image: cimg/rust:1.52.1
# resource_class: xlarge
# steps:
# - attach_workspace:
# at: /home/circleci/project/
# - run:
# name: leo add & remove
# command: |
# export LEO=/home/circleci/project/project/bin/leo
# ./project/.circleci/leo-add-remove.sh
leo-check-constraints:
docker:

View File

@ -135,6 +135,11 @@ impl Command for Build {
let package_name = manifest.get_package_name();
let imports_map = manifest.get_imports_map().unwrap_or_default();
// Error out if there are dependencies but no lock file found.
if !imports_map.is_empty() && !context.lock_file_exists()? {
return Err(anyhow!("Dependencies are not installed, please run `leo fetch` first"));
}
// Sanitize the package path to the root directory.
let mut package_path = path.clone();
if package_path.is_file() {
@ -175,6 +180,14 @@ impl Command for Build {
);
}
let imports_map = if context.lock_file_exists()? {
context.lock_file()?.to_hashmap()
} else {
Default::default()
};
dbg!(&imports_map);
// Load the program at `main_file_path`
let program = Compiler::<Fq, EdwardsGroupType>::parse_program_with_input(
package_name.clone(),

View File

@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!
// COMMAND TEMPORARILY DISABLED
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!
use crate::{api::Fetch, commands::Command, context::Context};
use leo_package::imports::{ImportsDirectory, IMPORTS_DIRECTORY_NAME};

View File

@ -59,6 +59,7 @@ impl Command for Fetch {
.map_err(|_| anyhow!("Package Manifest not found"))?
.get_package_dependencies();
// If program has no dependencies in the Leo.toml, exit with success.
let dependencies = match dependencies {
Some(dependencies) => dependencies,
None => return Ok(()),
@ -77,6 +78,8 @@ impl Command for Fetch {
}
impl Fetch {
/// Pulls dependencies and fills in the lock file. Also checks for
/// recursive dependencies with dependency tree.
fn add_dependencies(
&self,
context: Context,
@ -86,8 +89,11 @@ impl Fetch {
) -> Result<()> {
// Go through each dependency in Leo.toml and add it to the imports.
// While adding, pull dependencies of this package as well and check for recursion.
for (_import_name, dependency) in dependencies.into_iter() {
let mut package = Package::from(dependency);
for (import_name, dependency) in dependencies.into_iter() {
let mut package = Package::from(&dependency);
package.import_name = Some(import_name);
// Pull the dependency first.
let path = Add::new(
None,
Some(package.author.clone()),
@ -112,7 +118,11 @@ impl Fetch {
}
// Check imported dependency's dependencies.
let imported_dependencies = create_context(path, None)?.manifest()?.get_package_dependencies();
let imported_dependencies = create_context(path, None)?
.manifest()
.map_err(|_| anyhow!("Unable to parse imported dependency's manifest"))?
.get_package_dependencies();
if let Some(dependencies) = imported_dependencies {
if !dependencies.is_empty() {
// Fill in the lock file with imported dependency and information about its dependencies.

View File

@ -14,6 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!
// COMMAND TEMPORARILY DISABLED
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!
use crate::{commands::Command, context::Context};
use leo_package::LeoPackage;

View File

@ -15,7 +15,7 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{api::Api, config};
use leo_package::root::Manifest;
use leo_package::root::{LockFile, Manifest};
use anyhow::Result;
use std::{convert::TryFrom, env::current_dir, path::PathBuf};
@ -41,10 +41,20 @@ impl Context {
}
}
/// Get package manifest for current context
/// Get package manifest for current context.
pub fn manifest(&self) -> Result<Manifest> {
Ok(Manifest::try_from(self.dir()?.as_path())?)
}
/// Get lock file for current context.
pub fn lock_file(&self) -> Result<LockFile> {
Ok(LockFile::try_from(self.dir()?.as_path())?)
}
/// Check if lock file exists.
pub fn lock_file_exists(&self) -> Result<bool> {
Ok(LockFile::exists_at(&self.dir()?))
}
}
/// Create a new context for the current directory.

View File

@ -22,7 +22,7 @@ pub mod logger;
pub mod updater;
use commands::{
package::{Add, Clone, Fetch, Login, Logout, Publish, Remove},
package::{Clone, Fetch, Login, Logout, Publish},
Build,
Clean,
Command,
@ -131,13 +131,12 @@ enum CommandOpts {
command: Test,
},
#[structopt(about = "Import a package from the Aleo Package Manager")]
Add {
#[structopt(flatten)]
command: Add,
},
#[structopt(about = "Install dependencies for this program")]
// #[structopt(about = "Import a package from the Aleo Package Manager")]
// Add {
// #[structopt(flatten)]
// command: Add,
// },
#[structopt(about = "Pull dependencies from Aleo Package Manager")]
Fetch {
#[structopt(flatten)]
command: Fetch,
@ -167,12 +166,11 @@ enum CommandOpts {
command: Publish,
},
#[structopt(about = "Uninstall a package from the current package")]
Remove {
#[structopt(flatten)]
command: Remove,
},
// #[structopt(about = "Uninstall a package from the current package")]
// Remove {
// #[structopt(flatten)]
// command: Remove,
// },
#[structopt(about = "Lints the Leo files in the package (*)")]
Lint {
#[structopt(flatten)]
@ -219,14 +217,13 @@ fn run_with_args(opt: Opt) -> Result<()> {
CommandOpts::Watch { command } => command.try_execute(context),
CommandOpts::Update { command } => command.try_execute(context),
CommandOpts::Add { command } => command.try_execute(context),
// CommandOpts::Add { command } => command.try_execute(context),
CommandOpts::Fetch { command } => command.try_execute(context),
CommandOpts::Clone { command } => command.try_execute(context),
CommandOpts::Login { command } => command.try_execute(context),
CommandOpts::Logout { command } => command.try_execute(context),
CommandOpts::Publish { command } => command.try_execute(context),
CommandOpts::Remove { command } => command.try_execute(context),
// CommandOpts::Remove { command } => command.try_execute(context),
CommandOpts::Lint { command } => command.try_execute(context),
CommandOpts::Deploy { command } => command.try_execute(context),
}
@ -425,6 +422,7 @@ mod cli_tests {
.open(path.join("install/Leo.toml"))
.unwrap();
assert!(run_cmd("leo fetch", install_path).is_ok());
assert!(
file.write_all(
br#"
@ -435,5 +433,6 @@ mod cli_tests {
);
assert!(run_cmd("leo fetch", install_path).is_ok());
assert!(run_cmd("leo build", install_path).is_ok());
}
}

View File

@ -45,3 +45,9 @@ impl From<toml::ser::Error> for LockFileError {
LockFileError::Crate("toml", error.to_string())
}
}
impl From<toml::de::Error> for LockFileError {
fn from(error: toml::de::Error) -> Self {
LockFileError::Crate("toml", error.to_string())
}
}

View File

@ -17,7 +17,14 @@
use crate::{errors::LockFileError, root::Dependency};
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::HashMap, fs::File, io::Write, path::Path};
use std::{
borrow::Cow,
collections::HashMap,
convert::TryFrom,
fs::File,
io::{Read, Write},
path::Path,
};
pub const LOCKFILE_FILENAME: &str = "Leo.lock";
@ -31,10 +38,11 @@ pub struct LockFile {
/// Single dependency record.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Package {
// pub import_name: String,
pub name: String,
pub version: String,
pub author: String,
pub import_name: Option<String>,
#[serde(skip_serializing_if = "HashMap::is_empty", default)]
pub dependencies: HashMap<String, String>,
}
@ -43,6 +51,15 @@ impl LockFile {
LockFile { package: vec![] }
}
/// Check if LockFile exists in a directory.
pub fn exists_at(path: &Path) -> bool {
let mut path = Cow::from(path);
if path.is_dir() {
path.to_mut().push(LOCKFILE_FILENAME);
}
path.exists()
}
/// Add Package record to the lock file. Chainable.
pub fn add_package(&mut self, package: Package) -> &mut Self {
self.package.push(package);
@ -53,6 +70,18 @@ impl LockFile {
Ok(toml::to_string(self)?)
}
pub fn to_hashmap(&self) -> HashMap<String, String> {
let mut result = HashMap::new();
for package in self.package.iter() {
match &package.import_name {
Some(name) => result.insert(name.clone(), package.to_identifier()),
None => result.insert(package.name.clone(), package.to_identifier()),
};
}
result
}
/// Write Leo.lock to the given location.
pub fn write_to(self, path: &Path) -> Result<(), LockFileError> {
let mut path = Cow::from(path);
@ -66,29 +95,53 @@ impl LockFile {
}
}
impl TryFrom<&Path> for LockFile {
type Error = LockFileError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
let mut path = Cow::from(path);
if path.is_dir() {
path.to_mut().push(LOCKFILE_FILENAME);
}
let mut file = File::open(path.clone()).map_err(|error| LockFileError::Opening(LOCKFILE_FILENAME, error))?;
let size = file
.metadata()
.map_err(|error| LockFileError::Metadata(LOCKFILE_FILENAME, error))?
.len() as usize;
let mut buffer = String::with_capacity(size);
file.read_to_string(&mut buffer)
.map_err(|error| LockFileError::Reading(LOCKFILE_FILENAME, error))?;
toml::from_str(&buffer).map_err(|error| LockFileError::Parsing(LOCKFILE_FILENAME, error))
}
}
impl Package {
/// Fill dependencies from Leo Manifest data.
pub fn add_dependencies(&mut self, dependencies: &HashMap<String, Dependency>) {
for (import_name, dependency) in dependencies.iter() {
self.dependencies
.insert(import_name.clone(), dependency.package.clone());
.insert(import_name.clone(), Package::from(dependency).to_identifier());
}
}
/// Form an path identifier for a package. It is the path under which package is stored
/// inside the `imports/` directory.
pub fn to_identifier(&self) -> String {
format!("{}-{}@{}", self.author, self.name, self.version)
}
}
impl From<Dependency> for Package {
fn from(dependency: Dependency) -> Package {
let Dependency {
author,
version,
package,
} = dependency;
impl From<&Dependency> for Package {
fn from(dependency: &Dependency) -> Package {
Package {
name: package,
author,
version,
name: dependency.package.clone(),
author: dependency.author.clone(),
version: dependency.version.clone(),
dependencies: Default::default(),
import_name: None,
}
}
}