Skip to content

Commit

Permalink
Merge branch 'main' into try-init
Browse files Browse the repository at this point in the history
  • Loading branch information
speelbarrow authored Nov 27, 2024
2 parents df22375 + 1a2bee6 commit 8397711
Show file tree
Hide file tree
Showing 31 changed files with 250 additions and 276 deletions.
2 changes: 2 additions & 0 deletions esp-hal-procmacros/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Added the `BuilderLite` derive macro which implements the Builder Lite pattern for a struct (#2614)

### Fixed

### Changed
Expand Down
145 changes: 142 additions & 3 deletions esp-hal-procmacros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,22 @@ use proc_macro::{Span, TokenStream};
use proc_macro2::Ident;
use proc_macro_crate::{crate_name, FoundCrate};
use proc_macro_error2::abort;
use syn::{parse, parse::Error as ParseError, spanned::Spanned, Item, ItemFn, ReturnType, Type};
use quote::{format_ident, quote};
use syn::{
parse,
parse::Error as ParseError,
spanned::Spanned,
Data,
DataStruct,
GenericArgument,
Item,
ItemFn,
Path,
PathArguments,
PathSegment,
ReturnType,
Type,
};

use self::interrupt::{check_attr_whitelist, WhiteListCaller};

Expand Down Expand Up @@ -238,8 +253,8 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
/// esp_hal::interrupt::Priority::Priority2)]`.
///
/// If no priority is given, `Priority::min()` is assumed
#[proc_macro_error2::proc_macro_error]
#[proc_macro_attribute]
#[proc_macro_error2::proc_macro_error]
pub fn handler(args: TokenStream, input: TokenStream) -> TokenStream {
#[derive(Debug, FromMeta)]
struct MacroArgs {
Expand Down Expand Up @@ -341,8 +356,8 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {

/// Marks the entry function of a LP core / ULP program.
#[cfg(any(feature = "is-lp-core", feature = "is-ulp-core"))]
#[proc_macro_error2::proc_macro_error]
#[proc_macro_attribute]
#[proc_macro_error2::proc_macro_error]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
lp_core::entry(args, input)
}
Expand Down Expand Up @@ -381,3 +396,127 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {

run(&args.meta, f, main()).unwrap_or_else(|x| x).into()
}

/// Automatically implement the [Builder Lite] pattern for a struct.
///
/// This will create an `impl` which contains methods for each field of a
/// struct, allowing users to easily set the values. The generated methods will
/// be the field name prefixed with `with_`, and calls to these methods can be
/// chained as needed.
///
/// ## Example
///
/// ```rust, no_run
/// #[derive(Default)]
/// enum MyEnum {
/// #[default]
/// A,
/// B,
/// }
///
/// #[derive(Default, BuilderLite)]
/// #[non_exhaustive]
/// struct MyStruct {
/// enum_field: MyEnum,
/// bool_field: bool,
/// option_field: Option<i32>,
/// }
///
/// MyStruct::default()
/// .with_enum_field(MyEnum::B)
/// .with_bool_field(true)
/// .with_option_field(-5);
/// ```
///
/// [Builder Lite]: https://matklad.github.io/2022/05/29/builder-lite.html
#[proc_macro_derive(BuilderLite)]
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);

let span = input.span();
let ident = input.ident;

let mut fns = Vec::new();
if let Data::Struct(DataStruct { fields, .. }) = &input.data {
for field in fields {
let field_ident = field.ident.as_ref().unwrap();
let field_type = &field.ty;

let function_ident = format_ident!("with_{}", field_ident);

let maybe_path_type = extract_type_path(field_type)
.and_then(|path| extract_option_segment(path))
.and_then(|path_seg| match path_seg.arguments {
PathArguments::AngleBracketed(ref params) => params.args.first(),
_ => None,
})
.and_then(|generic_arg| match *generic_arg {
GenericArgument::Type(ref ty) => Some(ty),
_ => None,
});

let (field_type, field_assigns) = if let Some(inner_type) = maybe_path_type {
(inner_type, quote! { Some(#field_ident) })
} else {
(field_type, quote! { #field_ident })
};

fns.push(quote! {
#[doc = concat!(" Assign the given value to the `", stringify!(#field_ident) ,"` field.")]
pub fn #function_ident(mut self, #field_ident: #field_type) -> Self {
self.#field_ident = #field_assigns;
self
}
});

if maybe_path_type.is_some() {
let function_ident = format_ident!("with_{}_none", field_ident);
fns.push(quote! {
#[doc = concat!(" Set the value of `", stringify!(#field_ident), "` to `None`.")]
pub fn #function_ident(mut self) -> Self {
self.#field_ident = None;
self
}
});
}
}
} else {
return ParseError::new(
span,
"#[derive(Builder)] is only defined for structs, not for enums or unions!",
)
.to_compile_error()
.into();
}

let implementation = quote! {
#[automatically_derived]
impl #ident {
#(#fns)*
}
};

implementation.into()
}

// https://stackoverflow.com/a/56264023
fn extract_type_path(ty: &Type) -> Option<&Path> {
match *ty {
Type::Path(ref typepath) if typepath.qself.is_none() => Some(&typepath.path),
_ => None,
}
}

// https://stackoverflow.com/a/56264023
fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| {
acc.push_str(&v.ident.to_string());
acc.push('|');
acc
});

vec!["Option|", "std|option|Option|", "core|option|Option|"]
.into_iter()
.find(|s| idents_of_path == *s)
.and_then(|_| path.segments.last())
}
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The timer drivers `OneShotTimer` & `PeriodicTimer` now have a `Mode` parameter and type erase the underlying driver by default (#2586)
- `timer::Timer` has new trait requirements of `Into<AnyTimer>`, `'static` and `InterruptConfigurable` (#2586)
- `systimer::etm::Event` no longer borrows the alarm indefinitely (#2586)
- A number of public enums and structs in the I2C, SPI, and UART drivers have been marked with `#[non_exhaustive]` (#2614)

### Fixed

Expand Down
6 changes: 1 addition & 5 deletions esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@
//!
//! let mut spi = Spi::new_with_config(
//! peripherals.SPI2,
//! Config {
//! frequency: 100.kHz(),
//! mode: SpiMode::Mode0,
//! ..Config::default()
//! },
//! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0)
//! )
//! .with_sck(sclk)
//! .with_mosi(mosi)
Expand Down
9 changes: 7 additions & 2 deletions esp-hal/src/i2c/master/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const MAX_ITERATIONS: u32 = 1_000_000;
/// I2C-specific transmission errors
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// The transmission exceeded the FIFO size.
ExceedingFifo,
Expand All @@ -106,14 +107,15 @@ pub enum Error {
/// I2C-specific configuration errors
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConfigError {}

#[derive(PartialEq)]
// This enum is used to keep track of the last/next operation that was/will be
// performed in an embedded-hal(-async) I2c::transaction. It is used to
// determine whether a START condition should be issued at the start of the
// current operation and whether a read needs an ack or a nack for the final
// byte.
#[derive(PartialEq)]
enum OpKind {
Write,
Read,
Expand Down Expand Up @@ -217,6 +219,7 @@ enum Ack {
Ack = 0,
Nack = 1,
}

impl From<u32> for Ack {
fn from(ack: u32) -> Self {
match ack {
Expand All @@ -226,15 +229,17 @@ impl From<u32> for Ack {
}
}
}

impl From<Ack> for u32 {
fn from(ack: Ack) -> u32 {
ack as u32
}
}

/// I2C driver configuration
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
/// The I2C clock frequency.
pub frequency: HertzU32,
Expand Down
10 changes: 4 additions & 6 deletions esp-hal/src/spi/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@
//!
//! let mut spi = Spi::new_with_config(
//! peripherals.SPI2,
//! Config {
//! frequency: 100.kHz(),
//! mode: SpiMode::Mode0,
//! ..Config::default()
//! },
//! Config::default().with_frequency(100.kHz()).with_mode(SpiMode::Mode0)
//! )
//! .with_sck(sclk)
//! .with_mosi(mosi)
Expand Down Expand Up @@ -93,6 +89,7 @@ use crate::{
#[cfg(gdma)]
#[derive(Debug, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum SpiInterrupt {
/// Indicates that the SPI transaction has completed successfully.
///
Expand Down Expand Up @@ -423,8 +420,9 @@ impl Address {
}

/// SPI peripheral configuration
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
/// SPI clock frequency
pub frequency: HertzU32,
Expand Down
1 change: 1 addition & 0 deletions esp-hal/src/spi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod slave;
/// SPI errors
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
/// Error occurred due to a DMA-related issue.
DmaError(DmaError),
Expand Down
Loading

0 comments on commit 8397711

Please sign in to comment.