Skip to content

Commit 5cc9ec4

Browse files
committed
Merge branch 'client_termination' of https://github.com/Clasherzz/apidash into web_socket
2 parents 783821b + 31a7ab3 commit 5cc9ec4

File tree

2 files changed

+196
-1
lines changed

2 files changed

+196
-1
lines changed

doc/dev_guide/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@
66
4. [Integration Testing](https://github.com/foss42/apidash/blob/main/doc/dev_guide/integration_testing.md)
77
5. [List of API Endpoints for Testing](https://github.com/foss42/apidash/blob/main/doc/dev_guide/api_endpoints_for_testing.md)
88
6. [Packaging API Dash](https://github.com/foss42/apidash/blob/main/doc/dev_guide/packaging.md)
9-
9+
7. Other Topics
10+
- [Flutter Rust Bridge Experiment for Parsing Hurl](https://github.com/foss42/apidash/blob/main/doc/dev_guide/flutter_rust_bridge_experiment.md)
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Flutter Rust Bridge Experiment for Parsing Hurl
2+
*Author: [WrathOP](https://github.com/WrathOP)*
3+
4+
## Background and Initial Approach
5+
6+
When I first started adding hurl parser in API Dash, I wrote a custom parser using `petitparser` for the [hurl grammar](https://hurl.dev/docs/grammar.html). After spending about a week on this approach, I received some feedback from the maintainer to look into some approach of integrating the hurl Rust implementation directly rather than re-implementing the wheel. So that any future changes directly flow into the project.
7+
8+
## Understanding Flutter Rust Bridge
9+
10+
### What is Flutter Rust Bridge?
11+
12+
Flutter Rust Bridge serves as a powerful tool for developing Flutter plugins using Rust. It automatically generates all the necessary code for both Flutter and Rust, creating a seamless bridge between the two languages. This approach is particularly valuable when you want to harness Rust's performance and safety features within Flutter projects.
13+
14+
The [official documentation](https://cjycode.com/flutter_rust_bridge/) highlights three key features:
15+
1. Write standard Rust code, including complex features like arbitrary types, closures, mutable references, async functions, and traits
16+
2. Call Rust code from Flutter as if it were native Flutter code
17+
3. Automatic generation of all intermediate code required for communication
18+
19+
### Why Choose Flutter Rust Bridge?
20+
21+
Two main factors drove my decision to use Flutter Rust Bridge:
22+
23+
1. **Automatic Updates with Upstream Changes**: By using the official Hurl Rust library, any changes made to the `hurl.dev` parser would automatically propagate to our plugin. This ensures long-term maintainability and compatibility.
24+
25+
2. **Performance Benefits**: Rust's renowned performance characteristics and safety guarantees make it ideal for parsing large Hurl files efficiently.
26+
27+
## Implementation Process
28+
29+
### Setting Up the Flutter Rust Bridge Plugin
30+
31+
While there are several examples of Flutter Rust Bridge usage available, such as [flutter smooth](https://github.com/fzyzcjy/flutter_smooth) and the [official documentation demo](https://cjycode.com/flutter_rust_bridge/demo/), I found that plugin-specific examples were scarce. Here's a detailed breakdown of my setup process:
32+
33+
1. **Project Initialization**
34+
Instead of using the standard Flutter plugin template (`flutter create --template=plugin hurl`), Flutter Rust Bridge provides its own initialization command.
35+
36+
Inside the `apidash/packages` directory, I executed:
37+
38+
```bash
39+
flutter_rust_bridge_codegen create hurl --template plugin
40+
```
41+
42+
This command generated three main directories:
43+
44+
1. `cargokit/`: Contains build infrastructure
45+
- build_tool/: Build system utilities
46+
- cmake/: CMake configuration files
47+
- Pod creation scripts for iOS
48+
- Build scripts for various platforms
49+
- Gradle configuration for Android
50+
51+
2. `rust/`: The Rust project structure
52+
- src/
53+
- api/: Contains the core Rust implementation
54+
- simple.rs: Main implementation file
55+
- mod.rs: Module definitions
56+
- frbgenerated.rs: Auto-generated bridge code
57+
- lib.rs: Library entry point
58+
- cargo.toml: Rust dependencies and configuration
59+
- .gitignore
60+
61+
3. `lib/`: Flutter code
62+
- rust/: Generated Rust bindings
63+
- hurl.dart: Package export file
64+
65+
### Implementation Details
66+
67+
The core parsing functionality was implemented in [`rust/src/api/simple.rs`](https://github.com/foss42/apidash/blob/9a201dd10af6e6804c7fd8fab8352701ab722fb2/packages/hurl/rust/src/api/simple.rs):
68+
69+
```rust
70+
/// Parses a Hurl file content and returns its JSON representation
71+
#[flutter_rust_bridge::frb(sync)]
72+
pub fn parse_hurl_to_json(content: String) -> Result<String, String> {
73+
match parse_hurl_file(&content) {
74+
Ok(hurl_file) => {
75+
// Convert to JSON using hurlfmt's format_json
76+
Ok(format_json(&hurl_file))
77+
}
78+
Err(errors) => {
79+
let mut error_msg = String::new();
80+
write!(error_msg, "Failed to parse Hurl file: {:?}", errors).unwrap();
81+
Err(error_msg)
82+
}
83+
}
84+
}
85+
```
86+
87+
For dependencies, I added hurlfmt to `cargo.toml`:
88+
89+
```toml
90+
[package]
91+
name = "hurl"
92+
version = "0.1.0"
93+
edition = "2021"
94+
95+
[lib]
96+
crate-type = ["cdylib", "staticlib"]
97+
98+
[dependencies]
99+
flutter_rust_bridge = "=2.7.0"
100+
hurlfmt = "6.0.0"
101+
hurl_core = "6.0.0"
102+
```
103+
104+
### Dart Model Implementation
105+
106+
Based on the Hurl documentation, I identified the core data structures:
107+
- HurlFile: Contains a list of Entries
108+
- Entries: Composed of Request and Response objects
109+
110+
I implemented these models using the `freezed` package in Dart, creating a clean mapping between the JSON output from the Hurl parser and our Dart data structures. This part of the implementation proved particularly successful and robust.
111+
112+
## Compilation Challenges and Platform Support
113+
114+
### Build Process Overview
115+
116+
The compilation process varies by platform, but generally follows these steps:
117+
118+
1. For local testing in the `hurl/tests` directory, building the Rust component requires:
119+
```bash
120+
cd apidash/packages/hurl/rust
121+
cargo build --release
122+
```
123+
124+
2. For actual deployment, cargokit manages the platform-specific build process during Flutter's standard build phase. The process involves:
125+
- On iOS: Generating and building Pod files
126+
- On Android: Creating platform-specific binaries
127+
- On macOS/Linux: Building dynamic libraries
128+
- On Windows: Generating appropriate DLLs
129+
130+
### Understanding Flutter Rust Bridge on Mobile Platforms
131+
132+
Flutter Rust Bridge generally works seamlessly across platforms as long as the Rust dependencies don't rely on platform-specific implementations. The bridge itself handles the complexity of cross-platform compilation through cargokit, which manages the build process for each target platform. However, when Rust crates depend on system libraries or have platform-specific requirements, complications can arise.
133+
134+
For iOS and Android specifically, the build process involves:
135+
1. For iOS: Compiling Rust code to a static library that gets embedded in the iOS app bundle
136+
2. For Android: Creating platform-specific .so files for each supported architecture (arm64-v8a, armeabi-v7a, x86_64)
137+
138+
The challenge comes when dependencies like `libxml2` expect certain system libraries to be available, which may not exist or behave differently on mobile platforms.
139+
140+
### Platform-Specific Challenges
141+
142+
#### macOS Implementation
143+
144+
The main challenge on macOS involved the `libxml2` dependency. I reported this issue on the [Hurl repository](https://github.com/Orange-OpenSource/hurl/issues/3603). The solution required adding specific linker flags to the `apidash/packages/hurl/macos/hurl.podspec`:
145+
146+
```ruby
147+
spec.pod_target_xcconfig = {
148+
'OTHER_LDFLAGS' => '-force_load ${BUILT_PRODUCTS_DIR}/libhurl.a -lxml2'
149+
}
150+
```
151+
152+
#### iOS and Android Challenges
153+
154+
The iOS and Android implementations faced more significant hurdles, primarily due to `libxml2` compatibility issues. Unlike macOS, these platforms don't include `libxml2` as a system library, and the Rust crate wrapper doesn't support mobile platforms.
155+
156+
The following approaches were attempted for iOS:
157+
1. Modifying header search paths
158+
2. Adding framework linkage
159+
3. Adjusting Xcode build phases
160+
4. Experimenting with different linking configurations
161+
162+
None of these attempts proved successful, leading to a deeper investigation of the `libxml2` dependency.
163+
164+
#### Maintainer Feedback and Investigation
165+
166+
After reaching out to both the Hurl and Flutter Rust Bridge maintainers, I received valuable feedback from the Hurl maintainer:
167+
168+
> _"I'm not an expert in iOS/Android, but what I would do is try to build something smaller, maybe just calling libxml through a Flutter component, then trying to build with libxml but with the Rust crate wrapper, etc."_ ([GitHub comment](https://github.com/Orange-OpenSource/hurl/issues/3603#issuecomment-2611159759))
169+
170+
This suggestion highlighted a crucial debugging approach: isolating the `libxml2` integration from the rest of the Hurl parsing functionality. The idea is to:
171+
172+
1. First verify that we can call `libxml2` directly from Flutter
173+
2. Then attempt to use the Rust crate wrapper
174+
3. Finally integrate it with the full Hurl parser
175+
176+
This systematic approach could help identify exactly where the build process breaks down on mobile platforms and potentially lead to a workable solution.
177+
178+
### Current Status and Potential Solutions
179+
180+
After extensive testing and consultation with both the Hurl and Flutter Rust Bridge maintainers, I've identified several potential paths forward:
181+
182+
1. **Dart Native FFIs**: This approach is currently experimental but shows promise. The Flutter Rust Bridge maintainer suggested this option in this [Github comment](https://github.com/fzyzcjy/flutter_rust_bridge/issues/2510#issuecomment-2608469143).
183+
184+
2. **Legacy Hurlfmt Version**: We could use a version of the library from before the `libxml` transition. This would avoid the platform compatibility issues but might miss out on recent performance improvements.
185+
186+
3. **Custom Parser Completion**: Returning to and completing the `petitparser`-based implementation could provide a more controlled, albeit maintenance-heavy solution.
187+
188+
## Code Availability
189+
190+
The current implementation is available in two branches of my fork:
191+
- [Hurl Parser with flutter_rust_bridge](https://github.com/WrathOP/apidash/tree/hurl-parser-rust)
192+
- [Hurl Parser with petiteparser](https://github.com/WrathOP/apidash/tree/hurl-parser-added)
193+
194+
Feel free to contribute ideas or suggestions for addressing these challenges!

0 commit comments

Comments
 (0)