This one is a lot of boilerplate.
While trying to move graph generation to Rust (failed), I just so happened to reinforce the type-level abstractions. Didn't want to just trash it, so I decided to clean it up and let you decide whether we want it or not. I'll be happy either way.
There is a lot of very rust-y type level magic in this PR. I know this project is your first Rust, so I want you to really understand it first before merging. Here's what this PR basically does:
Creates a distinct type for every filter type. You can see all of them in FilterConfig enum. These types are basically their structure type (either FreqQualFilter, FreqQualGainFilter or CustomIIRFilter) plus a marker.
Each type has their own FilterType on the type system level. Pattern matching is outdated - we're encoding it directly on a type now with trait LowLevelFilter. Trait Discriminant then transforms FilterType into u8 as before. Check function read_filter - looks cool.
Each structure type implements StructuralPayload, which basically just says how we encode them into bytes. We get StructuralPayload for filter types of respective structure types for free.
I added some nice little things like Into<FilterConfig> for *Filter.
The why is interesting here. Type-level magic makes impossible for things to do not what you want. Like, if you refactor stuff and forget to implement a trait for a type or something - compiler will scream at you until you're done. And then it will probably work first try.
Disadvantage is, it is a bit hard to understand. It is also a bit hard to fully comprehend. Good thing is compiler will scream at you regardless, but feels kinda cheap anyway.
In short, you decide whether you'll want to work with this code. What we already have is fine, I just didn't want to trash what I wrote for the other thing.
This one is a lot of boilerplate. While trying to move graph generation to Rust (failed), I just so happened to reinforce the type-level abstractions. Didn't want to just trash it, so I decided to clean it up and let you decide whether we want it or not. I'll be happy either way.
There is a lot of very rust-y type level magic in this PR. I know this project is your first Rust, so I want you to really understand it first before merging. Here's what this PR basically does:
FilterConfig
enum. These types are basically their structure type (eitherFreqQualFilter
,FreqQualGainFilter
orCustomIIRFilter
) plus a marker.FilterType
on the type system level. Pattern matching is outdated - we're encoding it directly on a type now with traitLowLevelFilter
. Trait Discriminant then transformsFilterType
into u8 as before. Check functionread_filter
- looks cool.StructuralPayload
, which basically just says how we encode them into bytes. We getStructuralPayload
for filter types of respective structure types for free.Into<FilterConfig> for *Filter
.The why is interesting here. Type-level magic makes impossible for things to do not what you want. Like, if you refactor stuff and forget to implement a trait for a type or something - compiler will scream at you until you're done. And then it will probably work first try.
Disadvantage is, it is a bit hard to understand. It is also a bit hard to fully comprehend. Good thing is compiler will scream at you regardless, but feels kinda cheap anyway.
In short, you decide whether you'll want to work with this code. What we already have is fine, I just didn't want to trash what I wrote for the other thing.