Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,184 @@
|
||||
use dever::{
|
||||
compiler::{check, model::Program},
|
||||
syntax::{diagnostic::Diagnostic, source::Source},
|
||||
};
|
||||
|
||||
fn linked(text: &str) -> Result<Program, Diagnostic> {
|
||||
let inputs = [
|
||||
("main", text),
|
||||
("dever.json", include_str!("../../../lib/dever/json.dever")),
|
||||
(
|
||||
"dever.json.value",
|
||||
include_str!("../../../lib/dever/json/value.dever"),
|
||||
),
|
||||
];
|
||||
let modules = inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, (_, text))| {
|
||||
dever::syntax::parse(&Source::new(
|
||||
index,
|
||||
if index == 0 {
|
||||
"main.dever".into()
|
||||
} else {
|
||||
format!("library{index}.dever")
|
||||
},
|
||||
(*text).into(),
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
check::check(
|
||||
modules,
|
||||
&inputs
|
||||
.iter()
|
||||
.map(|(package, _)| (*package).into())
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_rejects_non_wire_types_and_untyped_empty_values() {
|
||||
for source in [
|
||||
"main(value: Secret) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"type Row { value: Secret }\nmain(value: Secret) (answer: Json) { row = Row { value = value } answer = dever.json.encode(row) }",
|
||||
"type Row { private values: List<Int> }\nmain(values: List<Int>) (answer: Json) { row = Row { values = values } answer = dever.json.encode(row) }",
|
||||
"type Row { private value: Int }\nmain(value: Row) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"type Row { value: Int > 0 }\nmain(values: List<Row?>) (answer: Json) { answer = dever.json.encode(values) }",
|
||||
"type State { Ready(value: Int) }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"type State { Ready\n error Failed }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"main(value: Map<Text, Int>) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"main(value: dever.system.File) (answer: Json) { answer = dever.json.encode(value) }",
|
||||
"main() (answer: Json) { answer = dever.json.encode([]) }",
|
||||
"main() (answer: Json) { answer = dever.json.encode(null) }",
|
||||
] {
|
||||
assert!(linked(source).is_err(), "{source}");
|
||||
}
|
||||
assert!(linked("type Row { amount: Decimal }\nmain(values: List<Row?>) (answer: Json) pure { answer = dever.json.encode(values) }").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payload_free_choices_use_the_existing_wire_codec() {
|
||||
assert!(linked("type State { Ready\n Busy }\nmain(value: State) (answer: Json) { answer = dever.json.encode(value) }").is_ok());
|
||||
assert!(linked("type State { Ready\n Busy }\ntype Row { state: State\n optional: State?\n states: List<State> }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_json_decode_can_be_captured_without_an_intermediate_function() {
|
||||
let prefix = "type Row { name: Text }\ntype Result { Decoded(value: Row)\n error Invalid(cause: dever.json.JsonResult) }\n";
|
||||
let program = linked(&format!("{prefix}main(payload: Json) (output: Result) {{ output = result(dever.json.decode(payload)) }}")).unwrap();
|
||||
let main = program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == "main")
|
||||
.unwrap();
|
||||
assert!(main.signature.failures.is_empty());
|
||||
assert!(
|
||||
linked(&format!(
|
||||
"{prefix}main(payload: Json) () {{ lost: Result = result(dever.json.decode(payload)) }}"
|
||||
))
|
||||
.unwrap_err()
|
||||
.code
|
||||
== "C012"
|
||||
);
|
||||
let source = format!(
|
||||
"{prefix}type Failure {{ error Missing }}\nraise() (payload: Json) {{ fail(Failure.Missing) }}\nmain() (output: Result) {{ output = result(dever.json.decode(raise())) }}"
|
||||
);
|
||||
let program = linked(&source).unwrap();
|
||||
let main = program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == "main")
|
||||
.unwrap();
|
||||
assert_eq!(main.signature.failures.len(), 1);
|
||||
assert_eq!(
|
||||
program.definitions[main.signature.failures[0].definition].name,
|
||||
"Failure"
|
||||
);
|
||||
for source in [
|
||||
format!("{prefix}main(payload: Json) () {{ lost = result(dever.json.decode(payload)) }}"),
|
||||
"type Wrong { Done(value: Int)\n error Invalid(message: Text) }\nmain(payload: Json) (output: Wrong) { output = result(dever.json.decode(payload)) }".into(),
|
||||
"type Limited { Done(value: Int > 0)\n error Invalid(cause: dever.json.JsonResult) }\nmain(payload: Json) (output: Limited) { output = result(dever.json.decode(payload)) }".into(),
|
||||
] {
|
||||
assert!(linked(&source).is_err(), "{source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_failures_preserve_capture_and_argument_propagation() {
|
||||
let source = "type Failure { error Missing }\nraise() (value: Int) { fail(Failure.Missing) }\nmain() (answer: dever.json.JsonResult) { answer = result(dever.json.encode(raise())) }";
|
||||
let program = linked(source).unwrap();
|
||||
let main = program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == "main")
|
||||
.unwrap();
|
||||
assert_eq!(main.signature.failures.len(), 1);
|
||||
assert_eq!(
|
||||
program.definitions[main.signature.failures[0].definition].name,
|
||||
"Failure"
|
||||
);
|
||||
for expression in [
|
||||
"dever.json.encode(1)",
|
||||
"dever.json.propertyValue(dever.json.fromText(\"42\"), \"a\")",
|
||||
] {
|
||||
let source = format!("main() () {{ lost = result({expression}) }}");
|
||||
assert_eq!(linked(&source).unwrap_err().code, "C012");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn typed_decode_requires_an_explicit_wire_type_and_propagates_errors() {
|
||||
let source = "type Row { names: List<Text>\noptional: Int? }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }";
|
||||
let program = linked(source).unwrap();
|
||||
let main = program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == "main")
|
||||
.unwrap();
|
||||
assert_eq!(main.signature.failures.len(), 1);
|
||||
for source in [
|
||||
"main(value: Json) () { row = dever.json.decode(value) }",
|
||||
"main(value: Text) (row: Int) { row = dever.json.decode(value) }",
|
||||
"type Row { private secret: Text }\nmain(value: Json) (row: Row) { row = dever.json.decode(value) }",
|
||||
] {
|
||||
assert!(linked(source).is_err(), "{source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_type_annotations_supply_context_without_wrapper_functions() {
|
||||
assert!(linked("type Row { names: List<Text> }\nmain(value: Json) (names: List<Text>) { row: Row = dever.json.decode(value)\n names = row.names }").is_ok());
|
||||
assert!(linked("main() (answer: Int) { values: List<Int> = []\n values = [1]\n answer = length(values) }").is_ok());
|
||||
for (source, code) in [
|
||||
("main(value: Int) () { value: Int = 1 }", "C002"),
|
||||
("main() () { value = 1\n value: Int = 2 }", "C002"),
|
||||
("main() () { value: Int = true }", "C005"),
|
||||
("main() () { value: Int = 1\n value = true }", "C005"),
|
||||
(
|
||||
"main() () { value: Channel<Int?> = channel(Int?, 1) }",
|
||||
"C013",
|
||||
),
|
||||
] {
|
||||
assert_eq!(linked(source).unwrap_err().code, code, "{source}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_is_a_nominal_source_type_and_intrinsics_remain_private() {
|
||||
assert!(
|
||||
linked("main(document: dever.json.value.Document) (value: Int) { value = document.root }")
|
||||
.is_ok()
|
||||
);
|
||||
assert!(linked("type Document { root: Int }\nmain(document: Document) (value: Text) { value = dever.json.stringify(document) }").is_err());
|
||||
for expression in [
|
||||
"dever.system.jsonFromText(\"{}\")",
|
||||
"dever.system.jsonObject({})",
|
||||
] {
|
||||
assert_eq!(
|
||||
linked(&format!("main() () {{ value = {expression} }}"))
|
||||
.unwrap_err()
|
||||
.code,
|
||||
"C006"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user