Skip to content

Commit 76dda88

Browse files
authored
Merge pull request #4 from chavic/chavic/maps-type-support
Reorganize project documentation and improve testing workflow
2 parents b13b373 + ffcf4c1 commit 76dda88

File tree

8 files changed

+155
-55
lines changed

8 files changed

+155
-55
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ ISSUES.md
1313
.tmp_tests/
1414
convert_fixtures.sh
1515
.DS_Store
16+
ISSUES.md
17+
.tmp_tests/

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ members = [
6969
"fixtures/metadata",
7070
"fixtures/simple-iface",
7171
"fixtures/streams_ext",
72-
#"fixtures/*",
72+
"fixtures/simple-fns",
7373
]
7474

7575
[workspace.dependencies]

TODO.md

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/gen/compounds.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,108 @@ impl_code_type_for_compound!(SequenceCodeType, "List<{}>", "Sequence{}");
183183

184184
impl_renderable_for_compound!(OptionalCodeType, "{}?", "FfiConverterOptional{}");
185185
impl_renderable_for_compound!(SequenceCodeType, "FfiConverterSequence{}");
186+
187+
// Map<K, V>
188+
#[derive(Debug)]
189+
pub struct MapCodeType {
190+
self_type: Type,
191+
key: Type,
192+
value: Type,
193+
}
194+
195+
impl MapCodeType {
196+
pub fn new(self_type: Type, key: Type, value: Type) -> Self {
197+
Self { self_type, key, value }
198+
}
199+
200+
fn key(&self) -> &Type {
201+
&self.key
202+
}
203+
204+
fn value(&self) -> &Type {
205+
&self.value
206+
}
207+
}
208+
209+
impl CodeType for MapCodeType {
210+
fn type_label(&self) -> String {
211+
format!(
212+
"Map<{}, {}>",
213+
DartCodeOracle::find(self.key()).type_label(),
214+
DartCodeOracle::find(self.value()).type_label()
215+
)
216+
}
217+
218+
fn canonical_name(&self) -> String {
219+
let key = DartCodeOracle::find(self.key()).canonical_name();
220+
let val = DartCodeOracle::find(self.value()).canonical_name();
221+
format!("Map{}To{}", key, val)
222+
}
223+
}
224+
225+
impl Renderable for MapCodeType {
226+
fn render_type_helper(&self, type_helper: &dyn TypeHelperRenderer) -> dart::Tokens {
227+
type_helper.include_once_check(&self.ffi_converter_name(), &self.self_type);
228+
229+
let key_codetype = DartCodeOracle::find(self.key());
230+
let val_codetype = DartCodeOracle::find(self.value());
231+
232+
type_helper.include_once_check(&key_codetype.canonical_name(), self.key());
233+
type_helper.include_once_check(&val_codetype.canonical_name(), self.value());
234+
235+
let cl_name = &self.ffi_converter_name();
236+
let key_type_label_owned = key_codetype.type_label();
237+
let val_type_label_owned = val_codetype.type_label();
238+
let key_type_label = &key_type_label_owned;
239+
let val_type_label = &val_type_label_owned;
240+
241+
let key_conv_owned = key_codetype.ffi_converter_name();
242+
let val_conv_owned = val_codetype.ffi_converter_name();
243+
let key_conv = &key_conv_owned;
244+
let val_conv = &val_conv_owned;
245+
246+
quote! {
247+
class $cl_name {
248+
static Map<$key_type_label, $val_type_label> lift(RustBuffer buf) {
249+
return $cl_name.read(buf.asUint8List()).value;
250+
}
251+
252+
static LiftRetVal<Map<$key_type_label, $val_type_label>> read(Uint8List buf) {
253+
final map = <$key_type_label, $val_type_label>{};
254+
final length = buf.buffer.asByteData(buf.offsetInBytes).getInt32(0);
255+
int offset = buf.offsetInBytes + 4;
256+
for (var i = 0; i < length; i++) {
257+
final k = $key_conv.read(Uint8List.view(buf.buffer, offset));
258+
offset += k.bytesRead;
259+
final v = $val_conv.read(Uint8List.view(buf.buffer, offset));
260+
offset += v.bytesRead;
261+
map[k.value] = v.value;
262+
}
263+
return LiftRetVal(map, offset - buf.offsetInBytes);
264+
}
265+
266+
static int write(Map<$key_type_label, $val_type_label> value, Uint8List buf) {
267+
buf.buffer.asByteData(buf.offsetInBytes).setInt32(0, value.length);
268+
int offset = buf.offsetInBytes + 4;
269+
for (final entry in value.entries) {
270+
offset += $key_conv.write(entry.key, Uint8List.view(buf.buffer, offset));
271+
offset += $val_conv.write(entry.value, Uint8List.view(buf.buffer, offset));
272+
}
273+
return offset - buf.offsetInBytes;
274+
}
275+
276+
static int allocationSize(Map<$key_type_label, $val_type_label> value) {
277+
return value.entries
278+
.map((e) => $key_conv.allocationSize(e.key) + $val_conv.allocationSize(e.value))
279+
.fold(4, (a, b) => a + b);
280+
}
281+
282+
static RustBuffer lower(Map<$key_type_label, $val_type_label> value) {
283+
final buf = Uint8List(allocationSize(value));
284+
write(value, buf);
285+
return toRustBuffer(buf);
286+
}
287+
}
288+
}
289+
}
290+
}

