-- in.cue --
@experiment(explicitopen)

noClose: {
	foo: a: int
	a: __reclose({
		foo...
	})

	t1: ok: a & {b: 1}
}

recursive: {
	#x: {foo: int}

	// Basic usage of __reclose - makes structs recursively closed
	a: __reclose({
		#x...
		nested: field2: int
	})

	// Test that extra fields are not allowed (should error)
	t1: err: a & {extra: "not allowed"}

	t2: ok: a & {nested: field2: 5}
	t3: ok: a & {foo: 5}

	// Test that nested extra fields are not allowed (should error)
	t4: err: a & {
		nested: {
			field2: 1
			extra:  "not allowed"
		}
	}

	// Test with empty struct
	// TODO(eval): is this correct?
	e: __reclose({})
	t5: err: e & {extra: "not allowed"}
}

nonRecursive: {
	x: __reclose({
		close({b: int})...
		c: int
		d: e: int
	})
	y: err: x & {f: int}
	z: ok: x & {d: f: int}
}

// Test that __reclose detects and restores non-recursive closing from
// an embedded close()-d value that was opened with ....
nonRecursiveDetect: {
	foo: close({b: {}})
	a: __reclose({
		foo...
	})
	// Top-level field not allowed: non-recursive close restored by __reclose.
	t1: err: a & {f: int}
	// Nested field allowed: close was non-recursive, so b is still open.
	t2: ok: a & {b: c: 1}
}

// Test that __reclose handles both recursive and non-recursive closing
// when both are present.
//
// NOTE: one would expect that with the old semantics, b.c is allowed in t2.
// But it is NOT. To mimic the old behavior, cue fix _could_ just use __closeAll
// in this case. Users can than chose to use __reclose instead to relax the
// condition.
mixedClose: {
	#D: {x: y: int}
	foo: close({b: {}})
	a: __reclose({
		#D...
		foo...
	})
	// Top-level extra field not allowed.
	t1: err: a & {f: int}
	// Allowed fields work.
	t2: ok: a & {x: y: 5, b: c: 1}
}

// Test that __reclose can wrap a close() call and conditionally add
// recursive closing on top. This is the pattern generated by cue fix
// when a close() at field-value level wraps a struct with embeddings.
recloseWrapsClose: {
	#def: {a: b: int}
	a: __reclose(close({
		#def...
	}))
	// Top-level extra field not allowed (close provides non-recursive).
	t1: err: a & {f: int}
	// Nested extra field not allowed (__reclose detects recursive from #def).
	t2: err: a & {a: c: 1}
	// Allowed nested field works.
	t3: ok: a & {a: b: 5}
}

nonStructLiterals: {
	// Test with non-struct should error
	t1: err: __reclose("not a struct")

	#A: a:   int
	t4: err: __reclose(#A)
}

nonStructDefinitions: {
	#Def: 1

	a: __reclose({
		#Def...
	})

	t1: ok: a & 1

	// errors because of regular field + embedded scalar
	t2: err: a & {b: 1}
}
-- old.cue --
retro: {
	// May not be used if explicitopen is not set.
	t1: err: __reclose({})
}
-- out/compile --
--- in.cue
{
  noClose: {
    foo: {
      a: int
    }
    a: __reclose({
      〈1;foo〉...
    })
    t1: {
      ok: (〈1;a〉 & {
        b: 1
      })
    }
  }
  recursive: {
    #x: {
      foo: int
    }
    a: __reclose({
      〈1;#x〉...
      nested: {
        field2: int
      }
    })
    t1: {
      err: (〈1;a〉 & {
        extra: "not allowed"
      })
    }
    t2: {
      ok: (〈1;a〉 & {
        nested: {
          field2: 5
        }
      })
    }
    t3: {
      ok: (〈1;a〉 & {
        foo: 5
      })
    }
    t4: {
      err: (〈1;a〉 & {
        nested: {
          field2: 1
          extra: "not allowed"
        }
      })
    }
    e: __reclose({})
    t5: {
      err: (〈1;e〉 & {
        extra: "not allowed"
      })
    }
  }
  nonRecursive: {
    x: __reclose({
      close({
        b: int
      })...
      c: int
      d: {
        e: int
      }
    })
    y: {
      err: (〈1;x〉 & {
        f: int
      })
    }
    z: {
      ok: (〈1;x〉 & {
        d: {
          f: int
        }
      })
    }
  }
  nonRecursiveDetect: {
    foo: close({
      b: {}
    })
    a: __reclose({
      〈1;foo〉...
    })
    t1: {
      err: (〈1;a〉 & {
        f: int
      })
    }
    t2: {
      ok: (〈1;a〉 & {
        b: {
          c: 1
        }
      })
    }
  }
  mixedClose: {
    #D: {
      x: {
        y: int
      }
    }
    foo: close({
      b: {}
    })
    a: __reclose({
      〈1;#D〉...
      〈1;foo〉...
    })
    t1: {
      err: (〈1;a〉 & {
        f: int
      })
    }
    t2: {
      ok: (〈1;a〉 & {
        x: {
          y: 5
        }
        b: {
          c: 1
        }
      })
    }
  }
  recloseWrapsClose: {
    #def: {
      a: {
        b: int
      }
    }
    a: __reclose(close({
      〈1;#def〉...
    }))
    t1: {
      err: (〈1;a〉 & {
        f: int
      })
    }
    t2: {
      err: (〈1;a〉 & {
        a: {
          c: 1
        }
      })
    }
    t3: {
      ok: (〈1;a〉 & {
        a: {
          b: 5
        }
      })
    }
  }
  nonStructLiterals: {
    t1: {
      err: __reclose("not a struct")
    }
    #A: {
      a: int
    }
    t4: {
      err: __reclose(〈1;#A〉)
    }
  }
  nonStructDefinitions: {
    #Def: 1
    a: __reclose({
      〈1;#Def〉...
    })
    t1: {
      ok: (〈1;a〉 & 1)
    }
    t2: {
      err: (〈1;a〉 & {
        b: 1
      })
    }
  }
}
--- old.cue
{
  retro: {
    t1: {
      err: __reclose({})
    }
  }
}
