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
Vendored
+189
@@ -0,0 +1,189 @@
|
||||
use darling::FromDeriveInput;
|
||||
use syn::parse_quote;
|
||||
|
||||
mod foo {
|
||||
pub mod bar {
|
||||
pub fn init() -> String {
|
||||
String::from("hello")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(speak))]
|
||||
pub struct SpeakerOpts {
|
||||
#[darling(default = foo::bar::init)]
|
||||
first_word: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_default() {
|
||||
let speaker: SpeakerOpts = FromDeriveInput::from_derive_input(&parse_quote! {
|
||||
struct Foo;
|
||||
})
|
||||
.expect("Unit struct with no attrs should parse");
|
||||
|
||||
assert_eq!(speaker.first_word, "hello");
|
||||
}
|
||||
|
||||
/// Tests in this module capture the somewhat-confusing behavior observed when defaults
|
||||
/// are set at both the field and container level.
|
||||
///
|
||||
/// The general rule is that more-specific declarations preempt less-specific ones; this is
|
||||
/// unsurprising and allows for granular control over what happens when parsing an AST.
|
||||
mod stacked_defaults {
|
||||
use darling::{FromDeriveInput, FromMeta};
|
||||
use syn::parse_quote;
|
||||
|
||||
fn jane() -> String {
|
||||
"Jane".into()
|
||||
}
|
||||
|
||||
#[derive(FromMeta)]
|
||||
#[darling(default)]
|
||||
struct PersonName {
|
||||
#[darling(default = "jane")]
|
||||
first: String,
|
||||
#[darling(default)]
|
||||
middle: String,
|
||||
last: String,
|
||||
}
|
||||
|
||||
impl Default for PersonName {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first: "John".into(),
|
||||
middle: "T".into(),
|
||||
last: "Doe".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(person))]
|
||||
struct Person {
|
||||
#[darling(default)]
|
||||
name: PersonName,
|
||||
age: u8,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn name_first_only() {
|
||||
let person = Person::from_derive_input(&parse_quote! {
|
||||
#[person(name(first = "Bill"), age = 5)]
|
||||
struct Foo;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(person.name.first, "Bill");
|
||||
assert_eq!(
|
||||
person.name.middle, "",
|
||||
"Explicit field-level default should preempt container-level default"
|
||||
);
|
||||
assert_eq!(
|
||||
person.name.last, "Doe",
|
||||
"Absence of a field-level default falls back to container-level default"
|
||||
);
|
||||
}
|
||||
|
||||
/// This is the most surprising case. The presence of `name()` means we invoke
|
||||
/// `PersonName::from_list(&[])`. When that finishes parsing each of the zero nested
|
||||
/// items it has received, it will then start filling in missing fields, using the
|
||||
/// explicit field-level defaults for `first` and `middle`, while for `last` it will
|
||||
/// use the `last` field from the container-level default.
|
||||
#[test]
|
||||
fn name_empty_list() {
|
||||
let person = Person::from_derive_input(&parse_quote! {
|
||||
#[person(name(), age = 5)]
|
||||
struct Foo;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(person.name.first, "Jane");
|
||||
assert_eq!(person.name.middle, "");
|
||||
assert_eq!(person.name.last, "Doe");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_name() {
|
||||
let person = Person::from_derive_input(&parse_quote! {
|
||||
#[person(age = 5)]
|
||||
struct Foo;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(person.age, 5);
|
||||
assert_eq!(
|
||||
person.name.first, "John",
|
||||
"If `name` is not specified, `Person`'s field-level default should be used"
|
||||
);
|
||||
assert_eq!(person.name.middle, "T");
|
||||
assert_eq!(person.name.last, "Doe");
|
||||
}
|
||||
}
|
||||
|
||||
mod implicit_default {
|
||||
use darling::{util::Flag, FromDeriveInput};
|
||||
use syn::parse_quote;
|
||||
|
||||
// No use of `darling(default)` here at all!
|
||||
// This struct will fill in missing fields using FromMeta::from_none.
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(attributes(person))]
|
||||
struct Person {
|
||||
first_name: String,
|
||||
last_name: Option<String>,
|
||||
lefty: Flag,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_fields_fill() {
|
||||
let person = Person::from_derive_input(&parse_quote! {
|
||||
#[person(first_name = "James")]
|
||||
struct Foo;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(person.first_name, "James");
|
||||
assert_eq!(person.last_name, None);
|
||||
assert!(!person.lefty.is_present());
|
||||
}
|
||||
}
|
||||
|
||||
/// Test that a field-level implicit default using FromMeta::from_none is superseded
|
||||
/// by the parent declaring `#[darling(default)]`.
|
||||
mod overridden_implicit_default {
|
||||
use darling::{util::Flag, FromDeriveInput};
|
||||
use syn::parse_quote;
|
||||
|
||||
#[derive(FromDeriveInput)]
|
||||
#[darling(default, attributes(person))]
|
||||
struct Person {
|
||||
first_name: String,
|
||||
last_name: Option<String>,
|
||||
lefty: Flag,
|
||||
}
|
||||
|
||||
impl Default for Person {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
first_name: "Jane".into(),
|
||||
last_name: Some("Doe".into()),
|
||||
lefty: Flag::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_missing() {
|
||||
let person = Person::from_derive_input(&parse_quote!(
|
||||
#[person(last_name = "Archer")]
|
||||
struct Foo;
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(person.first_name, "Jane");
|
||||
assert_eq!(person.last_name, Some("Archer".into()));
|
||||
assert!(!person.lefty.is_present());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user