mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Support deriving Decode for opaques
This commit is contained in:
parent
61ba59de07
commit
1d885c4ab2
3 changed files with 115 additions and 2 deletions
|
@ -7,7 +7,7 @@
|
|||
//! implementation to the value they wrap.
|
||||
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_module::{called_via::CalledVia, symbol::Symbol};
|
||||
use roc_parse::ast;
|
||||
use roc_region::all::{Loc, Region};
|
||||
|
||||
|
@ -49,6 +49,73 @@ fn to_encoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
fn decoder<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
let alloc_expr = |it| env.arena.alloc(Loc::at(DERIVED_REGION, it));
|
||||
|
||||
let call_custom = {
|
||||
let bytes = "#bytes";
|
||||
let fmt = "#fmt";
|
||||
|
||||
// Decode.decodeWith bytes Decode.decoder fmt
|
||||
let call_decode_with = ast::Expr::Apply(
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decodeWith",
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: bytes,
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "decoder",
|
||||
}),
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "",
|
||||
ident: fmt,
|
||||
}),
|
||||
]),
|
||||
CalledVia::Space,
|
||||
);
|
||||
|
||||
// Decode.mapResult (Decode.decodeWith bytes Decode.decoder fmt) @Opaq
|
||||
let call_map_result = ast::Expr::Apply(
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "mapResult",
|
||||
}),
|
||||
env.arena.alloc([
|
||||
&*alloc_expr(call_decode_with),
|
||||
alloc_expr(ast::Expr::OpaqueRef(at_opaque)),
|
||||
]),
|
||||
CalledVia::Space,
|
||||
);
|
||||
|
||||
// \bytes, fmt ->
|
||||
// Decode.mapResult (Decode.decodeWith bytes Decode.decoder fmt) @Opaq
|
||||
let custom_closure = ast::Expr::Closure(
|
||||
env.arena.alloc([
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(bytes)),
|
||||
Loc::at(DERIVED_REGION, ast::Pattern::Identifier(fmt)),
|
||||
]),
|
||||
alloc_expr(call_map_result),
|
||||
);
|
||||
|
||||
// Decode.custom \bytes, fmt -> ...
|
||||
ast::Expr::Apply(
|
||||
alloc_expr(ast::Expr::Var {
|
||||
module_name: "Decode",
|
||||
ident: "custom",
|
||||
}),
|
||||
env.arena.alloc([&*alloc_expr(custom_closure)]),
|
||||
CalledVia::Space,
|
||||
)
|
||||
};
|
||||
|
||||
call_custom
|
||||
}
|
||||
|
||||
fn hash<'a>(env: &mut Env<'a>, at_opaque: &'a str) -> ast::Expr<'a> {
|
||||
let alloc_pat = |it| env.arena.alloc(Loc::at(DERIVED_REGION, it));
|
||||
let alloc_expr = |it| env.arena.alloc(Loc::at(DERIVED_REGION, it));
|
||||
|
@ -162,7 +229,7 @@ pub(crate) fn synthesize_member_impl<'a>(
|
|||
format!("#{}_toEncoder", opaque_name),
|
||||
to_encoder(env, at_opaque),
|
||||
),
|
||||
Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), todo!()),
|
||||
Symbol::DECODE_DECODER => (format!("#{}_decoder", opaque_name), decoder(env, at_opaque)),
|
||||
Symbol::HASH_HASH => (format!("#{}_hash", opaque_name), hash(env, at_opaque)),
|
||||
Symbol::BOOL_IS_EQ => (format!("#{}_isEq", opaque_name), is_eq(env, at_opaque)),
|
||||
other => internal_error!("{:?} is not a derivable ability member!", other),
|
||||
|
|
|
@ -8062,6 +8062,26 @@ mod solve_expr {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_decoder_for_opaque() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
N := U8 has [Decoding]
|
||||
|
||||
main : Decoder N _
|
||||
main = Decode.custom \bytes, fmt ->
|
||||
Decode.decodeWith bytes Decode.decoder fmt
|
||||
# ^^^^^^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"N#Decode.decoder(3) : List U8, fmt -[[7(7)]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting"
|
||||
print_only_under_alias: true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_hash_for_opaque() {
|
||||
infer_queries!(
|
||||
|
|
|
@ -791,6 +791,32 @@ fn decode_use_stdlib() {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(
|
||||
any(feature = "gen-llvm", feature = "gen-wasm"),
|
||||
not(debug_assertions) // https://github.com/roc-lang/roc/issues/3898
|
||||
))]
|
||||
fn decode_derive_decoder_for_opaque() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test"
|
||||
imports [Json]
|
||||
provides [main] to "./platform"
|
||||
|
||||
HelloWorld := { a: Str } has [Decoding]
|
||||
|
||||
main =
|
||||
when Str.toUtf8 """{"a":"Hello, World!"}""" |> Decode.fromBytes Json.fromUtf8 is
|
||||
Ok (@HelloWorld {a}) -> a
|
||||
_ -> "FAIL"
|
||||
"#
|
||||
),
|
||||
RocStr::from(r#"Hello, World!"#),
|
||||
RocStr
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn decode_use_stdlib_json_list() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue