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
	
	 KtorZ
						KtorZ