-- in.cue --
import "math"

#Foo: {
	a: int
}

match: {
	[=~"^single"]: matchN(1, [#Foo])
	singleOK: {a: 2}
	singleErr: {
		a: "foo"
	}

	[=~"^incomplete"]: matchN(1, [#Foo])
	incompleteOK: {a: int}
	incompleteErr: {
		a: string
	}

	#A: {
		a: int
		b: _
		...
	}

	defaults: {
		// Because validators distribute over disjunctions, this validator may erase
		// a default value. Nonethenless, it will be guaranteed that the value
		// resulting from evaluation does not violate the validator.
		// TODO(defaults): take this use case into consideration for the defaults
		// rethink, as it seems less than ideal. Note that this typically not an
		// issue if the schema matched against is not concrete.
		[=~"^pickTop"]: matchN(1, [2])
		// TODO(inline): this matches the debug output, but it nonetheless doesn't pass.
		pickTopOK1: *2 | int
		pickTopOK2: int
		pickTopErr: *3 | int

		// Nested default values will be evaluated and may not be overridden by
		// values in the validator.
		[=~"^pickNested1"]: matchN(1, [{a: 2}])
		pickNested1OK1: {a: *2 | int}
		pickNested1OK2: {a: int}
		pickNested1Err: {
			a: *3 | int
		}

		[=~"^pickNested2"]: matchN(1, [{a: <=2}])
		pickNested2OK1: {a: *2 | int}
		pickNested2OK2: {a: int}
		pickNested2Err: {
			a: *3 | int
		}
	}

	// Stress test potential exponential behavior.
	nestedOK: {
		matchN(4, [#A, #A, #A, #A])

		a: 2
		b: {
			matchN(4, [#A, #A, #A, #A])

			a: 3
			b: matchN(4, [#A, #A, #A, #A])
			b: {a: 4}
			c: matchN(4, [#A, #A, #A, #A])
			c: {a: 5}
		}
		c: {
			matchN(4, [#A, #A, #A, #A])

			a: 3
			b: matchN(4, [#A, #A, #A, #A])
			b: {a: 4}
			c: matchN(4, [#A, #A, #A, #A])
			c: {a: 5}
		}
	}
}

not: {
	[=~"^single"]: matchN(0, [#Foo])
	singleOK: {a: "foo"}
	singleErr: {
		a: 2
	}

	[=~"^double"]: matchN(0, [matchN(0, [#Foo])])
	doubleOK: {a: 2}
	doubleErr: {
		a: "foo"
	}
}

oneOf: {
	[=~"^multiple1"]: matchN(1, [math.MultipleOf(3), math.MultipleOf(5)])

	multiple1Err1: 1

	multiple1OK1: 3
	multiple1OK2: 5

	multiple1Err2: 15
}

anyOf: {
	[=~"^multiple1"]: matchN(>0, [math.MultipleOf(3), math.MultipleOf(5)])

	multiple1Err1: 1

	multiple1OK1: 3
	multiple1OK2: 5
	multiple1OK3: 15
}

allOf: {
	[=~"^multiple1"]: matchN(2, [math.MultipleOf(3), math.MultipleOf(5)])

	multiple1Err1: 1
	multiple1Err2: 3
	multiple1Err3: 5

	multiple1OK1: 15
}

bare: {
	embed: t1: {
		a: {matchN(1, [>10])}
		b: {a}
	}
	embed: t2: {
		b: {a}
		a: {matchN(1, [>10])}
	}
	direct: t1: {
		a: matchN(1, [>10])
		b: a
	}
	direct: t2: {
		b: a
		a: matchN(1, [>10])
	}
}

required: {
	ok1: {
		x: matchN(0, [{foo!: string}])
		x: bar: 2
	}
	ok2: {
		x: matchN(0, [{foo!: string}])
	}
	// not matching for different reasons
	ok3: {
		x: matchN(0, [{bar!: string}])
		x: bar: 2
	}
}

// issue3575: Tests that matchN correctly validates structs with extra fields
// whether inside or outside a list argument. https://cuelang.org/issue/3575
// A struct with extra fields should match whether or not inside a list.
// evalv2 would get this wrong but evalv3 got it right.
issue3575: {
	test1: {
		#x: matchN(1, [
			[{}],
		])
		x: #x
		x: [{a: 1}]
	}
	test2: {
		#x: matchN(1, [
			{},
		])
		x: #x
		x: {a: 1}
	}
}
-- incomplete.cue --
import "math"

// The errors in this section should be "incomplete" errors.
incomplete: {
	// foo could still be provided later.
	incomplete1: {
		x: matchN(1, [{foo!: string}])
		x: bar: 2
	}
	// foo could still be provided later.
	incomplete2: {
		x: matchN(1, [{foo!: string}])
	}
	// in case of doubt, we should mark an error as incomplete to be safe.
	incomplete3: {
		x: matchN(math.MultipleOf(2), [{bar!: int}, {foo!: int}])
		x: bar: 2
	}
	incomplete4: {
		x: matchN(math.MultipleOf(2), [{bar!: int}, {foo!: int}, {baz!: int}])
		x: bar: 2
	}
	incomplete5: {
		x: matchN(>1 & <=3, [{bar!: int}, {foo!: int}, {baz!: int}])
		x: bar: 2
	}
	incomplete6: {
		x: matchN(2 | 3, [{bar!: int}, {foo!: int}, {baz!: int}])
		x: bar: 2
	}

	// incorrect type cannot be corrected
	err1: {
		x: matchN(1, [{bar!: string}])
		x: bar: 2
	}
	// additional incomplete matches should not be counted if they can otherwise
	// not satisfy the constraint.
	err2: {
		x: matchN(0, [{bar!: int}, {foo!: int}])
		x: bar: 2
	}
	// additional incomplete matches should not be counted if they can otherwise
	// not satisfy the constraint.
	err3: {
		x: matchN(2, [{bar!: string}, {foo!: string}])
		x: bar: 2
	}
	// one additional potential match cannot make up to satisfy the bound.
	err4: {
		x: matchN(>1, [{bar!: string}, {foo!: string}])
		x: bar: 2
	}
	err5: {
		x: matchN(<1, [{bar!: int}, {foo!: int}])
		x: bar: 2
	}
}
// issue3389: Tests that matchN only reports schema errors of lesser severity,
// not all constraint failures. https://cuelang.org/issue/3389
issue3389: {
	// only report schema errors that are of a lesser severity.
	// TODO(inline): issue converting to tests. Documented later.
	x: bar: 2
	x: matchN(1, [{bar: 3}, {bar: 2, foo!: int}])
}
-- closedness.cue --
ellipsis: ok: {
	out: #Schema & {
		field: shouldBeAllowed: 123
	}
	#Schema: {
		field?: #anything
		#anything: matchN(1, [{...}])
	}
}
// Arguments to function are not closed by enclosing definition.
openArguments: ok1: {
	#Schema: {{
		a: matchN(1, ["all", {foo: "bar"}])
	}}
	out: #Schema & {
		a: baz: "allowed"
	}
}
openArguments: err2: {
	#Schema: {{
		a?: matchN(1, [//fails
			null,               // fail
			{[string]: string}, // pass
			{b: {...}},         // pass
		])
	}}
	out: #Schema & {
		a: allowed: "once"
	}
}
explicitClose: err1: {
	#Schema: {{
		a: matchN(1, ["all", close({foo: "bar"})])
	}}
	out: #Schema & {
		a: baz: "notAllowed"
	}
}
explicitClose: ok2: {
	#Schema: {{
		a?: matchN(1, [
			null,                      // fail
			close({[string]: string}), // pass
			close({b: {...}}),         // fail
		])
	}}
	out: #Schema & {
		a: allowed: "once"
	}
}
mixed: ok3: {
	#Schema: {
		exports?: matchN(1, [
			close({never?: _}), // fail
			#exportsObject,     // pass
		])

		#exports: matchN(1, [string, #exportsObject])
		#exportsObject: exp1?: #exports
	}

	out: #Schema & {
		exports: exp1: "./main-module.js"
	}
}

closedByDefinition: ok4: {
	#Schema: {
		exports?: matchN(1, [string, #exportsObject])
		#exports: matchN(1, [string, #exportsObject])
		#exportsObject: exp1?: #exports
	}

	out: #Schema & {
		exports: exp1: "./main-module.js"
	}
}
closedByDefinition: err5: {
	#Schema: {
		exports?: matchN(1, [null, #exportsObject])
		#exports: matchN(1, [null, #exportsObject])
		#exportsObject: exp1?: #exports
	}

	out: #Schema & {
		exports: exp1: "./main-module.js"
	}
}
-- issue3694.cue --
// issue3694: Tests that matchN combined with close() correctly validates schemas
// requiring one of several required fields. https://cuelang.org/issue/3694
issue3694: full: {
	#step: matchN(1, [{
		uses!: _
		...
	}, {
		run!: _
		...
	}])
	#step: close({
		uses?: string
		run?:  string
	})

	s: #step & {
		run: "echo hello world"
	}
}
issue3694: simple: {
	#step: matchN(1, [{
		uses!: _
	}])
	#step: close({
		uses?: string
	})
}
-- out/compile --
--- closedness.cue
{
  ellipsis: {
    ok: {
      out: (〈0;#Schema〉 & {
        field: {
          shouldBeAllowed: 123
        }
      })
      #Schema: {
        field?: 〈0;#anything〉
        #anything: matchN(1, [
          {
            ...
          },
        ])
      }
    }
  }
  openArguments: {
    ok1: {
      #Schema: {
        {
          a: matchN(1, [
            "all",
            {
              foo: "bar"
            },
          ])
        }
      }
      out: (〈0;#Schema〉 & {
        a: {
          baz: "allowed"
        }
      })
    }
  }
  openArguments: {
    err2: {
      #Schema: {
        {
          a?: matchN(1, [
            null,
            {
              [string]: string
            },
            {
              b: {
                ...
              }
            },
          ])
        }
      }
      out: (〈0;#Schema〉 & {
        a: {
          allowed: "once"
        }
      })
    }
  }
  explicitClose: {
    err1: {
      #Schema: {
        {
          a: matchN(1, [
            "all",
            close({
              foo: "bar"
            }),
          ])
        }
      }
      out: (〈0;#Schema〉 & {
        a: {
          baz: "notAllowed"
        }
      })
    }
  }
  explicitClose: {
    ok2: {
      #Schema: {
        {
          a?: matchN(1, [
            null,
            close({
              [string]: string
            }),
            close({
              b: {
                ...
              }
            }),
          ])
        }
      }
      out: (〈0;#Schema〉 & {
        a: {
          allowed: "once"
        }
      })
    }
  }
  mixed: {
    ok3: {
      #Schema: {
        exports?: matchN(1, [
          close({
            never?: _
          }),
          〈1;#exportsObject〉,
        ])
        #exports: matchN(1, [
          string,
          〈1;#exportsObject〉,
        ])
        #exportsObject: {
          exp1?: 〈1;#exports〉
        }
      }
      out: (〈0;#Schema〉 & {
        exports: {
          exp1: "./main-module.js"
        }
      })
    }
  }
  closedByDefinition: {
    ok4: {
      #Schema: {
        exports?: matchN(1, [
          string,
          〈1;#exportsObject〉,
        ])
        #exports: matchN(1, [
          string,
          〈1;#exportsObject〉,
        ])
        #exportsObject: {
          exp1?: 〈1;#exports〉
        }
      }
      out: (〈0;#Schema〉 & {
        exports: {
          exp1: "./main-module.js"
        }
      })
    }
  }
  closedByDefinition: {
    err5: {
      #Schema: {
        exports?: matchN(1, [
          null,
          〈1;#exportsObject〉,
        ])
        #exports: matchN(1, [
          null,
          〈1;#exportsObject〉,
        ])
        #exportsObject: {
          exp1?: 〈1;#exports〉
        }
      }
      out: (〈0;#Schema〉 & {
        exports: {
          exp1: "./main-module.js"
        }
      })
    }
  }
}
--- in.cue
{
  #Foo: {
    a: int
  }
  match: {
    [=~"^single"]: matchN(1, [
      〈2;#Foo〉,
    ])
    singleOK: {
      a: 2
    }
    singleErr: {
      a: "foo"
    }
    [=~"^incomplete"]: matchN(1, [
      〈2;#Foo〉,
    ])
    incompleteOK: {
      a: int
    }
    incompleteErr: {
      a: string
    }
    #A: {
      a: int
      b: _
      ...
    }
    defaults: {
      [=~"^pickTop"]: matchN(1, [
        2,
      ])
      pickTopOK1: (*2|int)
      pickTopOK2: int
      pickTopErr: (*3|int)
      [=~"^pickNested1"]: matchN(1, [
        {
          a: 2
        },
      ])
      pickNested1OK1: {
        a: (*2|int)
      }
      pickNested1OK2: {
        a: int
      }
      pickNested1Err: {
        a: (*3|int)
      }
      [=~"^pickNested2"]: matchN(1, [
        {
          a: <=2
        },
      ])
      pickNested2OK1: {
        a: (*2|int)
      }
      pickNested2OK2: {
        a: int
      }
      pickNested2Err: {
        a: (*3|int)
      }
    }
    nestedOK: {
      matchN(4, [
        〈2;#A〉,
        〈2;#A〉,
        〈2;#A〉,
        〈2;#A〉,
      ])
      a: 2
      b: {
        matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        a: 3
        b: matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        b: {
          a: 4
        }
        c: matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        c: {
          a: 5
        }
      }
      c: {
        matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        a: 3
        b: matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        b: {
          a: 4
        }
        c: matchN(4, [
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
          〈3;#A〉,
        ])
        c: {
          a: 5
        }
      }
    }
  }
  not: {
    [=~"^single"]: matchN(0, [
      〈2;#Foo〉,
    ])
    singleOK: {
      a: "foo"
    }
    singleErr: {
      a: 2
    }
    [=~"^double"]: matchN(0, [
      matchN(0, [
        〈3;#Foo〉,
      ]),
    ])
    doubleOK: {
      a: 2
    }
    doubleErr: {
      a: "foo"
    }
  }
  oneOf: {
    [=~"^multiple1"]: matchN(1, [
      〈import;math〉.MultipleOf(3),
      〈import;math〉.MultipleOf(5),
    ])
    multiple1Err1: 1
    multiple1OK1: 3
    multiple1OK2: 5
    multiple1Err2: 15
  }
  anyOf: {
    [=~"^multiple1"]: matchN(>0, [
      〈import;math〉.MultipleOf(3),
      〈import;math〉.MultipleOf(5),
    ])
    multiple1Err1: 1
    multiple1OK1: 3
    multiple1OK2: 5
    multiple1OK3: 15
  }
  allOf: {
    [=~"^multiple1"]: matchN(2, [
      〈import;math〉.MultipleOf(3),
      〈import;math〉.MultipleOf(5),
    ])
    multiple1Err1: 1
    multiple1Err2: 3
    multiple1Err3: 5
    multiple1OK1: 15
  }
  bare: {
    embed: {
      t1: {
        a: {
          matchN(1, [
            >10,
          ])
        }
        b: {
          〈1;a〉
        }
      }
    }
    embed: {
      t2: {
        b: {
          〈1;a〉
        }
        a: {
          matchN(1, [
            >10,
          ])
        }
      }
    }
    direct: {
      t1: {
        a: matchN(1, [
          >10,
        ])
        b: 〈0;a〉
      }
    }
    direct: {
      t2: {
        b: 〈0;a〉
        a: matchN(1, [
          >10,
        ])
      }
    }
  }
  required: {
    ok1: {
      x: matchN(0, [
        {
          foo!: string
        },
      ])
      x: {
        bar: 2
      }
    }
    ok2: {
      x: matchN(0, [
        {
          foo!: string
        },
      ])
    }
    ok3: {
      x: matchN(0, [
        {
          bar!: string
        },
      ])
      x: {
        bar: 2
      }
    }
  }
  issue3575: {
    test1: {
      #x: matchN(1, [
        [
          {},
        ],
      ])
      x: 〈0;#x〉
      x: [
        {
          a: 1
        },
      ]
    }
    test2: {
      #x: matchN(1, [
        {},
      ])
      x: 〈0;#x〉
      x: {
        a: 1
      }
    }
  }
}
--- incomplete.cue
{
  incomplete: {
    incomplete1: {
      x: matchN(1, [
        {
          foo!: string
        },
      ])
      x: {
        bar: 2
      }
    }
    incomplete2: {
      x: matchN(1, [
        {
          foo!: string
        },
      ])
    }
    incomplete3: {
      x: matchN(〈import;math〉.MultipleOf(2), [
        {
          bar!: int
        },
        {
          foo!: int
        },
      ])
      x: {
        bar: 2
      }
    }
    incomplete4: {
      x: matchN(〈import;math〉.MultipleOf(2), [
        {
          bar!: int
        },
        {
          foo!: int
        },
        {
          baz!: int
        },
      ])
      x: {
        bar: 2
      }
    }
    incomplete5: {
      x: matchN((>1 & <=3), [
        {
          bar!: int
        },
        {
          foo!: int
        },
        {
          baz!: int
        },
      ])
      x: {
        bar: 2
      }
    }
    incomplete6: {
      x: matchN((2|3), [
        {
          bar!: int
        },
        {
          foo!: int
        },
        {
          baz!: int
        },
      ])
      x: {
        bar: 2
      }
    }
    err1: {
      x: matchN(1, [
        {
          bar!: string
        },
      ])
      x: {
        bar: 2
      }
    }
    err2: {
      x: matchN(0, [
        {
          bar!: int
        },
        {
          foo!: int
        },
      ])
      x: {
        bar: 2
      }
    }
    err3: {
      x: matchN(2, [
        {
          bar!: string
        },
        {
          foo!: string
        },
      ])
      x: {
        bar: 2
      }
    }
    err4: {
      x: matchN(>1, [
        {
          bar!: string
        },
        {
          foo!: string
        },
      ])
      x: {
        bar: 2
      }
    }
    err5: {
      x: matchN(<1, [
        {
          bar!: int
        },
        {
          foo!: int
        },
      ])
      x: {
        bar: 2
      }
    }
  }
  issue3389: {
    x: {
      bar: 2
    }
    x: matchN(1, [
      {
        bar: 3
      },
      {
        bar: 2
        foo!: int
      },
    ])
  }
}
--- issue3694.cue
{
  issue3694: {
    full: {
      #step: matchN(1, [
        {
          uses!: _
          ...
        },
        {
          run!: _
          ...
        },
      ])
      #step: close({
        uses?: string
        run?: string
      })
      s: (〈0;#step〉 & {
        run: "echo hello world"
      })
    }
  }
  issue3694: {
    simple: {
      #step: matchN(1, [
        {
          uses!: _
        },
      ])
      #step: close({
        uses?: string
      })
    }
  }
}
