aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Collapse)Author
2018-08-29c_run_key_action(): Add `mode` argumentTeddy Wing
Add a nullable `mode` argument like `trigger`. Change `trigger` to make it non-nullable again. We can now pass a mode trigger in from FFI and handle it in our Rust function.
2018-08-29c_run_key_action(): Make `trigger` argument nullableTeddy Wing
Actually, now that I think about it, the trigger argument shouldn't be nullable, since one should always be passed in. But this was really more of a test to make sure we could do the same for a new `mode` argument, which will also be a `Trigger`, but which might be null.
2018-08-29run_key_action(): Add `mode` argumentTeddy Wing
An Option `mode` argument. When `Some`, the function should look for `trigger` inside the given mode. Otherwise it will use the normal map scope.
2018-08-28c_run_key_action(): Change signature to take a `Trigger`Teddy Wing
Our new `Trigger` type groups together the two arguments this function used to take into a single type.
2018-08-28cocoa_bridge: Add a `Trigger` struct to use in `run_key_action()`Teddy Wing
This struct will be used as function arguments to the C version of `run_key_action()`. It will enable us to pass in both a trigger and a mode context more concisely.
2018-08-28cbindgen.toml: Prefix enum variants with enum nameTeddy Wing
For better namespacing and local context information. Thank goodness 'cbindgen' supports this.
2018-08-27Makefile: Add target for libraryTeddy Wing
A new Make target to build the Rust library. This facilitates handling of other targets that depend on the library.
2018-08-26c_run_key_action(): Correctly send action string to CTeddy Wing
Using `.as_ptr()` on the `CString` didn't work. The C code wouldn't print anything. Turns out I needed to use `.into_raw()` in order to: > [Consume] the CString and [transfer] ownership of the string to a C > caller. (https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw) This allows us to correctly access and print the string in our C code. Note that we'll still need to free the string using a new Rust function that should be callable from C, otherwise we'll end up with a memory leak.
2018-08-26c_run_key_action(): Get the C code to actually print something outTeddy Wing
Ensure that the C code can really print something sent from the Rust code. Our action string wasn't getting printed out, so I wanted to see it working with a test string. Need to figure out what I have to do to get it to work for real now.
2018-08-26cocoa_bridge(run_key_action): Add newline to end of test map groupTeddy Wing
I keep forgetting to add the newline at the end, darn. Wasted a few minutes of my time, but so cool that it's finally working now! The C program now correctly prints the corresponding map. Here's a diff of print debug statements I used to diagnose the problem: diff --git a/src/cocoa_bridge.rs b/src/cocoa_bridge.rs index fe65533..9690fe4 100644 --- a/src/cocoa_bridge.rs +++ b/src/cocoa_bridge.rs @@ -68,12 +68,14 @@ pub extern "C" fn c_run_key_action( Some(k) => { match k.action { Some(a) => { + println!("all good"); CKeyActionResult { action: a.as_ptr(), kind: &k.kind, } }, None => { + println!("Action null"); CKeyActionResult { action: ptr::null(), kind: &k.kind, @@ -82,6 +84,7 @@ pub extern "C" fn c_run_key_action( } }, None => { + println!("All null"); CKeyActionResult { action: ptr::null(), kind: ptr::null(), @@ -98,11 +101,17 @@ pub extern "C" fn run_key_action( ) -> Option<KeyActionResult> { let sample_maps = "map <up> k map <down> j -map <play><down> works!"; +map <play><down> works! +"; + + println!("{:?}", trigger); + assert!(trigger == &[HeadphoneButton::Play, HeadphoneButton::Down]); // Figure out how to persist this without re-parsing let map_group = MapGroup::parse(sample_maps).unwrap(); + println!("{:?}", map_group.maps); + let map = map_group.maps.get(trigger); let mode = map_group.modes.get(trigger);
2018-08-26Makefile: Always rebuild `includer`Teddy Wing
2018-08-26Link library with a test C programTeddy Wing
Make a test `includer.c` program that includes the Rust library and calls our `c_run_key_action()` to see if it actually works. Currently it doesn't, we get `(null)` printed to stdout. Add a Makefile with the build command for the C program. cbindgen.toml: Remove `KeyActionResult` from exported types, as the `Option` field it contains caused `gcc` to complain. cocoa_bridge.rs: * Comment out all 'cocoa' crate related code as the 'cocoa' code was interfering with GCC compilation as a result of my not linking to Cocoa frameworks. * Add a new test map definition that corresponds with the one we look for in `includer.c`. parser.rs: Add `#[repr(C)]` to `MapKind` because it needs to be exported in the library and generated into our C header file.
2018-08-26Make data types more C-likeTeddy Wing
Add a new `CKeyActionResult` that uses a `char *` instead of an `Option<CString>`. Make our `c_run_key_action()` return this type instead of `KeyActionResult`. Include some code to handle the translation between the two types. Names for now are just temporary so I can get something working. We'll re-think proper names once I've tested this manually and have something somewhat working.
2018-08-26Get `run_key_action` to export correctly to CTeddy Wing
Add a new wrapper function for `run_key_action` that uses C appropriate inputs & outputs and calls into our Rusty `run_key_action`. This new function now correctly gets a header generated for it by 'cbindgen'. Immense thanks to Jake Goulding on the Rust FFI Omnibus for showing me how to pass a slice argument from C: http://jakegoulding.com/rust-ffi-omnibus/slice_arguments/ In order to pass the slice from C, we need to pass a C array and its length to the function. Cool.
2018-08-25Generate a partial C header for our FFI APITeddy Wing
* Add the 'cbindgen' crate to help us auto-generate a C header for our exported FFI enums/structs/functions * Add a `build.rs` to generate the C header using cbindgen * Add a rough config for 'cbindgen' * Export everything from the `cocoa_bridge` crate to include the `KeyActionResult` struct * Commit the C header generated by 'cbindgen' Looks promising. We do, however, have some things to correct. We can't use `Option` in C, for instance, so we'll need to fix that in our `KeyActionResult`. Similarly, we need to rework the `run_key_action()` function to make it accessible as a C interface. Right now it returns an `Option`, which isn't going to work, and I'm not sure if the slice input translates. That (any maybe more) is why it doesn't get generated by 'cbindgen' into the header output file.
2018-08-25Configure for importing from C/FFITeddy Wing
* Add `#[repr(C)]` on `HeadphoneButton` to hopefully be able to use it outside Rust * Add `#[no_mangle]` to `run_key_action()` * Export `run_key_action()` as a public function * Build the crate as a static library (But holy cow, a 19 MB library file?)
2018-08-25run_key_action(): Include action string for `MapKind::Map`Teddy Wing
Couldn't figure out how to make my `Action` string a char slice. At first I changed it to a `CStr` reference, but ran into lifetime errors when trying to use a reference. Instead ended up making it a `CString`. Still need to handle errors. My plan is to pass back an error string in the result struct. Remains to be seen whether this actually works when calling it over FFI, but we have the beginnings of something.
2018-08-25cocoa_bridge: Comment `run_command()` for nowTeddy Wing
Causes an error as `Action` isn't imported.
2018-08-24run_key_action(): Approach for handling actions for different MapKindsTeddy Wing
Work out the essential body of the function, which necessitates looking inside a `MapGroup` and finding a `MapAction` corresponding to the `Trigger` given in the function argument. The function will then perform whatever action it can and return a `KeyActionResult` struct, allowing callers to pick up the baton.
2018-08-24cocoa_bridge: Idea for black box map run functionTeddy Wing
New `run_key_action()` will be called from Objective C. It will: * Get the action for a `Map` mapping and return it, so that the corresponding keys can be pressed in Cocoa/Carbon * Run a `Command` mapping * Somehow manage mode scoping in conjunction with the Objective C side The new `KeyActionResult` allows us to tell Objective C what type of action should be run (`Map`, `Command`, or `Mode`), along with additional information (for now just the action keys in the case of a `Map` mapping). The question is how to hold on to state, keeping the parsed mappings in memory on the Rust side when called over FFI.
2018-08-24cocoa_bridge(parse_mappings): Ideas for filling the ObjC collectionsTeddy Wing
Not working currently. Just committing this because I didn't before and because I'm working in a different direction now.
2018-08-23cocoa_bridge: Add some ideas for how to make the bridge workTeddy Wing
It's feeling like creating Cocoa objects in Rust is going to be a pain. Thinking I might want to make the API a little more black box-y and do more on the Rust side. These are some notes for ideas about how to do that.
2018-08-23cocoa_bridge: Continue outline of Objective C callableTeddy Wing
Add some more structure to the function that will be called from Objective C code. * Give it a name, `parse_mappings` (not very thoroughly considered) * Initialise some Foundation data structures to mirror our Rust ones * Add the beginning of a struct that will be the ObjC version of our `MapGroup` struct, containing `NSDictionary`ies. * Move the `extern crate` into `lib.rs`, I keep forgetting that's where they go. * Add a test that just calls `parse_mappings` to check that the code compiles. parser::MapGroup: Make both its fields public to enable us to access them from `cocoa_bridge`. Used this helpful example as a reference for the Rust Cocoa code: https://github.com/servo/core-foundation-rs/blob/c99c05c/cocoa/examples/hello_world.rs
2018-08-23Rename my `cocoa` module to `cocoa_bridge`Teddy Wing
My `cocoa` module was conflicting with the `cocoa` crate. Didn't think about that. Rename it to fix the conflict.
2018-08-22Add `MapGroup::parse()`; Start Cocoa functionTeddy Wing
Add a new `cocoa` module which will hold code to be called from Objective-C. Currently contains a sketch of a function that will be called via FFI from Objective-C. Haven't thought of a name for it yet. For now contains a sample map definition "file" in a hard-coded string. Tells `MapGroup` to parse the map definitions. Will try to create some Cocoa objects in the `unsafe` block. `MapGroup`: Add `parse()` method. Given a map definition string, this new function will parse the string into a `MapGroup` and return the result. It will serve as the entry point to parsing DomeKey map definitions. Thanks to these references for Combine parser error handling: - https://github.com/ordian/toml_edit/blob/b02bc3/src/parser/document.rs - https://github.com/ordian/toml_edit/blob/ee6895f/src/parser/errors.rs - https://github.com/Marwes/combine/blob/54bcfed/examples/ini.rs
2018-08-22Move all code in `lib.rs` to `parser.rs`Teddy Wing
Put our parsing code in its own module. We'll be creating a new `cocoa` module and doing this allows us to keep `lib` free as a module container.
2018-08-22Cargo.toml: Add 'cocoa' crateTeddy Wing
This will enable us to use Foundation data structures to hopefully export into a Cocoa project.
2018-08-20Parse whole map file stringsTeddy Wing
Very important: file must be terminated by a newline in order to parse correctly because of the way `Action`s are parsed. Will need to correct this to work for EOF also. We can now parse a whole file of map and mode definitions!
2018-08-20definitions(): Get rid of failed implementation attemptsTeddy Wing
Clear out the commented failed attempts from e96b453f33345ba08c8150192157077f310f1b01.
2018-08-20Parse multiple maps and modes intermixedTeddy Wing
Create a new `Definition` item type that groups `Map` and `Mode` into a single type that we can expect on a parser. This allows us to make a parser that can parse both modes & maps. A bunch of failed attempts, but finally managed to get something working, yay!
2018-08-20Try to parse a `MapGroup`Teddy Wing
No dice. Getting this error: thread 'tests::map_group_parses_a_whole_map_file_string' panicked at 'assertion failed: `(left == right)` left: `Err(Errors { position: PointerOffset(4376730950), errors: [Unexpected(Token('a')), Expected(Borrowed("mode"))] })`, right: `Ok(MapGroup { maps: {[Down]: MapAction { action: "/bin/echo nothing", kind: Command }, [Play]: MapAction { action: "/usr/bin/say hello", kind: Command }}, modes: {[Down, Up]: {[Play]: MapAction { action: "p", kind: Map }}} })`', src/lib.rs:421:9 I need a way to parse modes and maps in any order. Here the code is trying to parse first modes, then maps.
2018-08-19Parse modesTeddy Wing
Add a new `Mode` type to parse into, similar to the `Map` type we made recently. Parse a mode definition, which contains a `MapCollection`. Modes cannot contain other modes, so we don't bother worrying about recursive parsers.
2018-08-19map_collection(): Handle whitespace between map definitionsTeddy Wing
Add support for blank, whitespace, and comment lines between and around map definition lines. Fiddled with the existing functions I had for this, but finally managed to get it working, still using Combine's `examples/ini.rs` as a reference.
2018-08-17Parse a single-element `MapCollection`Teddy Wing
Managed to get `MapCollection` parsing working, but only for a single mapping for now. Need to figure out what I'm doing wrong. Here's the result of my test: thread 'tests::map_collection_parses_maps' panicked at 'assertion failed: `(left == right)` left: `Ok({[Up, Down]: MapAction { action: "test", kind: Map }})`, right: `Ok({[Down]: MapAction { action: "/usr/bin/say \'hello\'", kind: Command }, [Up, Down]: MapAction { action: "test", kind: Map }})`', src/lib.rs:265:9 note: Run with `RUST_BACKTRACE=1` for a backtrace. test tests::map_collection_parses_maps ... FAILED At first the assertion in the test wasn't working, failing to compile with this error: error[E0369]: binary operation `==` cannot be applied to type `std::collections::HashMap<std::vec::Vec<HeadphoneButton>, MapAction>` --> src/lib.rs:266:9 | 266 | assert_eq!(result.unwrap(), expected); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: an implementation of `std::cmp::PartialEq` might be missing for `std::collections::HashMap<std::vec::Vec<HeadphoneButton>, MapAction>` = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) Finally realised that I needed to derive `PartialEq` on `MapAction` to make it work. Added a comment parser, based on the `examples/ini.rs` example from Combine: fn whitespace<I>() -> impl Parser<Input = I> where I: Stream<Item = char>, I::Error: ParseError<I::Item, I::Range, I::Position>, { let comment = (token(';'), skip_many(satisfy(|c| c != '\n'))).map(|_| ()); // Wrap the `spaces().or(comment)` in `skip_many` so that it skips alternating whitespace and // comments skip_many(skip_many1(space()).or(comment)) } Tried writing a test for `comment()`, but couldn't get it to work, presumably because `comment()` doesn't have any output: #[test] fn comment_parses_line_comments() { let text = "# This is a comment "; let result = comment().parse(text).map(|t| t.0); // print!("{:?}", result); assert_eq!(result, Ok(())); }
2018-08-16Parse single map definitionsTeddy Wing
Parse a map definition line into a `Map`. Got so stuck on this error: error[E0277]: the trait bound `(): combine::Parser` is not satisfied --> src/lib.rs:107:16 | 107 | fn map<I>() -> impl Parser<Input = I, Output = Map> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `combine::Parser` is not implemented for `()` | = note: the return type of a function must have a statically known size error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. Turns out the problem was that I had put a semicolon at the end of the expression in `map()`. Geez. Took hours to finally see that.
2018-08-16New `Map` struct; Rename existing `Map` struct to `MapAction`Teddy Wing
Add a new `Map` type that includes a field for `trigger`. This allows us to parse a single map definition line into a single struct, making parsing more granular than going directly to `MapCollection`. Rename the existing `Map` type to `MapAction` to support this change.
2018-08-16Try to parse a `MapCollection`Teddy Wing
Can't get this to work. Right now I'm getting this error when running my test: Err(Errors { position: PointerOffset(4522385157), errors: [Unexpected(Token('m')), Expected(Borrowed("lf newline")), Expected(Borrowed("whitespace")), Expected(Borrowed("tab"))] }) It's supposed to parse a set of map lines, including whitespace and comments, into a `MapCollection`. Right now I'm thinking I should split this up more and make a more fine-grained parser for just a single map line, then build up to the collection from there.
2018-08-09Add parser for map actionTeddy Wing
For now just parses a string to a newline. In the future this will have to be more complicated to deal with `<...>`-style special characters and escaping (darn).
2018-08-09Ignore case when parsing `HeadphoneButton`Teddy Wing
Should work with `<play>`, `<Play>`, `<PLAY>`, `<PlAy>`.
2018-08-09Parse a `Trigger`, or sequence of `HeadphoneButton`sTeddy Wing
So cool! I love parser combinators.
2018-08-09Parse headphone buttons (`<play>`, `<up>`, `<down>`)Teddy Wing
Parses a headphone button into the `HeadphoneButton` enum.
2018-08-09map_kind(): Remove old commented codeTeddy Wing
These were early tests to figure out how to parse my tokens.
2018-08-09Parser for `map` and `cmd` keywordsTeddy Wing
This parser reads those tokens and parses them to `MapKind` types. Took a bit of struggling and researching, but it's so cool to see it working now!
2018-08-08Add `MapCollection` type aliasTeddy Wing
Further cleans up the `HashMap` definition in `DKMapGroup`.
2018-08-08Make `Trigger` a list of headphone buttonsTeddy Wing
Cleans up the `HashMap` definition in `DKMapGroup`.
2018-08-08Make `Trigger` an enum instead of a `String`Teddy Wing
`Trigger` can only be one of three values, so it should really be an enum. But of course, the actual trigger is a set of these buttons, so we want a list of trigger buttons as the map hash keys.
2018-08-08Make map `kind` an enum; Fix `modes` typeTeddy Wing
The `kind` has a specific set of things that it can be. It didn't need to be a `String`. Fix `DKMapGroup.modes` to ensure that values are map hashes. Remove `trigger` from `Map` because it didn't make sense to include it in the `HashMap` value if it's the hash key.
2018-08-08Add some types to use for storing the parsed resultTeddy Wing
Imagining how the data should be stored after parsing the map file.
2018-08-08Cargo.toml: Add 'combine'Teddy Wing
For parsing.
2018-08-08Initialise new project with CargoTeddy Wing
$ cargo init --lib $ rustc --version rustc 1.28.0 (9634041f0 2018-07-30)