Skip to content
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

HList madness #12

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft

HList madness #12

wants to merge 27 commits into from

Conversation

Defman
Copy link
Member

@Defman Defman commented May 24, 2020

Currently, it works, but the parse tree has to be recreated each time, which is not ideal.

let mut n = 45;

{
    let command = literal("hello").and(literal("world")).exec(|n: &mut i32, ()| {
        *n = 42;
    });

    let res = command.parse(&mut "hello world".into());
    if let Some((command,)) = res {
        command.call(&mut n);
    }
}

assert_eq!(n, 42);

Still, some work left, need some way to track the depth and give an appropriate error message accordingly.

Might want swap fn out for Func and do some Combine such that the closure signature is a bit cleaner.

Copy link
Member

@caelunshun caelunshun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

My thoughts: if we remove the lifetime parameters on command structs as per my comment below, it should be possible to cache the root Command. With this done, we should implement some sort of acceleration structure (the current implementation does a linear search on all commands).

src/command/exec.rs Outdated Show resolved Hide resolved
@Defman
Copy link
Member Author

Defman commented May 25, 2020

Still some issues left, one of the bigger one being

    F: for<'a> Func<
        <<<(&'a mut C,) as Tuple>::HList as Combine<<P::Extract as Tuple>::HList>>::Output as HList>::Tuple
    >,

Such that we can use

literal("foo").and(param()).exec(|ctx, n: i32| *ctx += n);

@Defman
Copy link
Member Author

Defman commented May 25, 2020

Damn ugly tubofish... for some reason it cannot figure out where C starts and ends on its own :/

let root = literal("hello")
    .and(literal("world"))
    .exec::<_, (_,)>(|n: &mut i32| *n = 42);

let mut n = 45;

if let Some((command,)) = root.parse(&mut "hello world".into()) {
    command.call((&mut n,));
}

assert_eq!(n, 42);

let command = root.parse(&mut "bar".into());
assert!(command.is_none());

One way of getting around this, is not to have C: Tuple but instead (C,): Tuple.

@Defman
Copy link
Member Author

Defman commented May 25, 2020

Alternatively, we can use a mapping, that maps "Self::Extracted -> T"

let root = literal("hello")
    .and(literal("world"))
    .and(param())
    .map(|a: i32| move |n: &mut i32| *n += a);

let mut n = 45;

if let Some((command,)) = root.parse(&mut "hello world -3".into()) {
    command(&mut n)
}

Async commands could be handled as

let root = literal("foo")
    .and(param())
    .map(|a: i32| move |n: i32| async move { n + a });

if let Some((command,)) = root.parse(&mut "foo 10".into()) {
    let res = smol::run(command(0));
    assert_eq!(res, 10)
}

We cant use &mut State for async commands, but we could have them take ownership and return the state as the result of the future.

@Defman
Copy link
Member Author

Defman commented May 26, 2020

There's still a few issue remaining.

  1. Error handling and suggestions
  2. Guards / Providers

Error handling and suggestions

One way of handling suggestions is to "parse" the input and suggest have the errors contain the suggestions. This, however, would require some special handling And and Or. And could be handle by some trait requirement of nested tuples and Or could be handle by "remaining length" of the input to determine the best path.

Alternatively to the using error handling as suggestions, we could have a Suggestions trait which is implemented for Literal, Parm, and etc. It would also have to be implemented for And<T, U> and Either<T, U> where T: Suggestions, U: Suggestions.

Guards / Providers

It would be nice to have some sort of guards and or providers.

literal("tp").then(param()).map(|pos: Position| move |state: State, _: Permission<{"tp"}>| { ... });
literal("tp").then(param()).map(|pos: Position| move permission("tp", |state: State| { ... }));
literal("tp").then(param()).guard(permission("tp")).map(|pos: Position| move |state: State| { ... });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants