Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Result<MyType, MyError> with exception throwing on Dart #582

Closed
wants to merge 89 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
75be93a
first try
lattice0 Jul 18, 2022
6f5a6fb
try2
lattice0 Jul 18, 2022
34b58ad
rollback
lattice0 Jul 18, 2022
e75094b
error handling
lattice0 Jul 18, 2022
82fde94
parseErrorData
lattice0 Jul 18, 2022
4407912
generates correct code
lattice0 Jul 18, 2022
3d0a427
works
lattice0 Jul 18, 2022
cc78745
works
lattice0 Jul 18, 2022
17c785d
works
lattice0 Jul 18, 2022
99677e4
works
lattice0 Jul 18, 2022
e87dfeb
works!
lattice0 Jul 18, 2022
b4925e5
nested errors work
lattice0 Jul 18, 2022
28b776e
rem dirt
lattice0 Jul 18, 2022
b470a1d
add book
lattice0 Jul 18, 2022
8306ca1
changes
lattice0 Jul 18, 2022
da91bfa
fixes
lattice0 Jul 18, 2022
4b89eaa
Merge branch 'master' of https://github.com/fzyzcjy/flutter_rust_brid…
lattice0 Jul 18, 2022
938fba5
implements exception
lattice0 Jul 19, 2022
6fc4f71
struct exceptions work as well
lattice0 Jul 19, 2022
ce96d39
clean printlns
lattice0 Jul 19, 2022
74ec09e
proper fmt
lattice0 Jul 19, 2022
0b41f65
adding exception backtrace support
lattice0 Jul 20, 2022
b5485c3
exception gen works
lattice0 Jul 24, 2022
17fdfeb
exceptions
lattice0 Jul 24, 2022
e98b820
errors
lattice0 Jul 24, 2022
1edbf98
pass
lattice0 Jul 24, 2022
46802a1
cleanup
lattice0 Jul 24, 2022
94b3e92
Merge branch 'master' of https://github.com/fzyzcjy/flutter_rust_brid…
lattice0 Jul 24, 2022
4bd5820
pass
lattice0 Jul 24, 2022
22f8423
backtrace
lattice0 Jul 24, 2022
9e74c48
clippy
lattice0 Jul 24, 2022
db40975
removes cargo
lattice0 Jul 24, 2022
dcc5d6b
fmt
lattice0 Jul 24, 2022
19d53b6
Update and rename feature_result.md to lang_result.md
fzyzcjy Jul 25, 2022
2eec320
Update SUMMARY.md
fzyzcjy Jul 25, 2022
8721719
Update SUMMARY.md
fzyzcjy Jul 25, 2022
5e5ae9b
adds backtrace
lattice0 Jul 28, 2022
182eadd
Merge branch 'error' of https://github.com/lattice0/flutter_rust_brid…
lattice0 Jul 28, 2022
e53323d
backtrace
lattice0 Jul 29, 2022
006fc03
backtrace
lattice0 Jul 29, 2022
3a3e94d
adds backtrace
lattice0 Jul 29, 2022
424f616
fix
lattice0 Jul 29, 2022
4e7d6a2
backtrace format
lattice0 Jul 29, 2022
2a82f72
disabled backtrace
lattice0 Jul 29, 2022
3d92aa9
multi
lattice0 Jul 30, 2022
50e83dd
tests pass
lattice0 Jul 30, 2022
c2ed509
dart fmt
lattice0 Jul 30, 2022
5abee1d
fmt
lattice0 Jul 30, 2022
cf8ed09
dart fmt
lattice0 Jul 30, 2022
1eab8a9
format
lattice0 Jul 30, 2022
5a665c8
more fmt
lattice0 Jul 30, 2022
44198ff
book
lattice0 Jul 30, 2022
885642f
rm submodule
lattice0 Jul 30, 2022
74ab207
Update lang_result.md
fzyzcjy Jul 30, 2022
ffc7635
checkpoint
lattice0 Jul 31, 2022
6fc3d06
mods
lattice0 Jul 31, 2022
fc04bd1
Merge branch 'error' of https://github.com/lattice0/flutter_rust_brid…
lattice0 Jul 31, 2022
b601ff9
cleaner
lattice0 Jul 31, 2022
ae89007
backtrace contains
lattice0 Jul 31, 2022
a07bd04
cleanup
lattice0 Jul 31, 2022
69e7a83
removes unnecessary files
lattice0 Jul 31, 2022
f1a7ab8
!
lattice0 Jul 31, 2022
bd7ca05
small corrections
lattice0 Jul 31, 2022
468de1d
first functios
lattice0 Jul 31, 2022
cf48221
implements frb
lattice0 Jul 31, 2022
e8dc72e
checkpoint
lattice0 Aug 3, 2022
f409204
box into dart
lattice0 Aug 3, 2022
902c8f3
anyhow
lattice0 Aug 4, 2022
4a80be0
backtrace delegate
lattice0 Aug 4, 2022
9511ba9
anyhow error
lattice0 Aug 4, 2022
2034428
result
lattice0 Aug 5, 2022
73cd4f5
anyhow::Result fixed
lattice0 Aug 5, 2022
74bd096
mods
lattice0 Aug 5, 2022
b86e01d
rm submodule
lattice0 Aug 5, 2022
9e27215
more
lattice0 Aug 5, 2022
0775f00
handler
lattice0 Aug 5, 2022
36b200d
diff
lattice0 Aug 5, 2022
12e794e
removes unused import
lattice0 Aug 5, 2022
973f210
ignore error
lattice0 Aug 6, 2022
db99e61
disables another error
lattice0 Aug 6, 2022
3e08698
removes other error
lattice0 Aug 6, 2022
da22e04
disables
lattice0 Aug 6, 2022
4049d17
one test
lattice0 Aug 6, 2022
d819796
returnCustomStructError
lattice0 Aug 6, 2022
9551f70
valgrind install
lattice0 Aug 6, 2022
06fb443
update
lattice0 Aug 6, 2022
c125477
sudo
lattice0 Aug 6, 2022
51ec4f8
libc6-dbg
lattice0 Aug 6, 2022
bf5829c
rerun
lattice0 Aug 6, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "allo-isolate"]
path = allo-isolate
url = https://github.com/sunshine-protocol/allo-isolate
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions allo-isolate
Submodule allo-isolate added at a24225
31 changes: 31 additions & 0 deletions book/src/feature/result.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Result
lattice0 marked this conversation as resolved.
Show resolved Hide resolved

