diff --git a/crates/aiken-project/src/blueprint/definitions.rs b/crates/aiken-project/src/blueprint/definitions.rs index 7815556d..97b0cffa 100644 --- a/crates/aiken-project/src/blueprint/definitions.rs +++ b/crates/aiken-project/src/blueprint/definitions.rs @@ -1,11 +1,11 @@ use crate::{ + Annotated, Schema, blueprint::{ parameter::Parameter, schema::{Data, Declaration, Items}, }, - Annotated, Schema, }; -use aiken_lang::tipo::{pretty::resolve_alias, Type, TypeAliasAnnotation, TypeVar}; +use aiken_lang::tipo::{Type, TypeAliasAnnotation, TypeVar, pretty::resolve_alias}; use itertools::Itertools; use serde::{ self, @@ -103,7 +103,7 @@ impl Definitions> { /// Initially, we would clean those Pair definitions right-away, but this would cause /// Pair definitions to be missing in some legit cases when the Pair is also used as a /// standalone type. - pub fn prune_orphan_pairs(&mut self, parameters: Vec<&Parameter>) { + pub fn prune_orphan_pairs(&mut self, parameters: Vec<&Parameter>) -> &mut Self { fn traverse_schema( src: Reference, schema: &Schema, @@ -238,6 +238,72 @@ impl Definitions> { last_len = usage.len(); } } + + self + } + + pub fn replace_pairs_with_data_lists(&mut self) { + fn swap_declaration(declaration: &mut Declaration) -> Declaration { + match std::mem::replace(declaration, Declaration::Inline(Schema::Unit.into())) { + Declaration::Referenced(reference) => Declaration::Referenced(reference), + Declaration::Inline(mut inline) => { + schema_to_data(&mut inline); + if let Schema::Data(data) = *inline { + Declaration::Inline(data.into()) + } else { + unreachable!() + } + } + } + } + + fn schema_to_data(schema: &mut Schema) { + let items = match schema { + Schema::Data(_) => None, + Schema::Pair(ref mut left, ref mut right) => { + let left = swap_declaration(left); + let right = swap_declaration(right); + Some(Items::Many(vec![left, right])) + } + Schema::List(Items::One(ref mut item)) => { + let item = swap_declaration(item); + Some(Items::One(item)) + } + Schema::List(Items::Many(ref mut items)) => Some(Items::Many( + items.iter_mut().map(swap_declaration).collect(), + )), + Schema::Integer => { + *schema = Schema::Data(Data::Integer); + None + } + Schema::Bytes => { + *schema = Schema::Data(Data::Bytes); + None + } + Schema::String => { + *schema = Schema::Data(Data::Bytes); + None + } + Schema::Unit => { + *schema = Schema::void(); + None + } + Schema::Boolean => { + *schema = Schema::bool(); + None + } + }; + + if let Some(items) = items { + *schema = Schema::Data(Data::List(items)); + } + } + + for (_, entry) in self.inner.iter_mut() { + if let Some(ref mut annotated) = entry { + schema_to_data(&mut annotated.annotated); + } + } } } diff --git a/crates/aiken-project/src/blueprint/parameter.rs b/crates/aiken-project/src/blueprint/parameter.rs index 340eea96..9618c7df 100644 --- a/crates/aiken-project/src/blueprint/parameter.rs +++ b/crates/aiken-project/src/blueprint/parameter.rs @@ -5,8 +5,8 @@ use super::{ }; use std::{iter, ops::Deref}; use uplc::{ - ast::{Constant, Data as UplcData}, PlutusData, + ast::{Constant, Data as UplcData}, }; #[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)] diff --git a/crates/aiken-project/src/blueprint/schema.rs b/crates/aiken-project/src/blueprint/schema.rs index 52b1b521..c5014e60 100644 --- a/crates/aiken-project/src/blueprint/schema.rs +++ b/crates/aiken-project/src/blueprint/schema.rs @@ -97,6 +97,48 @@ pub enum Schema { Data(Data), } +impl Schema { + pub fn void() -> Self { + Schema::Data(Data::AnyOf(vec![Annotated { + title: None, + description: None, + annotated: Constructor { + index: 0, + fields: vec![], + }, + }])) + } + + pub fn int() -> Self { + Schema::Data(Data::Integer) + } + + pub fn bytes() -> Self { + Schema::Data(Data::Bytes) + } + + pub fn bool() -> Self { + Schema::Data(Data::AnyOf(vec![ + Annotated { + title: Some("False".to_string()), + description: None, + annotated: Constructor { + index: 0, + fields: vec![], + }, + }, + Annotated { + title: Some("True".to_string()), + description: None, + annotated: Constructor { + index: 1, + fields: vec![], + }, + }, + ])) + } +} + /// A schema for Plutus' Data. #[derive(Debug, PartialEq, Eq, Clone)] pub enum Data { @@ -205,46 +247,22 @@ impl Annotated { annotated: Schema::Data(Data::Opaque), }), - "ByteArray" => Ok(with_title(title.as_ref(), Schema::Data(Data::Bytes))), + "ByteArray" => Ok(with_title(title.as_ref(), Schema::bytes())), - "Int" => Ok(with_title(title.as_ref(), Schema::Data(Data::Integer))), + "Int" => Ok(with_title(title.as_ref(), Schema::int())), "String" => Ok(with_title(title.as_ref(), Schema::String)), "Void" => Ok(Annotated { title: title.or(Some("Unit".to_string())), description: None, - annotated: Schema::Data(Data::AnyOf(vec![Annotated { - title: None, - description: None, - annotated: Constructor { - index: 0, - fields: vec![], - }, - }])), + annotated: Schema::void(), }), "Bool" => Ok(Annotated { title: title.or(Some("Bool".to_string())), description: None, - annotated: Schema::Data(Data::AnyOf(vec![ - Annotated { - title: Some("False".to_string()), - description: None, - annotated: Constructor { - index: 0, - fields: vec![], - }, - }, - Annotated { - title: Some("True".to_string()), - description: None, - annotated: Constructor { - index: 1, - fields: vec![], - }, - }, - ])), + annotated: Schema::bool(), }), "Ordering" => Ok(Annotated { @@ -1099,7 +1117,7 @@ Here's the types I followed and that led me to this problem: pub mod tests { use super::*; use proptest::prelude::*; - use serde_json::{self, json, Value}; + use serde_json::{self, Value, json}; pub fn assert_json(schema: &impl Serialize, expected: Value) { assert_eq!(serde_json::to_value(schema).unwrap(), expected); diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_of_lists.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_of_lists.snap new file mode 100644 index 00000000..6f492bd9 --- /dev/null +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_of_lists.snap @@ -0,0 +1,65 @@ +--- +source: crates/aiken-project/src/blueprint/validator.rs +description: "Code:\n\npub type MyPair =\n Pair, Bool>\n\nvalidator placeholder {\n spend(_datum: Option, _redeemer: MyPair, _utxo: Data, _self: Data,) {\n True\n }\n}\n" +--- +{ + "title": "test_module.placeholder.spend", + "datum": { + "title": "_datum", + "schema": { + "$ref": "#/definitions/Data" + } + }, + "redeemer": { + "title": "_redeemer", + "schema": { + "$ref": "#/definitions/MyPair" + } + }, + "compiledCode": "", + "hash": "", + "definitions": { + "Bool": { + "title": "Bool", + "anyOf": [ + { + "title": "False", + "dataType": "constructor", + "index": 0, + "fields": [] + }, + { + "title": "True", + "dataType": "constructor", + "index": 1, + "fields": [] + } + ] + }, + "Data": { + "title": "Data", + "description": "Any Plutus data." + }, + "Int": { + "dataType": "integer" + }, + "List$Int": { + "dataType": "list", + "items": { + "$ref": "#/definitions/Int" + } + }, + "MyPair": { + "title": "MyPair", + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/List$Int" + }, + { + "$ref": "#/definitions/Bool" + } + ] + } + } +} diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_after_map.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_after_map.snap index 77ad17ab..c1f9ac28 100644 --- a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_after_map.snap +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_after_map.snap @@ -33,13 +33,15 @@ description: "Code:\n\npub type MyPair =\n Pair\n\npub type MyDatum { }, "MyPair": { "title": "MyPair", - "dataType": "#pair", - "left": { - "$ref": "#/definitions/Int" - }, - "right": { - "$ref": "#/definitions/Int" - } + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/Int" + }, + { + "$ref": "#/definitions/Int" + } + ] }, "Void": { "title": "Unit", diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_before_map.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_before_map.snap index 3bb64dbc..1ecdb0d7 100644 --- a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_before_map.snap +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__pair_used_before_map.snap @@ -33,13 +33,15 @@ description: "Code:\n\npub type MyPair =\n Pair\n\npub type MyDatum { }, "MyPair": { "title": "MyPair", - "dataType": "#pair", - "left": { - "$ref": "#/definitions/Int" - }, - "right": { - "$ref": "#/definitions/Int" - } + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/Int" + }, + { + "$ref": "#/definitions/Int" + } + ] }, "Void": { "title": "Unit", diff --git a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__type_aliases.snap b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__type_aliases.snap index 16b93cc6..25351dd4 100644 --- a/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__type_aliases.snap +++ b/crates/aiken-project/src/blueprint/snapshots/aiken_project__blueprint__validator__tests__type_aliases.snap @@ -78,13 +78,15 @@ description: "Code:\n\npub type Asset = (ByteArray, Int)\n\npub type POSIXTime = }, "Pair$POSIXTime_Bool": { "title": "Pair", - "dataType": "#pair", - "left": { - "$ref": "#/definitions/POSIXTime" - }, - "right": { - "$ref": "#/definitions/Bool" - } + "dataType": "list", + "items": [ + { + "$ref": "#/definitions/POSIXTime" + }, + { + "$ref": "#/definitions/Bool" + } + ] }, "Pairs$Asset_POSIXTime": { "title": "Pairs", diff --git a/crates/aiken-project/src/blueprint/validator.rs b/crates/aiken-project/src/blueprint/validator.rs index b51665d1..36b476b8 100644 --- a/crates/aiken-project/src/blueprint/validator.rs +++ b/crates/aiken-project/src/blueprint/validator.rs @@ -202,13 +202,15 @@ impl Validator { schema: Declaration::Inline(Box::new(Schema::Data(Data::Opaque))), })); - definitions.prune_orphan_pairs( - parameters - .iter() - .chain(redeemer.as_ref().map(|x| vec![x]).unwrap_or_default()) - .chain(datum.as_ref().map(|x| vec![x]).unwrap_or_default()) - .collect::>(), - ); + definitions + .prune_orphan_pairs( + parameters + .iter() + .chain(redeemer.as_ref().map(|x| vec![x]).unwrap_or_default()) + .chain(datum.as_ref().map(|x| vec![x]).unwrap_or_default()) + .collect::>(), + ) + .replace_pairs_with_data_lists(); Ok(Validator { title: format!("{}.{}.{}", &module.name, &def.name, &func.name,), @@ -805,6 +807,22 @@ mod tests { ); } + #[test] + fn pair_of_lists() { + assert_validator!( + r#" + pub type MyPair = + Pair, Bool> + + validator placeholder { + spend(_datum: Option, _redeemer: MyPair, _utxo: Data, _self: Data,) { + True + } + } + "# + ); + } + #[test] fn else_redeemer() { assert_validator!(