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**: 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
|
- **uplc**: support evaluation of Plutus V3 transactions, including new purposes introduced in Conway. @KtorZ
|
||||||
|
|
||||||
### Changed
|
### 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],
|
parameters: &[String],
|
||||||
annotation: &Annotation,
|
annotation: &Annotation,
|
||||||
typ: &Type,
|
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::{
|
use serde::{
|
||||||
self,
|
self,
|
||||||
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
|
de::{self, Deserialize, Deserializer, MapAccess, Visitor},
|
||||||
|
@ -125,6 +126,22 @@ impl Display for Reference {
|
||||||
|
|
||||||
impl Reference {
|
impl Reference {
|
||||||
pub fn from_type(type_info: &Type, type_parameters: &HashMap<u64, Rc<Type>>) -> Self {
|
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 {
|
match type_info {
|
||||||
Type::App {
|
Type::App {
|
||||||
module, name, args, ..
|
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 {
|
impl Serialize for Reference {
|
||||||
|
|
|
@ -172,6 +172,24 @@ impl Annotated<Schema> {
|
||||||
type_parameters: &mut HashMap<u64, Rc<Type>>,
|
type_parameters: &mut HashMap<u64, Rc<Type>>,
|
||||||
definitions: &mut Definitions<Self>,
|
definitions: &mut Definitions<Self>,
|
||||||
) -> Result<Reference, Error> {
|
) -> 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 {
|
match type_info {
|
||||||
Type::App {
|
Type::App {
|
||||||
module: module_name,
|
module: module_name,
|
||||||
|
@ -182,20 +200,20 @@ impl Annotated<Schema> {
|
||||||
definitions.register(type_info, &type_parameters.clone(), |definitions| {
|
definitions.register(type_info, &type_parameters.clone(), |definitions| {
|
||||||
match &type_name[..] {
|
match &type_name[..] {
|
||||||
"Data" => Ok(Annotated {
|
"Data" => Ok(Annotated {
|
||||||
title: Some("Data".to_string()),
|
title: title.or(Some("Data".to_string())),
|
||||||
description: Some("Any Plutus data.".to_string()),
|
description: Some("Any Plutus data.".to_string()),
|
||||||
annotated: Schema::Data(Data::Opaque),
|
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 {
|
"Void" => Ok(Annotated {
|
||||||
title: Some("Unit".to_string()),
|
title: title.or(Some("Unit".to_string())),
|
||||||
description: Some("The nullary constructor.".to_string()),
|
description: None,
|
||||||
annotated: Schema::Data(Data::AnyOf(vec![Annotated {
|
annotated: Schema::Data(Data::AnyOf(vec![Annotated {
|
||||||
title: None,
|
title: None,
|
||||||
description: None,
|
description: None,
|
||||||
|
@ -207,7 +225,7 @@ impl Annotated<Schema> {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Bool" => Ok(Annotated {
|
"Bool" => Ok(Annotated {
|
||||||
title: Some("Bool".to_string()),
|
title: title.or(Some("Bool".to_string())),
|
||||||
description: None,
|
description: None,
|
||||||
annotated: Schema::Data(Data::AnyOf(vec![
|
annotated: Schema::Data(Data::AnyOf(vec![
|
||||||
Annotated {
|
Annotated {
|
||||||
|
@ -230,7 +248,7 @@ impl Annotated<Schema> {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
"Ordering" => Ok(Annotated {
|
"Ordering" => Ok(Annotated {
|
||||||
title: Some("Ordering".to_string()),
|
title: title.or(Some("Ordering".to_string())),
|
||||||
description: None,
|
description: None,
|
||||||
annotated: Schema::Data(Data::AnyOf(vec![
|
annotated: Schema::Data(Data::AnyOf(vec![
|
||||||
Annotated {
|
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" => {
|
"Option" => {
|
||||||
let generic = Annotated::do_from_type(
|
let generic = Annotated::do_from_type(
|
||||||
args.first()
|
args.first()
|
||||||
|
@ -270,7 +305,7 @@ impl Annotated<Schema> {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Annotated {
|
Ok(Annotated {
|
||||||
title: Some("Optional".to_string()),
|
title: title.or(Some("Option".to_string())),
|
||||||
description: None,
|
description: None,
|
||||||
annotated: Schema::Data(Data::AnyOf(vec![
|
annotated: Schema::Data(Data::AnyOf(vec![
|
||||||
Annotated {
|
Annotated {
|
||||||
|
@ -329,7 +364,7 @@ impl Annotated<Schema> {
|
||||||
_ => Data::List(Items::One(Declaration::Referenced(generic))),
|
_ => 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)),
|
_ => Err(Error::new(ErrorContext::UnsupportedType, type_info)),
|
||||||
|
@ -360,7 +395,7 @@ impl Annotated<Schema> {
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Annotated {
|
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()),
|
description: data_type.doc.clone().map(|s| s.trim().to_string()),
|
||||||
annotated,
|
annotated,
|
||||||
})
|
})
|
||||||
|
@ -377,7 +412,7 @@ impl Annotated<Schema> {
|
||||||
.map_err(|e| e.backtrack(type_info))?;
|
.map_err(|e| e.backtrack(type_info))?;
|
||||||
|
|
||||||
Ok(Annotated {
|
Ok(Annotated {
|
||||||
title: Some("Pair".to_owned()),
|
title: title.or(Some("Pair".to_owned())),
|
||||||
description: None,
|
description: None,
|
||||||
annotated: Schema::Pair(left, right),
|
annotated: Schema::Pair(left, right),
|
||||||
})
|
})
|
||||||
|
@ -396,7 +431,7 @@ impl Annotated<Schema> {
|
||||||
.map_err(|e| e.backtrack(type_info))?;
|
.map_err(|e| e.backtrack(type_info))?;
|
||||||
|
|
||||||
Ok(Annotated {
|
Ok(Annotated {
|
||||||
title: Some("Tuple".to_owned()),
|
title: title.or(Some("Tuple".to_owned())),
|
||||||
description: None,
|
description: None,
|
||||||
annotated: Schema::Data(Data::List(Items::Many(elems))),
|
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>",
|
"hash": "<redacted>",
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"ByteArray": {
|
"ByteArray": {
|
||||||
|
"title": "ByteArray",
|
||||||
"dataType": "bytes"
|
"dataType": "bytes"
|
||||||
},
|
},
|
||||||
"Int": {
|
"Int": {
|
||||||
"dataType": "integer"
|
"dataType": "integer"
|
||||||
},
|
},
|
||||||
"List$ByteArray": {
|
"List$Party": {
|
||||||
"dataType": "list",
|
"dataType": "list",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/ByteArray"
|
"$ref": "#/definitions/Party"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Party": {
|
||||||
|
"title": "Party",
|
||||||
|
"dataType": "bytes"
|
||||||
|
},
|
||||||
"test_module/ContestationPeriod": {
|
"test_module/ContestationPeriod": {
|
||||||
"title": "ContestationPeriod",
|
"title": "ContestationPeriod",
|
||||||
"description": "Whatever",
|
"description": "Whatever",
|
||||||
|
@ -89,7 +94,7 @@ description: "Code:\n\n/// On-chain state\npub type State {\n /// The contest
|
||||||
{
|
{
|
||||||
"title": "parties",
|
"title": "parties",
|
||||||
"description": "List of public key hashes of all participants",
|
"description": "List of public key hashes of all participants",
|
||||||
"$ref": "#/definitions/List$ByteArray"
|
"$ref": "#/definitions/List$Party"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "utxoHash",
|
"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]
|
#[test]
|
||||||
fn validate_arguments_integer() {
|
fn validate_arguments_integer() {
|
||||||
let definitions = fixture_definitions();
|
let definitions = fixture_definitions();
|
||||||
|
|
Loading…
Reference in New Issue