Preserve type-aliases during blueprint generation.
This commit is contained in:
parent
007b85b864
commit
f60df16bc2
|
@ -26,6 +26,8 @@
|
|||
|
||||
- **aiken-project**: modules starting with `@hidden` in their docs will be skipped from docs generation. @KtorZ
|
||||
|
||||
- **aiken-project**: preserve type-aliases as titles in blueprint generated schemas. @KtorZ
|
||||
|
||||
- **uplc**: support evaluation of Plutus V3 transactions, including new purposes introduced in Conway. @KtorZ
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -214,7 +214,7 @@ fn qualify_type_name(module: &String, typ_name: &str) -> Document<'static> {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_alias(
|
||||
pub fn resolve_alias(
|
||||
parameters: &[String],
|
||||
annotation: &Annotation,
|
||||
typ: &Type,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use aiken_lang::tipo::{Type, TypeVar};
|
||||
use aiken_lang::tipo::{pretty::resolve_alias, Type, TypeAliasAnnotation, TypeVar};
|
||||
use itertools::Itertools;
|
||||
use serde::{
|
||||
self,
|
||||
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
|
||||
|
@ -125,6 +126,22 @@ impl Display for Reference {
|
|||
|
||||
impl Reference {
|
||||
pub fn from_type(type_info: &Type, type_parameters: &HashMap<u64, Rc<Type>>) -> Self {
|
||||
if let Some(TypeAliasAnnotation {
|
||||
alias,
|
||||
parameters,
|
||||
annotation,
|
||||
}) = type_info.alias().as_deref()
|
||||
{
|
||||
if let Some(resolved_parameters) = resolve_alias(parameters, annotation, type_info) {
|
||||
return Self::from_type_alias(
|
||||
type_info,
|
||||
alias.to_string(),
|
||||
resolved_parameters,
|
||||
type_parameters,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match type_info {
|
||||
Type::App {
|
||||
module, name, args, ..
|
||||
|
@ -190,6 +207,41 @@ impl Reference {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_type_alias(
|
||||
type_info: &Type,
|
||||
alias: String,
|
||||
parameters: Vec<Rc<Type>>,
|
||||
type_parameters: &HashMap<u64, Rc<Type>>,
|
||||
) -> Self {
|
||||
if !parameters.is_empty() {
|
||||
Reference {
|
||||
inner: format!(
|
||||
"{alias}${}",
|
||||
parameters
|
||||
.iter()
|
||||
.map(|param| {
|
||||
// Avoid infinite recursion for recursive types instantiated to
|
||||
// themselves. For example: type Identity<t> = t
|
||||
if param.as_ref() == type_info {
|
||||
Self::from_type(
|
||||
type_info.clone().set_alias(None).as_ref(),
|
||||
type_parameters,
|
||||
)
|
||||
.inner
|
||||
} else {
|
||||
Self::from_type(param, type_parameters).inner
|
||||
}
|
||||
})
|
||||
.join("_"),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
Reference {
|
||||
inner: alias.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Reference {
|
||||
|
|
|
@ -172,6 +172,24 @@ impl Annotated<Schema> {
|
|||
type_parameters: &mut HashMap<u64, Rc<Type>>,
|
||||
definitions: &mut Definitions<Self>,
|
||||
) -> Result<Reference, Error> {
|
||||
let title = if type_info.alias().is_some() {
|
||||
Some(type_info.to_pretty(0))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
fn with_title(title: Option<&String>, annotated: Schema) -> Annotated<Schema> {
|
||||
if title.is_some() {
|
||||
Annotated {
|
||||
title: title.cloned(),
|
||||
description: None,
|
||||
annotated,
|
||||
}
|
||||
} else {
|
||||
annotated.into()
|
||||
}
|
||||
}
|
||||
|
||||
match type_info {
|
||||
Type::App {
|
||||
module: module_name,
|
||||
|
@ -182,20 +200,20 @@ impl Annotated<Schema> {
|
|||
definitions.register(type_info, &type_parameters.clone(), |definitions| {
|
||||
match &type_name[..] {
|
||||
"Data" => Ok(Annotated {
|
||||
title: Some("Data".to_string()),
|
||||
title: title.or(Some("Data".to_string())),
|
||||
description: Some("Any Plutus data.".to_string()),
|
||||
annotated: Schema::Data(Data::Opaque),
|
||||
}),
|
||||
|
||||
"ByteArray" => Ok(Schema::Data(Data::Bytes).into()),
|
||||
"ByteArray" => Ok(with_title(title.as_ref(), Schema::Data(Data::Bytes))),
|
||||
|
||||
"Int" => Ok(Schema::Data(Data::Integer).into()),
|
||||
"Int" => Ok(with_title(title.as_ref(), Schema::Data(Data::Integer))),
|
||||
|
||||
"String" => Ok(Schema::String.into()),
|
||||
"String" => Ok(with_title(title.as_ref(), Schema::String)),
|
||||
|
||||
"Void" => Ok(Annotated {
|
||||
title: Some("Unit".to_string()),
|
||||
description: Some("The nullary constructor.".to_string()),
|
||||
title: title.or(Some("Unit".to_string())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::AnyOf(vec![Annotated {
|
||||
title: None,
|
||||
description: None,
|
||||
|
@ -207,7 +225,7 @@ impl Annotated<Schema> {
|
|||
}),
|
||||
|
||||
"Bool" => Ok(Annotated {
|
||||
title: Some("Bool".to_string()),
|
||||
title: title.or(Some("Bool".to_string())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::AnyOf(vec![
|
||||
Annotated {
|
||||
|
@ -230,7 +248,7 @@ impl Annotated<Schema> {
|
|||
}),
|
||||
|
||||
"Ordering" => Ok(Annotated {
|
||||
title: Some("Ordering".to_string()),
|
||||
title: title.or(Some("Ordering".to_string())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::AnyOf(vec![
|
||||
Annotated {
|
||||
|
@ -260,6 +278,23 @@ impl Annotated<Schema> {
|
|||
])),
|
||||
}),
|
||||
|
||||
"Never" => {
|
||||
Ok(Annotated {
|
||||
title: title.or(Some("Never".to_string())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::AnyOf(vec![
|
||||
Annotated {
|
||||
title: Some("Never".to_string()),
|
||||
description: Some("Nothing.".to_string()),
|
||||
annotated: Constructor {
|
||||
index: 1,
|
||||
fields: vec![],
|
||||
},
|
||||
},
|
||||
])),
|
||||
})
|
||||
}
|
||||
|
||||
"Option" => {
|
||||
let generic = Annotated::do_from_type(
|
||||
args.first()
|
||||
|
@ -270,7 +305,7 @@ impl Annotated<Schema> {
|
|||
)?;
|
||||
|
||||
Ok(Annotated {
|
||||
title: Some("Optional".to_string()),
|
||||
title: title.or(Some("Option".to_string())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::AnyOf(vec![
|
||||
Annotated {
|
||||
|
@ -329,7 +364,7 @@ impl Annotated<Schema> {
|
|||
_ => Data::List(Items::One(Declaration::Referenced(generic))),
|
||||
};
|
||||
|
||||
Ok(Schema::Data(data).into())
|
||||
Ok(with_title(title.as_ref(), Schema::Data(data)))
|
||||
}
|
||||
|
||||
_ => Err(Error::new(ErrorContext::UnsupportedType, type_info)),
|
||||
|
@ -360,7 +395,7 @@ impl Annotated<Schema> {
|
|||
);
|
||||
|
||||
Ok(Annotated {
|
||||
title: Some(data_type.name.clone()),
|
||||
title: title.or(Some(data_type.name.clone())),
|
||||
description: data_type.doc.clone().map(|s| s.trim().to_string()),
|
||||
annotated,
|
||||
})
|
||||
|
@ -377,7 +412,7 @@ impl Annotated<Schema> {
|
|||
.map_err(|e| e.backtrack(type_info))?;
|
||||
|
||||
Ok(Annotated {
|
||||
title: Some("Pair".to_owned()),
|
||||
title: title.or(Some("Pair".to_owned())),
|
||||
description: None,
|
||||
annotated: Schema::Pair(left, right),
|
||||
})
|
||||
|
@ -396,7 +431,7 @@ impl Annotated<Schema> {
|
|||
.map_err(|e| e.backtrack(type_info))?;
|
||||
|
||||
Ok(Annotated {
|
||||
title: Some("Tuple".to_owned()),
|
||||
title: title.or(Some("Tuple".to_owned())),
|
||||
description: None,
|
||||
annotated: Schema::Data(Data::List(Items::Many(elems))),
|
||||
})
|
||||
|
|
|
@ -20,17 +20,22 @@ description: "Code:\n\n/// On-chain state\npub type State {\n /// The contest
|
|||
"hash": "<redacted>",
|
||||
"definitions": {
|
||||
"ByteArray": {
|
||||
"title": "ByteArray",
|
||||
"dataType": "bytes"
|
||||
},
|
||||
"Int": {
|
||||
"dataType": "integer"
|
||||
},
|
||||
"List$ByteArray": {
|
||||
"List$Party": {
|
||||
"dataType": "list",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ByteArray"
|
||||
"$ref": "#/definitions/Party"
|
||||
}
|
||||
},
|
||||
"Party": {
|
||||
"title": "Party",
|
||||
"dataType": "bytes"
|
||||
},
|
||||
"test_module/ContestationPeriod": {
|
||||
"title": "ContestationPeriod",
|
||||
"description": "Whatever",
|
||||
|
@ -89,7 +94,7 @@ description: "Code:\n\n/// On-chain state\npub type State {\n /// The contest
|
|||
{
|
||||
"title": "parties",
|
||||
"description": "List of public key hashes of all participants",
|
||||
"$ref": "#/definitions/List$ByteArray"
|
||||
"$ref": "#/definitions/List$Party"
|
||||
},
|
||||
{
|
||||
"title": "utxoHash",
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
---
|
||||
source: crates/aiken-project/src/blueprint/validator.rs
|
||||
description: "Code:\n\npub type Asset = (ByteArray, Int)\n\npub type POSIXTime = Int\n\npub type AlwaysNone = Never\n\npub type MyDatum {\n Either(AlwaysNone)\n OrElse(Pair<POSIXTime, Bool>)\n}\n\npub type MyRedeemer<a> {\n fst_field: List<a>,\n snd_field: Pairs<a, POSIXTime>\n}\n\nvalidator recursive_types {\n spend(datum: Option<MyDatum>, redeemer: MyRedeemer<Asset>, output_reference: Data, transaction: Data) {\n True\n }\n}\n"
|
||||
---
|
||||
{
|
||||
"title": "test_module.recursive_types.spend",
|
||||
"datum": {
|
||||
"title": "datum",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/test_module~1MyDatum"
|
||||
}
|
||||
},
|
||||
"redeemer": {
|
||||
"title": "redeemer",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/test_module~1MyRedeemer$Asset"
|
||||
}
|
||||
},
|
||||
"compiledCode": "<redacted>",
|
||||
"hash": "<redacted>",
|
||||
"definitions": {
|
||||
"AlwaysNone": {
|
||||
"title": "AlwaysNone",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "Never",
|
||||
"description": "Nothing.",
|
||||
"dataType": "constructor",
|
||||
"index": 1,
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"Asset": {
|
||||
"title": "Asset",
|
||||
"dataType": "list",
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/ByteArray"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/Int"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Bool": {
|
||||
"title": "Bool",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "False",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"title": "True",
|
||||
"dataType": "constructor",
|
||||
"index": 1,
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"ByteArray": {
|
||||
"dataType": "bytes"
|
||||
},
|
||||
"Int": {
|
||||
"dataType": "integer"
|
||||
},
|
||||
"List$Asset": {
|
||||
"dataType": "list",
|
||||
"items": {
|
||||
"$ref": "#/definitions/Asset"
|
||||
}
|
||||
},
|
||||
"POSIXTime": {
|
||||
"title": "POSIXTime",
|
||||
"dataType": "integer"
|
||||
},
|
||||
"Pair$POSIXTime_Bool": {
|
||||
"title": "Pair",
|
||||
"dataType": "#pair",
|
||||
"left": {
|
||||
"$ref": "#/definitions/POSIXTime"
|
||||
},
|
||||
"right": {
|
||||
"$ref": "#/definitions/Bool"
|
||||
}
|
||||
},
|
||||
"Pairs$Asset_POSIXTime": {
|
||||
"title": "Pairs<a, POSIXTime>",
|
||||
"dataType": "map",
|
||||
"keys": {
|
||||
"$ref": "#/definitions/Asset"
|
||||
},
|
||||
"values": {
|
||||
"$ref": "#/definitions/POSIXTime"
|
||||
}
|
||||
},
|
||||
"test_module/MyDatum": {
|
||||
"title": "MyDatum",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "Either",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": [
|
||||
{
|
||||
"$ref": "#/definitions/AlwaysNone"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "OrElse",
|
||||
"dataType": "constructor",
|
||||
"index": 1,
|
||||
"fields": [
|
||||
{
|
||||
"$ref": "#/definitions/Pair$POSIXTime_Bool"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"test_module/MyRedeemer$Asset": {
|
||||
"title": "MyRedeemer",
|
||||
"anyOf": [
|
||||
{
|
||||
"title": "MyRedeemer",
|
||||
"dataType": "constructor",
|
||||
"index": 0,
|
||||
"fields": [
|
||||
{
|
||||
"title": "fst_field",
|
||||
"$ref": "#/definitions/List$Asset"
|
||||
},
|
||||
{
|
||||
"title": "snd_field",
|
||||
"$ref": "#/definitions/Pairs$Asset_POSIXTime"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -695,6 +695,35 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn type_aliases() {
|
||||
assert_validator!(
|
||||
r#"
|
||||
pub type Asset = (ByteArray, Int)
|
||||
|
||||
pub type POSIXTime = Int
|
||||
|
||||
pub type AlwaysNone = Never
|
||||
|
||||
pub type MyDatum {
|
||||
Either(AlwaysNone)
|
||||
OrElse(Pair<POSIXTime, Bool>)
|
||||
}
|
||||
|
||||
pub type MyRedeemer<a> {
|
||||
fst_field: List<a>,
|
||||
snd_field: Pairs<a, POSIXTime>
|
||||
}
|
||||
|
||||
validator recursive_types {
|
||||
spend(datum: Option<MyDatum>, redeemer: MyRedeemer<Asset>, output_reference: Data, transaction: Data) {
|
||||
True
|
||||
}
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_arguments_integer() {
|
||||
let definitions = fixture_definitions();
|
||||
|
|
Loading…
Reference in New Issue