feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+175
@@ -0,0 +1,175 @@
|
||||
//! Tests for `FromGenerics`, and - indirectly - `FromGenericParam`.
|
||||
//! These tests assume `FromTypeParam` is working and only look at whether the wrappers for magic
|
||||
//! fields are working as expected.
|
||||
|
||||
use darling::{
|
||||
ast::{self, GenericParamExt},
|
||||
util::{Ignored, WithOriginal},
|
||||
FromDeriveInput, FromTypeParam, Result,
|
||||
};
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(lorem))]
|
||||
struct MyReceiver {
|
||||
pub generics: ast::Generics<ast::GenericParam<MyTypeParam>>,
|
||||
}
|
||||
|
||||
#[derive(FromTypeParam)]
|
||||
#[darling(attributes(lorem))]
|
||||
struct MyTypeParam {
|
||||
pub ident: syn::Ident,
|
||||
#[darling(default)]
|
||||
pub foo: bool,
|
||||
pub bar: Option<String>,
|
||||
}
|
||||
|
||||
fn fdi<T: FromDeriveInput>(src: &str) -> Result<T> {
|
||||
FromDeriveInput::from_derive_input(&syn::parse_str(src).expect("Source parses"))
|
||||
}
|
||||
|
||||
/// Verify that `ast::Generics` is populated correctly when there is no generics declaration
|
||||
#[test]
|
||||
fn no_generics() {
|
||||
let rec: MyReceiver = fdi("struct Baz;").expect("Input is well-formed");
|
||||
assert!(rec.generics.where_clause.is_none());
|
||||
assert_eq!(rec.generics.params.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::bool_assert_comparison)]
|
||||
fn expand_some() {
|
||||
let rec: MyReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U);
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
assert!(rec.generics.where_clause.is_none());
|
||||
|
||||
// Make sure we've preserved the lifetime param, though we don't do anything with it.
|
||||
assert!(rec.generics.params[0].as_lifetime_param().is_some());
|
||||
|
||||
let mut ty_param_iter = rec.generics.type_params();
|
||||
|
||||
let first = ty_param_iter
|
||||
.next()
|
||||
.expect("type_params should not be empty");
|
||||
assert!(first.bar.is_none());
|
||||
assert!(first.foo);
|
||||
assert_eq!(first.ident, "T");
|
||||
|
||||
let second = ty_param_iter
|
||||
.next()
|
||||
.expect("type_params should have a second value");
|
||||
assert_eq!(
|
||||
second
|
||||
.bar
|
||||
.as_ref()
|
||||
.expect("Second type param should set bar"),
|
||||
"x"
|
||||
);
|
||||
assert_eq!(second.foo, false);
|
||||
assert_eq!(second.ident, "U");
|
||||
}
|
||||
|
||||
/// Verify ≤0.4.1 behavior - where `generics` had to be `syn::Generics` - keeps working.
|
||||
#[test]
|
||||
fn passthrough() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct PassthroughReceiver {
|
||||
pub generics: syn::Generics,
|
||||
}
|
||||
|
||||
let rec: PassthroughReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U);
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
let mut type_param_iter = rec.generics.type_params();
|
||||
assert!(type_param_iter.next().is_some());
|
||||
}
|
||||
|
||||
/// Verify that `where_clause` is passed through when it exists.
|
||||
/// As of 0.4.1, there is no `FromWhereClause` trait, so other types aren't supported
|
||||
/// for that field.
|
||||
#[test]
|
||||
fn where_clause() {
|
||||
let rec: MyReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
assert!(rec.generics.where_clause.is_some());
|
||||
}
|
||||
|
||||
/// Test that `WithOriginal` works for generics.
|
||||
#[test]
|
||||
fn with_original() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct WorigReceiver {
|
||||
generics: WithOriginal<ast::Generics<ast::GenericParam<MyTypeParam>>, syn::Generics>,
|
||||
}
|
||||
|
||||
let rec: WorigReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
// Make sure we haven't lost anything in the conversion
|
||||
assert_eq!(rec.generics.parsed.params.len(), 3);
|
||||
assert_eq!(rec.generics.original.params.len(), 3);
|
||||
|
||||
let parsed_t: &MyTypeParam = rec.generics.parsed.params[1]
|
||||
.as_type_param()
|
||||
.expect("Second argument should be type param");
|
||||
|
||||
// Make sure the first type param in each case is T
|
||||
assert_eq!(parsed_t.ident, "T");
|
||||
assert_eq!(
|
||||
rec.generics
|
||||
.original
|
||||
.type_params()
|
||||
.next()
|
||||
.expect("First type param should exist")
|
||||
.ident,
|
||||
"T"
|
||||
);
|
||||
|
||||
// Make sure we actually parsed the first type param
|
||||
assert!(parsed_t.foo);
|
||||
assert!(parsed_t.bar.is_none());
|
||||
}
|
||||
|
||||
/// Make sure generics can be ignored
|
||||
#[test]
|
||||
fn ignored() {
|
||||
#[derive(FromDeriveInput)]
|
||||
struct IgnoredReceiver {
|
||||
generics: Ignored,
|
||||
}
|
||||
|
||||
let rec: IgnoredReceiver = fdi(r#"
|
||||
struct Baz<
|
||||
'a,
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(&'a T, U) where T: Into<String>;
|
||||
"#)
|
||||
.expect("Input is well-formed");
|
||||
|
||||
assert_eq!(Ignored, rec.generics);
|
||||
}
|
||||
Reference in new issue
Block a user