src/gen/mod.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22
use std::collections::HashSet;
33
use std::io::Read;
4+
use std::process::Command;
45

56
use anyhow::Result;
67
use camino::Utf8Path;
@@ -226,6 +227,22 @@ impl BindingGenerator for DartBindingGenerator {
226227

227228
tokens.format_file(&mut w.as_formatter(&fmt), &config)?;
228229
}
230+
231+
// Run full Dart formatter on the output directory as a best-effort step.
232+
// This is non-fatal: failures will only emit a warning.
233+
let mut format_command = Command::new("dart");
234+
format_command
235+
.current_dir(&settings.out_dir)
236+
.arg("format")
237+
.arg(".");
238+
match format_command.spawn().and_then(|mut c| c.wait()) {
239+
Ok(status) if status.success() => {}
240+
Ok(_) | Err(_) => {
241+
println!(
242+
"WARNING: dart format failed or is unavailable; proceeding without full formatting"
243+
);
244+
}
245+
}
229246
Ok(())
230247
}
231248

@@ -281,7 +298,7 @@ pub fn generate_dart_bindings(
281298
&LocalConfigSupplier(udl_file.to_string()),
282299
None,
283300
out_dir_override.unwrap(),
284-
false,
301+
true,
285302
)?;
286303
Ok(())
287304
} else {

src/gen/oracle.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,11 @@ impl<T: AsType> AsCodeType for T {
688688
self.as_type(),
689689
*inner_type,
690690
)),
691+
Type::Map { key_type, value_type, .. } => Box::new(compounds::MapCodeType::new(
692+
self.as_type(),
693+
*key_type,
694+
*value_type,
695+
)),
691696
Type::Enum { name, .. } => Box::new(enums::EnumCodeType::new(name)),
692697
Type::Record { name, .. } => Box::new(records::RecordCodeType::new(name)),
693698
Type::CallbackInterface { name, .. } => Box::new(

src/gen/render/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ impl<T: AsType> AsRenderable for T {
9494
self.as_type(),
9595
*inner_type,
9696
)),
97+
Type::Map { key_type, value_type } => Box::new(compounds::MapCodeType::new(
98+
self.as_type(),
99+
*key_type,
100+
*value_type,
101+
)),
97102
Type::Enum { name, .. } => Box::new(enums::EnumCodeType::new(name)),
98103
Type::Record { name, .. } => Box::new(records::RecordCodeType::new(name)),
99104
Type::Custom {

src/testing.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,28 @@ pub fn run_test(fixture: &str, udl_path: &str, config_path: Option<&str>) -> Res
6464
// Copy fixture test files to output directory
6565
let test_glob_pattern = "test/*.dart";
6666
for file in glob::glob(test_glob_pattern)?.filter_map(Result::ok) {
67-
let filename = file.file_name().expect("bad filename").to_str().expect("non-UTF8 filename");
67+
let filename = file
68+
.file_name()
69+
.expect("bad filename")
70+
.to_str()
71+
.expect("non-UTF8 filename");
6872
copy(&file, test_outdir.join(filename))?;
6973
}
7074

75+
// Format the generated Dart code before running tests (best-effort)
76+
let mut format_command = Command::new("dart");
77+
format_command.current_dir(&out_dir).arg("format").arg(".");
78+
match format_command.spawn().and_then(|mut c| c.wait()) {
79+
Ok(status) if status.success() => {}
80+
Ok(_) | Err(_) => {
81+
println!("WARNING: dart format unavailable or failed; continuing with tests anyway");
82+
if std::env::var("CI").is_err() {
83+
// skip in CI environment
84+
thread::sleep(Duration::from_secs(1));
85+
}
86+
}
87+
}
88+
7189
// Run the test script against compiled bindings
7290
let mut command = Command::new("dart");
7391
command.current_dir(&out_dir).arg("test");

0 commit comments

Comments
 (0)