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>
90 lines
2.4 KiB
Rust
90 lines
2.4 KiB
Rust
use crate::{Error, Result};
|
|
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
|
use quote::ToTokens;
|
|
use std::iter::Peekable;
|
|
|
|
pub(crate) fn parse_input(
|
|
input: TokenStream,
|
|
) -> Result<(Vec<Attribute>, Vec<TokenTree>, TokenTree)> {
|
|
let mut input = input.into_iter().peekable();
|
|
let mut attrs = Vec::new();
|
|
|
|
while let Some(attr) = parse_next_attr(&mut input)? {
|
|
attrs.push(attr);
|
|
}
|
|
|
|
let sig = parse_signature(&mut input);
|
|
let body = input.next().ok_or_else(|| {
|
|
Error::new(
|
|
Span::call_site(),
|
|
"`#[proc_macro_error]` can be applied only to functions".to_string(),
|
|
)
|
|
})?;
|
|
|
|
Ok((attrs, sig, body))
|
|
}
|
|
|
|
fn parse_next_attr(
|
|
input: &mut Peekable<impl Iterator<Item = TokenTree>>,
|
|
) -> Result<Option<Attribute>> {
|
|
let shebang = match input.peek() {
|
|
Some(TokenTree::Punct(ref punct)) if punct.as_char() == '#' => input.next().unwrap(),
|
|
_ => return Ok(None),
|
|
};
|
|
|
|
let group = match input.peek() {
|
|
Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Bracket => {
|
|
let res = group.clone();
|
|
input.next();
|
|
res
|
|
}
|
|
other => {
|
|
let span = other.map_or(Span::call_site(), TokenTree::span);
|
|
return Err(Error::new(span, "expected `[`".to_string()));
|
|
}
|
|
};
|
|
|
|
let path = match group.stream().into_iter().next() {
|
|
Some(TokenTree::Ident(ident)) => Some(ident),
|
|
_ => None,
|
|
};
|
|
|
|
Ok(Some(Attribute {
|
|
shebang,
|
|
group: TokenTree::Group(group),
|
|
path,
|
|
}))
|
|
}
|
|
|
|
fn parse_signature(input: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Vec<TokenTree> {
|
|
let mut sig = Vec::new();
|
|
loop {
|
|
match input.peek() {
|
|
Some(TokenTree::Group(ref group)) if group.delimiter() == Delimiter::Brace => {
|
|
return sig;
|
|
}
|
|
None => return sig,
|
|
_ => sig.push(input.next().unwrap()),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) struct Attribute {
|
|
pub(crate) shebang: TokenTree,
|
|
pub(crate) group: TokenTree,
|
|
pub(crate) path: Option<Ident>,
|
|
}
|
|
|
|
impl Attribute {
|
|
pub(crate) fn path_is_ident(&self, ident: &str) -> bool {
|
|
self.path.as_ref().map_or(false, |p| *p == ident)
|
|
}
|
|
}
|
|
|
|
impl ToTokens for Attribute {
|
|
fn to_tokens(&self, ts: &mut TokenStream) {
|
|
self.shebang.to_tokens(ts);
|
|
self.group.to_tokens(ts);
|
|
}
|
|
}
|