-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Store lifecycle hooks dynamically #162
Conversation
13bae45
to
f8b95e8
Compare
src/hooks.rs
Outdated
fn supported_when(&self) -> Vec<When> { | ||
match self { | ||
LifecycleEvent::Test => vec![When::During], | ||
LifecycleEvent::Startup | LifecycleEvent::Reload | LifecycleEvent::Restart => { | ||
vec![When::Before, When::After] | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rather than check at runtime that we don't have an invalid combination of LifecycleEvent
and When
we should enforce that statically by combining them into a single data structure using something like this:
pub enum Bounded {
Before,
After,
}
pub enum LifecycleEvent {
Test,
StartUp{ when: When },
Reload{ when: When },
Restart{ when: When },
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did consider this, but it gets kind of clumsy; for one, I'd have to add an explicit list of each possible event:
fn events() -> Vec<LifecycleEvent> {
vec![
LifecycleEvent::Test,
LifecycleEvent::StartUp(When::Before),
LifecycleEvent::StartUp(When::After),
// ...
]
}
With that approach, when I add a new lifecycle event, nothing will tell me to add the new event to the list, and I also have to make sure to include every possible variant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't that be an issue even for the existing approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
supported_when
(the function you highlighted) matches on a value of type LifecycleEvent
. If you added another event, the pattern match would become non-exhaustive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I have a proc macro that generates a function which iterates over all the enum variants, which I can only use if they don't contain data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is true that in that one case you might lose exhaustiveness, but on the balance I feel like you gain better safety/exhaustiveness by making invalid states unrepresentable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider that the impetus of this system is that I actually already forgot to fill in new entries in the old lifecycle hooks list! This system prevents that. If there's a way to implement this data type that makes invalid states unrepresentable and doesn't require me to explicitly list all the valid states, I'll gladly implement it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you clarify which list you're referring to? I understand what you're asking for in the abstract but couldn't place where in each pull request this corresponds to
src/hooks.rs
Outdated
} | ||
|
||
impl LifecycleEvent { | ||
fn supported_when(&self) -> Vec<When> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn supported_when(&self) -> Vec<When> { | |
fn supported_when(&self) -> HashSet<When> { |
src/hooks.rs
Outdated
} | ||
} | ||
|
||
fn supported_kind(&self, when: When) -> Vec<CommandKind> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn supported_kind(&self, when: When) -> Vec<CommandKind> { | |
fn supported_kind(&self, when: When) -> HashSet<CommandKind> { |
impl<C> Hook<C> { | ||
fn with_command<C2>(&self, command: C2) -> Hook<C2> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the C2
necessary? C
isn't constrained in any way, so I think it would always unify with whatever the type of command
is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, because this lets you change the generic type (i.e. it lets you go from a Hook<CommandKind>
to a Hook<Command>
). That's also why I can't use the ..self
record update syntax here — the types need to match exactly.
This is 100% overkill but You could replace In this case it's so unnecessary, but the |
f8b95e8
to
2e5458c
Compare
This converts the lifecycle hook arguments (like `--before-startup-shell`) to be generated and stored dyanmically. This makes running them and querying them easier.
2e5458c
to
a1335c4
Compare
Instead of having a field for each event (
before_restart_ghci
,test_ghci
,after_startup_shell
, etc.), we generate and store the arguments dynamically. This simplifies the running of hooks and will hopefully make it easier to extend the lifecycle hooks in the future.