use crate::{ast::ModuleKind, format, parser}; use indoc::indoc; use pretty_assertions::assert_eq; fn assert_fmt(src: &str, expected: &str) { let (module, extra) = parser::module(src, ModuleKind::Lib).unwrap(); let mut out = String::new(); format::pretty(&mut out, module, extra, src); // Output is what we expect assert_eq!(out, expected); // Formatting is idempotent let (module2, extra2) = parser::module(&out, ModuleKind::Lib).unwrap(); let mut out2 = String::new(); format::pretty(&mut out2, module2, extra2, &out); assert_eq!(out, out2, "formatting isn't idempotent"); } #[test] fn comment_at_end_of_file() { let input = indoc! { r#" type Foo = Int //"#}; let output = indoc! { r#" type Foo = Int // "#}; assert_fmt(input, output); } #[test] fn test_format_if() { let src = indoc! {r#" pub fn foo(a) { if a { 14 } else { 42 } } pub fn bar(xs) { list.map(xs, fn (x) { if x > 0 { "foo" } else { "bar" } }) } "#}; let expected = indoc! {r#" pub fn foo(a) { if a { 14 } else { 42 } } pub fn bar(xs) { list.map( xs, fn(x) { if x > 0 { "foo" } else { "bar" } }, ) } "#}; assert_fmt(src, expected) } #[test] fn test_format_validator() { let src = indoc! {r#" validator ( ) { // What is the purpose of life fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } } // What? validator { /// Some documentation for foo fn foo() { Void } // I am lost } "#}; let expected = indoc! {r#" validator { // What is the purpose of life fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } } // What? validator { /// Some documentation for foo fn foo() { Void } // I am lost } "#}; assert_fmt(src, expected) } #[test] fn test_format_double_validator() { let src = indoc! {r#" validator ( param1 : ByteArray ) { fn foo (d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } /// This is bar fn bar(r: Redeemer, ctx : ScriptContext ) -> Bool { True } } "#}; let expected = indoc! {r#" validator(param1: ByteArray) { fn foo(d: Datum, r: Redeemer, ctx: ScriptContext) -> Bool { True } /// This is bar fn bar(r: Redeemer, ctx: ScriptContext) -> Bool { True } } "#}; assert_fmt(src, expected) } #[test] fn test_format_when() { let src = indoc! {r#" pub fn foo( a) { when a is{ True -> { bar() 14 } False -> 42 } } "#}; let expected = indoc! {r#" pub fn foo(a) { when a is { True -> { bar() 14 } False -> 42 } } "#}; assert_fmt(src, expected) } #[test] fn test_format_nested_if() { let src = indoc! {r#" pub fn foo(n) { if n > 0 { if n > 1 { if n > 2 { "foo" } else { "foo" } } else { "bar" } } else { if n < -1 { "baz" } else { "biz" } } } "#}; assert_fmt(src, src) } #[test] fn test_format_nested_when_if() { let src = indoc! {r#" pub fn drop(xs: List, n: Int) -> List { if n <= 0 { xs } else { when xs is { [] -> [] [_x, ..rest] -> drop(rest, n - 1) } } } "#}; let expected = indoc! {r#" pub fn drop(xs: List, n: Int) -> List { if n <= 0 { xs } else { when xs is { [] -> [] [_x, ..rest] -> drop(rest, n - 1) } } } "#}; assert_fmt(src, expected) } #[test] fn test_format_nested_when() { let src = indoc! {r#" fn foo() { when a is { None -> "foo" Some(b) -> when b is { None -> "foo" Some(c) -> when c is { None -> "foo" Some(_) -> "foo" } } } } "#}; let expected = indoc! {r#" fn foo() { when a is { None -> "foo" Some(b) -> when b is { None -> "foo" Some(c) -> when c is { None -> "foo" Some(_) -> "foo" } } } } "#}; assert_fmt(src, expected) } #[test] fn test_format_else_if() { let src = indoc! {r#" pub fn foo(xs: List, n: Int) -> List { if n <= 0 { xs } else if n <= 10 { xs } else { xs } } "#}; assert_fmt(src, src) } #[test] fn test_format_imports() { let src = indoc! {r#" use aiken/list // foo use aiken/bytearray use aiken/transaction/certificate // bar use aiken/transaction use aiken/transaction/value "#}; let expected = indoc! {r#" // foo use aiken/bytearray use aiken/list // bar use aiken/transaction use aiken/transaction/certificate use aiken/transaction/value "#}; assert_fmt(src, expected) } #[test] fn test_negate() { let src = indoc! {r#" fn foo() { - 42 } "#}; let expected = indoc! {r#" fn foo() { -42 } "#}; assert_fmt(src, expected) } #[test] fn test_block_arithmetic_expr() { let src = indoc! {r#" fn foo() { ( 14 + 42 ) * 1337 } fn bar() { { 14 + 42 } * 1337 } "#}; let expected = indoc! {r#" fn foo() { ( 14 + 42 ) * 1337 } fn bar() { ( 14 + 42 ) * 1337 } "#}; assert_fmt(src, expected); } #[test] fn test_block_logical_expr() { let src = indoc! {r#" fn foo() { !(a && b) } fn bar() { (a || b) && (c || d) } fn baz() { a || (b && c) || d } "#}; let expected = indoc! {r#" fn foo() { !(a && b) } fn bar() { ( a || b ) && ( c || d ) } fn baz() { a || b && c || d } "#}; assert_fmt(src, expected); } #[test] fn test_nested_function_calls() { let src = indoc! {r#" fn foo(output) { [ output.address.stake_credential == Some( Inline( VerificationKeyCredential( #"66666666666666666666666666666666666666666666666666666666", )) ) , when output.datum is { InlineDatum(_) -> True _ -> error "expected inline datum" }, ] |> list.and } "#}; let expected = indoc! {r#" fn foo(output) { [ output.address.stake_credential == Some( Inline( VerificationKeyCredential( #"66666666666666666666666666666666666666666666666666666666", ), ), ), when output.datum is { InlineDatum(_) -> True _ -> error @"expected inline datum" }, ] |> list.and } "#}; assert_fmt(src, expected); } #[test] fn format_trace_todo_error() { let src = indoc! {r#" fn foo_1() { todo } fn foo_2() { todo "my custom message" } fn foo_3() { when x is { Foo -> True _ -> error } } fn foo_4() { if 14 == 42 { error "I don't think so" } else { trace "been there" True } } "#}; let out = indoc! {r#" fn foo_1() { todo } fn foo_2() { todo @"my custom message" } fn foo_3() { when x is { Foo -> True _ -> error } } fn foo_4() { if 14 == 42 { error @"I don't think so" } else { trace @"been there" True } } "#}; assert_fmt(src, out); } #[test] fn test_trace_if_false() { let src = indoc! {r#" fn foo() { my_expression? } fn bar() { (True && False)? || foo()? } "#}; assert_fmt(src, src); } #[test] fn test_newline_comments() { let src = indoc! {r#" // My comment // // has a newline. fn foo() { True } // My comments // can live apart fn bar() { True } "#}; assert_fmt(src, src); } #[test] fn test_newline_doc_comments() { let src = indoc! {r#" /// My doc comment /// /// has a newline. fn foo() { True } /// My doc comments /// cannot be separated fn bar() { True } "#}; let out = indoc! {r#" /// My doc comment /// /// has a newline. fn foo() { True } /// My doc comments /// cannot be separated fn bar() { True } "#}; assert_fmt(src, out); } #[test] fn test_newline_module_comments() { let src = indoc! {r#" //// My module comment //// //// has a newline. fn foo() { True } //// My module comments //// cannot be separated "#}; let out = indoc! {r#" //// My module comment //// //// has a newline. //// My module comments //// cannot be separated fn foo() { True } "#}; assert_fmt(src, out); } #[test] fn test_bytearray_literals() { let src = indoc! {r#" const foo_const_array = #[102, 111, 111] const foo_const_hex = #"666f6f" const foo_const_utf8 = "foo" fn foo() { let foo_const_array = #[102, 111, 111] let foo_const_hex = #"666f6f" let foo_const_utf8 = "foo" } "#}; assert_fmt(src, src); } #[test] fn test_string_literal() { let src = indoc! {r#" const foo_const: String = @"foo" fn foo() { let foo_var: String = @"foo" } "#}; assert_fmt(src, src); } #[test] fn test_unicode() { let src = indoc! {r#" /// ∞ ★ ♩ ♫ ✓ fn foo() { trace @"∀💩" Void } "#}; assert_fmt(src, src); } #[test] fn test_preserve_pipe() { let src = indoc! { r#" fn foo() { a |> b |> c |> d } fn foo() { a // Foo |> b// Some comments |> c |> d } fn baz() { // Commented however |> it_automatically_breaks |> into_multiple_lines |> anytime_when |> it_is_too_long // What? } "#}; let expected = indoc! { r#" fn foo() { a |> b |> c |> d } fn foo() { a // Foo |> b // Some comments |> c |> d } fn baz() { // Commented however |> it_automatically_breaks |> into_multiple_lines |> anytime_when |> it_is_too_long // What? } "#}; assert_fmt(src, expected); } #[test] fn weird_comments() { let src = indoc! { r#" // A /// B /// C fn foo() { todo } // E /// F // G fn bar() { todo } "#}; let expected = indoc! { r#" // A /// B /// C fn foo() { todo } // E // G /// F fn bar() { todo } "#}; assert_fmt(src, expected); } #[test] fn format_trace_callback() { let src = indoc! { r#" fn foo() { list.any([], fn (e) { trace @"foo" e }) } "#}; let expected = indoc! { r#" fn foo() { list.any( [], fn(e) { trace @"foo" e }, ) } "#}; assert_fmt(src, expected); } #[test] fn format_pipe_fn() { let src = indoc! { r#" fn foo() { outputs |> list.any( fn(output) { value.quantity_of(output.value, policy_id, asset_name) == 1 }, ) } "#}; let expected = indoc! { r#" fn foo() { outputs |> list.any( fn(output) { value.quantity_of(output.value, policy_id, asset_name) == 1 }, ) } "#}; assert_fmt(src, expected); } #[test] fn match_record() { let src = indoc! { r#" fn foo() { when bar is { Bar { a, b, c } -> Void Bar { a, .. } -> Void Bar { .. } -> Void Bar(a, b, c) -> Void Bar(..) -> Void } } "#}; assert_fmt(src, src); } #[test] fn test_fail() { let src = indoc! { r#" !test foo() { expect Some(a) = bar a } "#}; assert_fmt(src, src); } #[test] fn pipes_and_expressions() { let src = indoc! {r#" test fmt() { (x == y) && ((z |> length()) == x) } "#}; let expected = indoc! {r#" test fmt() { x == y && ( z |> length() ) == x } "#}; assert_fmt(src, expected); } #[test] fn hex_and_numeric_underscore() { let src = indoc! {r#" fn foo() { let a = 1_000_000 + 1_423 + 10393841 let b = 0xa4 - 0xcd let c = #[0xfd, 0x12, 0x00, 0x1b, 0x0a, 0x90] let d = -100_000 } "#}; assert_fmt(src, src); }