Merge pull request #1129 from aiken-lang/blueprint-apply-better-errors

Provide better errors when failing to match validators in blueprint apply.
This commit is contained in:
Matthias Benkort 2025-03-21 10:40:20 +01:00 committed by GitHub
commit 37220241e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 39 deletions

View File

@ -14,12 +14,10 @@
- **aiken-lang**: Change default placeholder for `trace` to `Void` instead of `todo`. @KtorZ
- **aiken-lang**: Disallow (parse error) dangling colon `:` in traces. See [#1113](https://github.com/aiken-lang/aiken/issues/1113). @KtorZ
- **aiken-lang**: Fix `aiken blueprint apply` wrongly overriding all validators handlers names & ABI to the mint's one. See [#1099](https://github.com/aiken-lang/aiken/issues/1099). @KtorZ
### Fixed
- **aiken-lang**: Formatter was removing comments from function type annotation args @rvcas
- **aiken-lang**: Parser wrongly merged two adjacent sequences together, effectively fusioning scopes. @KtorZ
- **aiken-lang**: Fix hint when suggesting to use named fields, wrongly suggesting args in lexicographical order instead of definition order. @KtorZ
- **aiken-project**: Better errors on `blueprint apply` when matching multiple or no validators. See [#1127](https://github.com/aiken-lang/aiken/issues/1127) @KtorZ
## v1.1.13 - 2025-02-26

View File

@ -13,7 +13,7 @@ use aiken_lang::gen_uplc::CodeGenerator;
use definitions::Definitions;
pub use error::Error;
use schema::{Annotated, Schema};
use std::fmt::Debug;
use std::{collections::BTreeSet, fmt::Debug};
use validator::Validator;
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize)]
@ -99,15 +99,7 @@ impl Blueprint {
let mut validator = None;
for v in self.validators.iter() {
let mut split = v.title.split('.');
let known_module_name = split
.next()
.expect("validator's name must have two dot-separated components.");
let known_validator_name = split
.next()
.expect("validator's name must have two dot-separated components.");
let (known_module_name, known_validator_name) = v.get_module_and_name();
let is_target = match (want_module_name, want_validator_name) {
(None, None) => true,
@ -143,8 +135,8 @@ impl Blueprint {
&self,
module_name: Option<&str>,
validator_name: Option<&str>,
when_too_many: fn(Vec<String>) -> E,
when_missing: fn(Vec<String>) -> E,
when_too_many: fn(BTreeSet<(String, String, bool)>) -> E,
when_missing: fn(BTreeSet<(String, String, bool)>) -> E,
action: F,
) -> Result<A, E>
where
@ -153,10 +145,35 @@ impl Blueprint {
match self.lookup(module_name, validator_name) {
Some(LookupResult::One(_, validator)) => action(validator),
Some(LookupResult::Many) => Err(when_too_many(
self.validators.iter().map(|v| v.title.clone()).collect(),
self.validators
.iter()
.filter_map(|v| {
let (l, r) = v.get_module_and_name();
if let Some(module_name) = module_name {
if l != module_name {
return None;
}
}
if let Some(validator_name) = validator_name {
if r != validator_name {
return None;
}
}
Some((l.to_string(), r.to_string(), !v.parameters.is_empty()))
})
.collect(),
)),
None => Err(when_missing(
self.validators.iter().map(|v| v.title.clone()).collect(),
self.validators
.iter()
.map(|v| {
let (l, r) = v.get_module_and_name();
(l.to_string(), r.to_string(), !v.parameters.is_empty())
})
.collect(),
)),
}
}

View File

@ -46,6 +46,20 @@ pub struct Validator {
}
impl Validator {
pub fn get_module_and_name(&self) -> (&str, &str) {
let mut split = self.title.split('.');
let known_module_name = split
.next()
.expect("validator's name must have two dot-separated components.");
let known_validator_name = split
.next()
.expect("validator's name must have two dot-separated components.");
(known_module_name, known_validator_name)
}
pub fn from_checked_module(
modules: &CheckedModules,
generator: &mut CodeGenerator,

View File

@ -15,6 +15,7 @@ use owo_colors::{
Stream::{Stderr, Stdout},
};
use std::{
collections::BTreeSet,
fmt::{self, Debug, Display},
io,
path::{Path, PathBuf},
@ -137,10 +138,14 @@ pub enum Error {
},
#[error("I didn't find any validator matching your criteria.")]
NoValidatorNotFound { known_validators: Vec<String> },
NoValidatorNotFound {
known_validators: BTreeSet<(String, String, bool)>,
},
#[error("I found multiple suitable validators and I need you to tell me which one to pick.")]
MoreThanOneValidatorFound { known_validators: Vec<String> },
MoreThanOneValidatorFound {
known_validators: BTreeSet<(String, String, bool)>,
},
#[error("I couldn't find any exportable function named '{name}' in module '{module}'.")]
ExportNotFound { module: String, name: String },
@ -423,27 +428,13 @@ impl Diagnostic for Error {
None => String::new(),
}
))),
Error::NoValidatorNotFound { known_validators } => Some(Box::new(format!(
"Here's a list of all validators I've found in your project. Please double-check this list against the options that you've provided:\n\n{}",
known_validators
.iter()
.map(|title| format!(
"→ {title}",
title = title.if_supports_color(Stdout, |s| s.purple())
))
.collect::<Vec<String>>()
.join("\n")
Error::NoValidatorNotFound { known_validators } => Some(Box::new(hint_validators(
known_validators,
"Here's a list of all validators I've found in your project.\nPlease double-check this list against the options that you've provided."
))),
Error::MoreThanOneValidatorFound { known_validators } => Some(Box::new(format!(
"Here's a list of all validators I've found in your project. Select one of them using the appropriate options:\n\n{}",
known_validators
.iter()
.map(|title| format!(
"→ {title}",
title = title.if_supports_color(Stdout, |s| s.purple())
))
.collect::<Vec<String>>()
.join("\n")
Error::MoreThanOneValidatorFound { known_validators } => Some(Box::new(hint_validators(
known_validators,
"Here's a list of matching validators I've found in your project.\nPlease narrow the selection using additional options.",
))),
Error::Module(e) => e.help(),
}
@ -809,3 +800,53 @@ fn default_miette_handler(context_lines: usize) -> MietteHandler {
.context_lines(context_lines)
.build()
}
fn hint_validators(known_validators: &BTreeSet<(String, String, bool)>, hint: &str) -> String {
let (pad_module, pad_validator) = known_validators.iter().fold(
(9, 12),
|(module_len, validator_len), (module, validator, _)| {
(
module_len.max(module.len()),
validator_len.max(validator.len()),
)
},
);
format!(
"{hint}\n\n\
{:<pad_module$} {:<pad_validator$}\n\
{:<pad_module$} {:<pad_validator$}\n\
{}\n\nFor convenience, I have highlighted in {bold_green} suitable candidates that {has_params}.",
"module(s)",
"validator(s)",
"",
"",
{
known_validators
.iter()
.map(|(module, validator, has_params)| {
let title = format!(
"{:>pad_module$} . {:<pad_validator$}",
module,
validator,
);
if *has_params {
title
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.green())
.to_string()
} else {
title
}
})
.collect::<Vec<String>>()
.join("\n")
},
bold_green = "bold green"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.green()),
has_params = "can take parameters"
.if_supports_color(Stderr, |s| s.bold())
.if_supports_color(Stderr, |s| s.green()),
)
}