Skip to content

Commit 4368eb1

Browse files
committed
adds new example project for logging
1 parent ebce348 commit 4368eb1

28 files changed

Lines changed: 3481 additions & 1 deletion

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exclude = [
44
# Exclude example packages to ensure the example mimics how a typical user will use it.
55
# For example, the `target` directory is different with and without workspaces.
66
"frb_example/dart_minimal/rust",
7+
"frb_example/dart_logging/rust",
78
"frb_example/deliberate_bad/rust",
89
"frb_example/flutter_via_create/rust",
910
"frb_example/flutter_via_integrate/rust",
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
migrate_working_dir/
12+
13+
# IntelliJ related
14+
*.iml
15+
*.ipr
16+
*.iws
17+
.idea/
18+
19+
# The .vscode folder contains launch configuration and tasks you configure in
20+
# VS Code which you may wish to be included in version control, so this line
21+
# is commented out by default.
22+
#.vscode/
23+
24+
# Flutter/Dart/Pub related
25+
**/doc/api/
26+
**/ios/Flutter/.last_build_id
27+
.dart_tool/
28+
.flutter-plugins
29+
.flutter-plugins-dependencies
30+
.packages
31+
.pub-cache/
32+
.pub/
33+
/build/
34+
35+
# Symbolication related
36+
app.*.symbols
37+
38+
# Obfuscation related
39+
app.*.map.json
40+
41+
# Android Studio will place build artifacts here
42+
/android/app/debug
43+
/android/app/profile
44+
/android/app/release

frb_example/dart_logging/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Dart minimal logging example for `flutter_rust_bridge`
2+
3+
Please visit the main documentation or open an issue for more information.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
analyzer:
2+
exclude:
3+
- rust/target/**.dart # contains dumped debug info, instead of normal code
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import 'package:flutter_rust_bridge_utils/flutter_rust_bridge_utils.dart';
2+
3+
void main(List<String> args) async => simpleBuild(args);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# See `pure_dart` example for comments on the configs
2+
rust_input: rust/src/api/**/*.rs
3+
dart_output: lib/src/rust
4+
c_output: frb_generated.h
5+
dump_all: true
6+
local: true
7+
8+
# TODO temp
9+
#full_dep: true
10+
#enable_lifetime: true
11+
#stop_on_error: true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// Nothing when using full_dep=false mode
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
2+
import 'package:frb_example_dart_logging/src/rust/api/minimal_logging.dart';
3+
import 'package:frb_example_dart_logging/src/rust/frb_generated.dart';
4+
5+
final LOGGER = FRBLogger.initLogger(maxLogLevel: LogLevel.trace);
6+
7+
// If you are developing a binary program, you may want to put it in `bin/something.dart`
8+
Future<void> main() async {
9+
await RustLib.init();
10+
LOGGER.trace(
11+
'Call Rust and get: 100+200 = ${await minimalAdder(a: 100, b: 200)}');
12+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// This file is automatically generated, so please do not edit it.
2+
// @generated by `flutter_rust_bridge`@ 2.10.0.
3+
4+
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
5+
6+
import '../frb_generated.dart';
7+
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
8+
import 'package:logging/logging.dart';
9+
10+
// These functions are ignored because they are not marked as `pub`: `_construct_default_message`, `_default_log_fn`, `_default_logger_name`, `_default_max_log_level`, `from_u16`, `to_u16`
11+
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `enabled`, `flush`, `from`, `log`
12+
13+
String rootLoggerName() =>
14+
RustLib.instance.api.crateApiMinimalLoggingRootLoggerName();
15+
16+
String maxLogLevel() =>
17+
RustLib.instance.api.crateApiMinimalLoggingMaxLogLevel();
18+
19+
/// this is the call for logging (from Rust and Dart (as logFn))
20+
void logFn({required MirLogRecord record}) =>
21+
RustLib.instance.api.crateApiMinimalLoggingLogFn(record: record);
22+
23+
/// uses custom type translation to translate between log::LogLevel and Dart:logging::Level
24+
/// loglevel is represented by a number, so that we don't need to put \import `import 'package:logging/logging.dart';`
25+
/// into the dart preamble in flutter_rust_bridge.yaml
26+
Stream<MirLogRecord> initializeLog2Dart({required int maxLogLevel}) =>
27+
RustLib.instance.api
28+
.crateApiMinimalLoggingInitializeLog2Dart(maxLogLevel: maxLogLevel);
29+
30+
Future<int> minimalAdder({required int a, required int b}) =>
31+
RustLib.instance.api.crateApiMinimalLoggingMinimalAdder(a: a, b: b);
32+
33+
class FRBLogger {
34+
final RustStreamSink<MirLogRecord> streamSink;
35+
36+
const FRBLogger({
37+
required this.streamSink,
38+
});
39+
40+
// HINT: Make it `#[frb(sync)]` to let it become the default constructor of Dart class.
41+
static Future<FRBLogger> newInstance() =>
42+
RustLib.instance.api.crateApiMinimalLoggingFrbLoggerNew();
43+
44+
static FRBDartLogger initLogger(
45+
{String name = 'FRBLogger',
46+
LogLevel maxLogLevel = LogLevel.info,
47+
Function({required MirLogRecord record}) customLogFunction = logFn}) {
48+
//initialize the rust side
49+
int maxLogLevelNumber = maxLogLevel.levelNumberThreshold;
50+
Stream<MirLogRecord> stream =
51+
initializeLog2Dart(maxLogLevel: maxLogLevelNumber);
52+
53+
// Functions for type conversion for interaction with frb_dart/utils/frb_logging.dart
54+
// Wrap logFn to match `void Function({required dynamic record})`
55+
void Function({required dynamic record}) wrappedLogFn =
56+
({required dynamic record}) {
57+
// Safely cast `dynamic` record back to `MirLogRecord` for the original `logFn`
58+
logFn(record: record as MirLogRecord);
59+
};
60+
// Wrap fromDartLogRecord to match `dynamic Function(LogRecord record)`
61+
MirLogRecord Function(LogRecord record) wrappedFromDartLogRecord =
62+
(LogRecord record) {
63+
return MirLogRecord.fromDartLogRecord(record);
64+
};
65+
// Wrap customLogFunction if provided, to match `void Function({required dynamic record})?`
66+
void Function({required dynamic record})? wrappedCustomLogFunction;
67+
wrappedCustomLogFunction = ({required dynamic record}) {
68+
customLogFunction(record: record as MirLogRecord);
69+
};
70+
71+
return FRBDartLogger.initAndGetSingleton<MirLogRecord>(
72+
streamSink: stream,
73+
name: name,
74+
logFn: wrappedLogFn,
75+
fromDartLogRecord: wrappedFromDartLogRecord,
76+
maxLogLevel: maxLogLevel,
77+
customLogFunction: wrappedCustomLogFunction,
78+
);
79+
}
80+
81+
static FRBDartLogger getLogger([String? name]) {
82+
return FRBDartLogger.getLogger(name);
83+
}
84+
85+
@override
86+
int get hashCode => streamSink.hashCode;
87+
88+
@override
89+
bool operator ==(Object other) =>
90+
identical(this, other) ||
91+
other is FRBLogger &&
92+
runtimeType == other.runtimeType &&
93+
streamSink == other.streamSink;
94+
}
95+
96+
/// mapping log crate's [Record](https://docs.rs/log/latest/log/struct.Record.html) to dart's Logger [LogRecord](https://pub.dev/documentation/logging/latest/logging/LogRecord-class.html).
97+
/// intermediary struct to avoid Record's lifetimes
98+
class MirLogRecord {
99+
final int levelNumber;
100+
final String levelName;
101+
final String message;
102+
final String loggerName;
103+
final String timestamp;
104+
final bool rustLog;
105+
final String? modulePath;
106+
final String? fileName;
107+
final int? lineNumber;
108+
109+
const MirLogRecord({
110+
required this.levelNumber,
111+
required this.levelName,
112+
required this.message,
113+
required this.loggerName,
114+
required this.timestamp,
115+
required this.rustLog,
116+
this.modulePath,
117+
this.fileName,
118+
this.lineNumber,
119+
});
120+
121+
static MirLogRecord fromDartLogRecord(LogRecord record) {
122+
return MirLogRecord(
123+
message: record.message,
124+
levelNumber: record.level.value,
125+
levelName: LogLevel.fromLoggingLevel(record.level).name.toUpperCase(),
126+
timestamp: record.time.toString(),
127+
loggerName: record.loggerName,
128+
rustLog: false,
129+
);
130+
}
131+
132+
static LogRecord toDartLogRecordFromMir(MirLogRecord record) {
133+
return LogRecord(
134+
LogLevel.fromString(record.levelName).toLoggingLevel(),
135+
record.message,
136+
record.loggerName,
137+
);
138+
}
139+
140+
LogRecord toDartLogRecord() {
141+
return toDartLogRecordFromMir(this);
142+
}
143+
144+
@override
145+
int get hashCode =>
146+
levelNumber.hashCode ^
147+
levelName.hashCode ^
148+
message.hashCode ^
149+
loggerName.hashCode ^
150+
timestamp.hashCode ^
151+
rustLog.hashCode ^
152+
modulePath.hashCode ^
153+
fileName.hashCode ^
154+
lineNumber.hashCode;
155+
156+
@override
157+
bool operator ==(Object other) =>
158+
identical(this, other) ||
159+
other is MirLogRecord &&
160+
runtimeType == other.runtimeType &&
161+
levelNumber == other.levelNumber &&
162+
levelName == other.levelName &&
163+
message == other.message &&
164+
loggerName == other.loggerName &&
165+
timestamp == other.timestamp &&
166+
rustLog == other.rustLog &&
167+
modulePath == other.modulePath &&
168+
fileName == other.fileName &&
169+
lineNumber == other.lineNumber;
170+
}

0 commit comments

Comments
 (0)