Implement dynamic PromptHandlers by using genericized methods that accept impl PromptHandler #59

Closed
opened 2024-12-19 21:02:55 +00:00 by ryan · 0 comments
Owner
pub trait Handler {
    fn validate(&self, validator: &dyn Fn(String, &mut Option<Box<dyn std::any::Any>>) -> Result<(), Box<dyn std::error::Error>>) -> Result<Box<dyn std::any::Any>, String>;
}

fn validate_typed<V: 'static>(h: &dyn Handler, validator: &dyn Fn(String) -> Result<V, Box<dyn std::error::Error>>) -> Result<Box<V>, String> {
    let any = h.validate(&|string, opt: &mut Option<Box<dyn std::any::Any>>| {
        let result = validator(string)?;
        opt.replace(Box::new(result));
        Ok(())
    });
    match any {
        Ok(v) => {
            let downcasted = v.downcast::<V>().unwrap();
            Ok(downcasted)
        }
        Err(e) => {
            Err(e)
        }
    }
}

pub struct Thing;

impl Handler for Thing {
    fn validate(&self, validator: &dyn Fn(String, &mut Option<Box<dyn std::any::Any>>) -> Result<(), Box<dyn std::error::Error>>) -> Result<Box<dyn std::any::Any>, String> {
        let mut opt = None;
        match validator("hello world".to_string(), &mut opt) {
            Ok(_) => {
                Ok(opt.unwrap())
            }
            Err(e) => {
                Err(e.to_string())
            }
        }
    }
}

fn main() {
    let thing: Box<dyn Handler> = Box::new(Thing);
    dbg!(validate_typed(&*thing, &|i| Ok(i == "hello world")));
}

validate() takes no generic arguments which is why it can be made into a dyn Trait. Then, by using validate_typed, we can ensure type safety. We can unwrap safely with the assumption that no public consumer will use the trait.

Side note: Should we make validate() a member of the trait? I am unsure. It may be reasonable to make it a member of a sealed trait. In the case of Terminal PromptHandlers, we can implement retry functionality, but in the case of something like headless, we should assume there is no retry mechanism, which means the implementation may be different between the two, but that is something that could possibly be worked around by adding a const retries() -> u8.

```rust pub trait Handler { fn validate(&self, validator: &dyn Fn(String, &mut Option<Box<dyn std::any::Any>>) -> Result<(), Box<dyn std::error::Error>>) -> Result<Box<dyn std::any::Any>, String>; } fn validate_typed<V: 'static>(h: &dyn Handler, validator: &dyn Fn(String) -> Result<V, Box<dyn std::error::Error>>) -> Result<Box<V>, String> { let any = h.validate(&|string, opt: &mut Option<Box<dyn std::any::Any>>| { let result = validator(string)?; opt.replace(Box::new(result)); Ok(()) }); match any { Ok(v) => { let downcasted = v.downcast::<V>().unwrap(); Ok(downcasted) } Err(e) => { Err(e) } } } pub struct Thing; impl Handler for Thing { fn validate(&self, validator: &dyn Fn(String, &mut Option<Box<dyn std::any::Any>>) -> Result<(), Box<dyn std::error::Error>>) -> Result<Box<dyn std::any::Any>, String> { let mut opt = None; match validator("hello world".to_string(), &mut opt) { Ok(_) => { Ok(opt.unwrap()) } Err(e) => { Err(e.to_string()) } } } } fn main() { let thing: Box<dyn Handler> = Box::new(Thing); dbg!(validate_typed(&*thing, &|i| Ok(i == "hello world"))); } ``` validate() takes no generic arguments which is why it can be made into a dyn Trait. Then, by using validate_typed, we can ensure type safety. We can unwrap safely with the assumption that no public consumer will use the trait. Side note: Should we make `validate()` a member of the trait? I am unsure. It may be reasonable to make it a member of a sealed trait. In the case of Terminal PromptHandlers, we can implement retry functionality, but in the case of something like headless, we should assume there is no retry mechanism, which means the implementation may be different between the two, but that is something that could possibly be worked around by adding a const retries() -> u8.
ryan closed this issue 2025-01-04 07:22:11 +00:00
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: public/keyfork#59
No description provided.