There is support for Rust's `std::Result`, where you can return either a value or an error.

## Example

```rust,noplayground

pub enum CustomError {
Error0(String),
Error1(u32),
}

pub fn return_err_custom_error() -> Result<u32, CustomError> {
Err(CustomError::Error1(3))
}
```

Becomes something that can be used like this:

```Dart

try {
final r = await api.returnErrCustomError();
print("received $r");
} catch (e) {
print('dart catch e: $e');
expect(e, isA<CustomError>());
}
}
```
8 changes: 8 additions & 0 deletions frb_codegen/src/generator/dart/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,12 @@ fn generate_api_func(func: &IrFunc, ir_file: &IrFile) -> GeneratedApiFunc {
format!("_wire2api_{}", func.output.safe_ident())
};

let parse_error_data = if let Some(error_output) = &func.error_output {
format!("parseErrorData: _wire2api_{},", error_output.safe_ident())
} else {
"parseErrorData: null,".to_string()
};
lattice0 marked this conversation as resolved.
Show resolved Hide resolved

let implementation = match func.mode {
IrFuncMode::Sync => format!(
"{} => {}(FlutterRustBridgeSyncTask(
Expand All @@ -427,12 +433,14 @@ fn generate_api_func(func: &IrFunc, ir_file: &IrFile) -> GeneratedApiFunc {
callFfi: (port_) => inner.{}({}),
parseSuccessData: {},
{}
{}
));",
partial,
execute_func_name,
func.wire_func_name(),
wire_param_list.join(", "),
parse_sucess_data,
parse_error_data,
task_common_args,
),
};
Expand Down
2 changes: 1 addition & 1 deletion frb_codegen/src/generator/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ impl Generator {
let code_call_inner_func_result = if func.fallible {
code_call_inner_func
} else {
format!("Ok({})", code_call_inner_func)
format!("Result::<_,()>::Ok({})", code_call_inner_func)
};

let (handler_func_name, return_type, code_closure) = match func.mode {
Expand Down
3 changes: 3 additions & 0 deletions frb_codegen/src/ir/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ impl IrFile {
}
if include_func_output {
func.output.visit_types(f, self);
if let Some(error_output) = &func.error_output {
error_output.visit_types(f, self);
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion frb_codegen/src/ir/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub struct IrFunc {
pub name: String,
pub inputs: Vec<IrField>,
pub output: IrType,
pub error_output: Option<IrType>,
pub fallible: bool,
Comment on lines +8 to 9
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether error_output and fallible kind of duplicated? Or shall we use an enum to represent all states?

e.g.

enum {
  CustomError(IrType),
  AnyhowError,
  Infallible,
}

or

enum {
  CustomError(IrType),
  Infallible,
}

etc

pub mode: IrFuncMode,
pub comments: Vec<IrComment>,
Expand All @@ -19,7 +20,7 @@ impl IrFunc {
/// Represents a function's output type
#[derive(Debug, Clone)]
pub enum IrFuncOutput {
ResultType(IrType),
ResultType(IrType, Option<IrType>),
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
Type(IrType),
}

Expand Down
36 changes: 25 additions & 11 deletions frb_codegen/src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,24 @@ impl<'a> Parser<'a> {
let inner = ty::SupportedInnerType::try_from_syn_type(ty)?;

match inner {
ty::SupportedInnerType::Path(ty::SupportedPathType {
ident,
generic: Some(generic),
}) if ident == RESULT_IDENT => Some(IrFuncOutput::ResultType(
self.type_parser.convert_to_ir_type(*generic)?,
)),
ty::SupportedInnerType::Path(ty::SupportedPathType { ident, mut generic })
if ident == RESULT_IDENT && !generic.is_empty() =>
{
let first_argument = self
.type_parser
.convert_to_ir_type(generic.remove(0))
.unwrap();
let second_argument = if generic.len() == 1 {
Some(
self.type_parser
.convert_to_ir_type(generic.remove(0))
.unwrap(),
)
} else {
None
};
Some(IrFuncOutput::ResultType(first_argument, second_argument))
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
}
_ => Some(IrFuncOutput::Type(
self.type_parser.convert_to_ir_type(inner)?,
)),
Expand Down Expand Up @@ -112,6 +124,7 @@ impl<'a> Parser<'a> {

let mut inputs = Vec::new();
let mut output = None;
let mut error_output = None;
let mut mode: Option<IrFuncMode> = None;
let mut fallible = true;

Expand Down Expand Up @@ -147,26 +160,26 @@ impl<'a> Parser<'a> {
}

if output.is_none() {
output = Some(match &sig.output {
(output, error_output) = match &sig.output {
ReturnType::Type(_, ty) => {
match self.try_parse_fn_output_type(ty).unwrap_or_else(|| {
panic!(
"Failed to parse function output type `{}`",
type_to_string(ty)
)
}) {
IrFuncOutput::ResultType(ty) => ty,
IrFuncOutput::ResultType(ty, err) => (Some(ty), err),
IrFuncOutput::Type(ty) => {
fallible = false;
ty
(Some(ty), None)
}
}
}
ReturnType::Default => {
fallible = false;
IrType::Primitive(IrTypePrimitive::Unit)
(Some(IrType::Primitive(IrTypePrimitive::Unit)), None)
}
});
};
mode = Some(
if let Some(IrType::Delegate(IrTypeDelegate::SyncReturnVecU8)) = output {
IrFuncMode::Sync
Expand All @@ -180,6 +193,7 @@ impl<'a> Parser<'a> {
name: func_name,
inputs,
output: output.expect("unsupported output"),
error_output,
fallible,
mode: mode.expect("missing mode"),
comments: extract_comments(&func.attrs),
Expand Down
97 changes: 50 additions & 47 deletions frb_codegen/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ impl std::fmt::Display for SupportedInnerType {
#[derive(Debug)]
pub struct SupportedPathType {
pub ident: syn::Ident,
pub generic: Option<Box<SupportedInnerType>>,
pub generic: Vec<SupportedInnerType>,
}

impl std::fmt::Display for SupportedPathType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let ident = self.ident.to_string();
if let Some(generic) = &self.generic {
write!(f, "{}<{}>", ident, generic)
if !self.generic.is_empty() {
write!(f, "{}<{:?}>", ident, self.generic)
} else {
write!(f, "{}", ident)
}
Expand All @@ -94,19 +94,20 @@ impl SupportedInnerType {
match last_segment.arguments {
syn::PathArguments::None => Some(SupportedInnerType::Path(SupportedPathType {
ident: last_segment.ident,
generic: None,
generic: Vec::new(),
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
})),
syn::PathArguments::AngleBracketed(a) => {
let generic = match a.args.into_iter().next() {
Some(syn::GenericArgument::Type(t)) => {
Some(Box::new(SupportedInnerType::try_from_syn_type(&t)?))
let args = a.args.into_iter().collect::<Vec<GenericArgument>>();
let mut supported_inner_types: Vec<SupportedInnerType> = Vec::new();
for arg in args {
if let syn::GenericArgument::Type(t) = arg {
supported_inner_types
.push(SupportedInnerType::try_from_syn_type(&t).unwrap());
}
_ => None,
};

}
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
Some(SupportedInnerType::Path(SupportedPathType {
ident: last_segment.ident,
generic,
generic: supported_inner_types,
}))
}
_ => None,
Expand Down Expand Up @@ -166,48 +167,49 @@ impl<'a> TypeParser<'a> {
}

/// Converts a path type into an `IrType` if possible.
pub fn convert_path_to_ir_type(&mut self, p: SupportedPathType) -> Option<IrType> {
pub fn convert_path_to_ir_type(&mut self, mut p: SupportedPathType) -> Option<IrType> {
let p_as_str = format!("{}", &p);
let ident_string = &p.ident.to_string();
if let Some(generic) = p.generic {
if !p.generic.is_empty() {
match ident_string.as_str() {
"SyncReturn" => {
// Special-case SyncReturn<Vec<u8>>. SyncReturn for any other type is not
// supported.
match *generic {
SupportedInnerType::Path(SupportedPathType {
ident,
generic: Some(generic),
}) if ident == "Vec" => match *generic {
SupportedInnerType::Path(SupportedPathType {
ident,
generic: None,
}) if ident == "u8" => {
Some(IrType::Delegate(IrTypeDelegate::SyncReturnVecU8))
match &p.generic[0] {
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
SupportedInnerType::Path(SupportedPathType { ident, generic })
if ident == "Vec" && !generic.is_empty() =>
{
match &generic[0] {
SupportedInnerType::Path(SupportedPathType { ident, generic })
if ident == "u8" && generic.is_empty() =>
{
Some(IrType::Delegate(IrTypeDelegate::SyncReturnVecU8))
}
_ => None,
}
_ => None,
},
}
_ => None,
}
}
"Vec" => {
// Special-case Vec<String> as StringList
if matches!(*generic, SupportedInnerType::Path(SupportedPathType { ref ident, .. }) if ident == "String")
if matches!(p.generic[0], SupportedInnerType::Path(SupportedPathType { ref ident, .. }) if ident == "String")
lattice0 marked this conversation as resolved.
Show resolved Hide resolved
{
Some(IrType::Delegate(IrTypeDelegate::StringList))
} else {
self.convert_to_ir_type(*generic).map(|inner| match inner {
Primitive(primitive) => {
PrimitiveList(IrTypePrimitiveList { primitive })
}
others => GeneralList(IrTypeGeneralList {
inner: Box::new(others),
}),
})
self.convert_to_ir_type(p.generic.remove(0))
.map(|inner| match inner {
Primitive(primitive) => {
PrimitiveList(IrTypePrimitiveList { primitive })
}
others => GeneralList(IrTypeGeneralList {
inner: Box::new(others),
}),
})
}
}
"ZeroCopyBuffer" => {
let inner = self.convert_to_ir_type(*generic);
let inner = self.convert_to_ir_type(p.generic.remove(0));
if let Some(IrType::PrimitiveList(IrTypePrimitiveList { primitive })) = inner {
Some(IrType::Delegate(
IrTypeDelegate::ZeroCopyBufferVecPrimitive(primitive),
Expand All @@ -216,31 +218,32 @@ impl<'a> TypeParser<'a> {
None
}
}
"Box" => self.convert_to_ir_type(*generic).map(|inner| {
"Box" => self.convert_to_ir_type(p.generic.remove(0)).map(|inner| {
Boxed(IrTypeBoxed {
exist_in_real_api: true,
inner: Box::new(inner),
})
}),
"Option" => {
// Disallow nested Option
if matches!(*generic, SupportedInnerType::Path(SupportedPathType { ref ident, .. }) if ident == "Option")
if matches!(p.generic[0], SupportedInnerType::Path(SupportedPathType { ref ident, .. }) if ident == "Option")
{
panic!(
"Nested optionals without indirection are not supported. (Option<Option<{}>>)",
p_as_str
);
}
self.convert_to_ir_type(*generic).map(|inner| match inner {
Primitive(prim) => IrType::Optional(IrTypeOptional::new_prim(prim)),
st @ StructRef(_) => {
IrType::Optional(IrTypeOptional::new_ptr(Boxed(IrTypeBoxed {
inner: Box::new(st),
exist_in_real_api: false,
})))
}
other => IrType::Optional(IrTypeOptional::new_ptr(other)),
})
self.convert_to_ir_type(p.generic.remove(0))
.map(|inner| match inner {
Primitive(prim) => IrType::Optional(IrTypeOptional::new_prim(prim)),
st @ StructRef(_) => {
IrType::Optional(IrTypeOptional::new_ptr(Boxed(IrTypeBoxed {
inner: Box::new(st),
exist_in_real_api: false,
})))
}
other => IrType::Optional(IrTypeOptional::new_ptr(other)),
})
}
_ => None,
}
Expand Down
Loading