Add new 'deps add' command

This makes it easier to add new dependencies, without having to
  manually edit the `aiken.toml` file.

  The command is accessible via two different paths:

  - aiken deps add

  or simply

  - aiken add

  for this is quite common to find at the top-level of the command-line,
  and, we still want to keep commands for managing dependencies grouped
  under a command sub-group and not all at the top-level. So we're
  merely promoting that one for visibility.
This commit is contained in:
KtorZ
2023-01-14 23:00:44 +01:00
parent d4f905b1db
commit 38df9a586e
8 changed files with 135 additions and 20 deletions

View File

@@ -2,17 +2,13 @@ use crate::{package_name::PackageName, Error};
use aiken_lang::ast::Span;
use miette::NamedSource;
use serde::{Deserialize, Serialize};
use std::{
fmt::Display,
fs, io,
path::{Path, PathBuf},
};
use std::{fmt::Display, fs, io, path::Path};
#[derive(Deserialize, Serialize)]
pub struct Config {
pub name: PackageName,
pub version: String,
pub license: String,
pub license: Option<String>,
#[serde(default)]
pub description: String,
pub repository: Option<Repository>,
@@ -57,7 +53,7 @@ impl Config {
Config {
name: name.clone(),
version: "0.0.0".to_string(),
license: "Apache-2.0".to_string(),
license: Some("Apache-2.0".to_string()),
description: format!("Aiken contracts for project '{name}'"),
repository: Some(Repository {
user: name.owner.clone(),
@@ -81,10 +77,11 @@ impl Config {
fs::write(aiken_toml_path, aiken_toml)
}
pub fn load(dir: PathBuf) -> Result<Config, Error> {
pub fn load(dir: &Path) -> Result<Config, Error> {
let config_path = dir.join("aiken.toml");
let raw_config = fs::read_to_string(&config_path)
.map_err(|_| Error::MissingManifest { path: dir.clone() })?;
let raw_config = fs::read_to_string(&config_path).map_err(|_| Error::MissingManifest {
path: dir.to_path_buf(),
})?;
let result: Self = toml::from_str(&raw_config).map_err(|e| Error::TomlLoading {
path: config_path.clone(),
@@ -100,4 +97,19 @@ impl Config {
Ok(result)
}
pub fn insert(mut self, dependency: &Dependency, and_replace: bool) -> Option<Self> {
for mut existing in self.dependencies.iter_mut() {
if existing.name == dependency.name {
return if and_replace {
existing.version = dependency.version.clone();
Some(self)
} else {
None
};
}
}
self.dependencies.push(dependency.clone());
Some(self)
}
}

View File

@@ -1,4 +1,4 @@
use crate::{deps::manifest::Package, pretty, script::EvalHint};
use crate::{deps::manifest::Package, package_name::PackageName, pretty, script::EvalHint};
use aiken_lang::{
ast::{BinOp, Span},
parser::error::ParseError,
@@ -418,6 +418,8 @@ pub enum Warning {
#[source]
warning: tipo::error::Warning,
},
#[error("{name} is already a dependency.")]
DependencyAlreadyExists { name: PackageName },
}
impl Diagnostic for Warning {
@@ -429,6 +431,7 @@ impl Diagnostic for Warning {
match self {
Warning::Type { named, .. } => Some(named),
Warning::NoValidators => None,
Warning::DependencyAlreadyExists { .. } => None,
}
}
@@ -436,6 +439,7 @@ impl Diagnostic for Warning {
match self {
Warning::Type { warning, .. } => warning.labels(),
Warning::NoValidators => None,
Warning::DependencyAlreadyExists { .. } => None,
}
}
@@ -443,6 +447,19 @@ impl Diagnostic for Warning {
match self {
Warning::Type { .. } => Some(Box::new("aiken::check")),
Warning::NoValidators => Some(Box::new("aiken::check")),
Warning::DependencyAlreadyExists { .. } => {
Some(Box::new("aiken::deps::already_exists"))
}
}
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self {
Warning::Type { .. } => None,
Warning::NoValidators => None,
Warning::DependencyAlreadyExists { .. } => Some(Box::new(
"If you need to change the version, try 'aiken packages upgrade' instead.",
)),
}
}
}

View File

@@ -86,7 +86,7 @@ where
module_types.insert("aiken".to_string(), builtins::prelude(&id_gen));
module_types.insert("aiken/builtin".to_string(), builtins::plutus(&id_gen));
let config = Config::load(root.clone())?;
let config = Config::load(&root)?;
Ok(Project {
config,

View File

@@ -13,22 +13,24 @@ pub struct PackageName {
}
impl PackageName {
fn validate(&self) -> Result<(), Error> {
let name = format!("{}/{}", self.owner, self.repo);
pub fn restrict(&self) -> Result<(), Error> {
if self.owner.starts_with("aiken") {
return Err(Error::InvalidProjectName {
reason: InvalidProjectNameReason::Reserved,
name,
name: self.to_string(),
});
}
Ok(())
}
fn validate(&self) -> Result<(), Error> {
let r = regex::Regex::new("^[a-z0-9_-]+$").expect("regex could not be compiled");
if !(r.is_match(&self.owner) && r.is_match(&self.repo)) {
return Err(Error::InvalidProjectName {
reason: InvalidProjectNameReason::Format,
name,
name: self.to_string(),
});
}