aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib.rs
AgeCommit message (Collapse)Author
2018-10-29Remove audio playing codeTeddy Wing
Remove code that was commented in 44f6a2d5544e3ad49e5e3c52167c045aed1d56b2. Since we weren't able to correctly link to the Core Audio framework from the Objective-C code using this Rust audio code, get rid of it. Instead, we play audio in the Objective-C application.
2018-10-29Turn off audio handling with 'rodio'Teddy Wing
I was getting the following error when building the Objective-C project with the latest audio playing code: ld: warning: object file (.../DomeKey/lib/dome-key-map/target/debug/libdome_key_map.a(minimp3.o)) was built for newer OSX version (10.12) than being linked (10.7) ld: warning: object file (.../DomeKey/lib/dome-key-map/target/debug/libdome_key_map.a(util_helpers.o)) was built for newer OSX version (10.12) than being linked (10.7) ld: warning: object file (.../DomeKey/lib/dome-key-map/target/debug/libdome_key_map.a(aesni_helpers.o)) was built for newer OSX version (10.12) than being linked (10.7) Undefined symbols for architecture x86_64: "_AudioComponentFindNext", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioComponentInstanceNew", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioObjectGetPropertyData", referenced from: cpal::cpal_impl::Device::name::h96cf527e96e03bec in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::Device::supported_formats::h9019a8aeda6b1a55 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::Device::default_format::h1cffda96edcf1b5f in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::EventLoop::build_input_stream::rate_listener::he669322d080b4fd0 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::enumerate::audio_devices::h35df9cd3ad61905a in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) cpal::cpal_impl::enumerate::default_input_device::h4d38066593597777 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) cpal::cpal_impl::enumerate::default_output_device::hfb207f3766c759d9 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) ... "_AudioObjectGetPropertyDataSize", referenced from: cpal::cpal_impl::Device::supported_formats::h9019a8aeda6b1a55 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::enumerate::audio_devices::h35df9cd3ad61905a in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) "_AudioOutputUnitStart", referenced from: coreaudio::audio_unit::AudioUnit::start::h0ceab6edb211b577 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioOutputUnitStop", referenced from: coreaudio::audio_unit::AudioUnit::stop::h2929fdfd118ee7be in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioUnitGetProperty", referenced from: coreaudio::audio_unit::get_property::h58ff3c44f7ccc617 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::get_property::habcd9010fa8dc136 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioUnitInitialize", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioUnitSetProperty", referenced from: coreaudio::audio_unit::set_property::h5e5a6451ac49ed54 in libdome_key_map.a(rodio-d252ad0025b836aa.rodio15.rcgu.o) coreaudio::audio_unit::set_property::h86353d3d091c7540 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::set_property::hbaa7f6c599c38fa2 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::set_property::hf450b8b66e374b66 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioUnitUninitialize", referenced from: _$LT$coreaudio..audio_unit..AudioUnit$u20$as$u20$core..ops..drop..Drop$GT$::drop::h0a1df660f851dad4 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) After setting: $ export MACOSX_DEPLOYMENT_TARGET=10.7 in the shell, the error changed to: Undefined symbols for architecture x86_64: "_AudioComponentFindNext", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioComponentInstanceNew", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioObjectGetPropertyData", referenced from: cpal::cpal_impl::Device::name::h96cf527e96e03bec in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::Device::supported_formats::h9019a8aeda6b1a55 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::Device::default_format::h1cffda96edcf1b5f in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::EventLoop::build_input_stream::rate_listener::he669322d080b4fd0 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::enumerate::audio_devices::h35df9cd3ad61905a in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) cpal::cpal_impl::enumerate::default_input_device::h4d38066593597777 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) cpal::cpal_impl::enumerate::default_output_device::hfb207f3766c759d9 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) ... "_AudioObjectGetPropertyDataSize", referenced from: cpal::cpal_impl::Device::supported_formats::h9019a8aeda6b1a55 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal0.rcgu.o) cpal::cpal_impl::enumerate::audio_devices::h35df9cd3ad61905a in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal8.rcgu.o) "_AudioOutputUnitStart", referenced from: coreaudio::audio_unit::AudioUnit::start::h0ceab6edb211b577 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioOutputUnitStop", referenced from: coreaudio::audio_unit::AudioUnit::stop::h2929fdfd118ee7be in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioUnitGetProperty", referenced from: coreaudio::audio_unit::get_property::h58ff3c44f7ccc617 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::get_property::habcd9010fa8dc136 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) "_AudioUnitInitialize", referenced from: coreaudio::audio_unit::AudioUnit::new_with_flags::hc9c3029c77a9ba1d in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioUnitSetProperty", referenced from: coreaudio::audio_unit::set_property::h5e5a6451ac49ed54 in libdome_key_map.a(rodio-d252ad0025b836aa.rodio15.rcgu.o) coreaudio::audio_unit::set_property::h86353d3d091c7540 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::set_property::hbaa7f6c599c38fa2 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) coreaudio::audio_unit::set_property::hf450b8b66e374b66 in libdome_key_map.a(cpal-b228ca6a35ab9c25.cpal12.rcgu.o) "_AudioUnitUninitialize", referenced from: _$LT$coreaudio..audio_unit..AudioUnit$u20$as$u20$core..ops..drop..Drop$GT$::drop::h0a1df660f851dad4 in libdome_key_map.a(coreaudio-b5e291d8855015a5.coreaudio3.rcgu.o) ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) Couldn't figure out how to get past this, so decided to move the audio playing into the Objective-C code. It's been something of a theme, where if something doesn't work in one language (like dome_key_event_source_simulator), write it in the other. Here we just comment out the audio-related code so I can work on connecting mode activation and deactivation and audio playing. I'll be completely removing this code soon.
2018-10-28Test of playing an audio fileTeddy Wing
Success! Using the 'rodio' crate to play an audio file. Include the audio file in the binary using `include_bytes!`. This makes it so we get a single self-contained binary. Struggled a bit with getting the reader/array to `Seek`, but finally figured out `Cursor` and got it working. Cool.
2018-10-28Remove unused cratesTeddy Wing
2018-10-27Move non-FFI functions from `ffi.rs` to `map.rs`Teddy Wing
Keep FFI functions separate from the rest.
2018-10-27lib.rs: Remove unneeded `use`Teddy Wing
2018-10-27Rename `cocoa_bridge.rs` to `ffi.rs`Teddy Wing
When I first created the file, it was going to be used to call Cocoa methods using the 'cocoa' crate. It's since turned into a module that contains C functions called by Objective-C code. This new name makes more sense. We'll need to move one or two non-FFI functions outside of this module for better organisation.
2018-10-27Delete unused importsTeddy Wing
2018-10-26Add a `--license` command line argumentTeddy Wing
Add a new argument that allows users to pass in the path to a license file. Doing so should get picked up by the Objective-C code which will copy and validate the license. In order for the Objective-C code to pick up the path string, we add a `license` field to the `Config.Args` struct. The new `license` field is a `*mut c_char`, which means we can't derive Default any more and need to implement a custom `Default` impl for `Args`. Additionally, 'getopts' gives us a Rust string (of course), which needs to be converted into a raw `CString` before we can stick it in the `license` field in `Args`. I previously wrote `dkeprintln` to be able to print an error here at the conversion point between `CString` and raw C string. Since we used `CString#into_raw()`, we need to call `from_raw()` on the pointer to free it, otherwise we end up with a memory leak. This necessitated adding some additional code to `config_free()` to free the `license` field if non-null. In order to access `config.args.license` from within `config_free()`, I also needed to make the `args` and `license` struct fields public, since the free function is in a different module. Instead of making only those two fields public, I decided to make them all public, as all the fields are technically accessible from C code, and are needed by that C code. Finally, move the module declaration of `prefix_println` to the top of the module list instead of listing it alphabetically, because otherwise any modules declared before it (like `config`) don't get access to its macro. Also add a `#[macro_use]` attribute to the module declaration because we do want to export its macro. (https://stackoverflow.com/questions/26731243/how-do-i-use-a-macro-across-module-files#comment78329437_31749071)
2018-10-25Add `dkeprintln` macroTeddy Wing
This macro functions exactly like `eprintln` while prepending the string "dome-key: error: " to all messages. Allows us to give a uniform prefix to printed rerrors. Used the `println` macro as a reference: https://doc.rust-lang.org/src/std/macros.rs.html#157-162
2018-10-23trial: Implement `do_trial()`Teddy Wing
This function serves as the entry point to all trial functionality. When called, it will try to determine the number of days remaining in the trial. * If the trial period is in progress, the number of days remaining will be printed. * If an error is encountered involving the trial file, or the trial has expired, an error message is printed and the program exits. * Finally, if an unrelated error occurs, print the error without exiting. Change `DateTime<Local>`s to `DateTime<FixedOffset>`s in order to have uniform date types. Otherwise we run into trouble when comparing dates and when querying functions with different date typs. TODO: This fails the tests (unless your local timezone is UTC presumably) because the local time gets stripped of its offset and the correct offset gets reapplied, incorrectly shifting the time. Figure out a way to have uniform datetime types without muffing up the timezone offsets.
2018-10-22trial: Encrypt and decrypt timestampTeddy Wing
Add functions to encrypt and decrypt a timestamp. We'll be using those functions to write the encrypted timestamp of the first trial launch date to a file. Decided to go for some light encryption instead of just storing a plain or base64-encoded value. Still need to come up with a key, but that will be stored as a plain string in the binary. Need a way to capture the results so we don't end up panicking. Trouble is, 'magic-crypt' doesn't implement the required `Error` traits, so I can't just slot it into 'error-chain'. Still trying to work out how to deal with that. Also add a function to get the days remaining from now.
2018-10-21Add `trial` module, calculate days remainingTeddy Wing
This new module will contain functions to calculate trial days. Here we implement a function to calculate the days remaining in a trial period. If there are less than 0 days remaining, it returns an error result. Couldn't use 'error-chain' because it was complaining about `Debug` and `PartialEq` not being defined. Don't know if it's possible to implement those traits on 'error-chain''s ErrorKind`, but it was complicated enough that I ended up not bothering. Went with 'quick_error' instead to create a custom error type without having to `impl` all the required traits.
2018-10-20read_config_file(): Update to get the config from `config.toml`Teddy Wing
Use XDG handling from `state_load_map_group()` to do basically the same thing here for reading the config from a `config.toml` file. Use 'error-chain' to handle the `Result`s more easily. No more nested pattern matching, we'll just have to unwrap one `Result` and write the error to stderr in an FFI function.
2018-10-19config: Add a function to read a config file (WIP)Teddy Wing
A new function that will read a TOML config file. Doesn't currently access the filesystem. This commit is mostly supporting code to allow that to work. * Add the 'toml' crate * `parse_args()`: Take a mutable `Config` and return that config instead of creating a new one. The recipe will now be to read the config from a file first and then pass that config to this function to be updated. * Modify `c_parse_args()` to take a `*Config` and mirror the new method signature of `parse_args()`
2018-10-19Config: Add `timeout` fieldTeddy Wing
This new field will be used by the calling Objective-C code as the timeout between multi-button maps. It will be defined in a `config.toml` file in the XDG config directory, with a default if unspecified. That part of the code hasn't been written yet, but we've prepared by deriving Serde `Deserialize` on the `Config` struct in order to deserialise a TOML config file. The latest Serde is v1.0.80, but we can't use it because 'cbindgen' depends on an older version. We had been using 'cbindgen' v0.6.3, which depends on serde v1.0.21. Decided to update 'cbindgen' in order to use a newer version of Serde, but that puts us at v1.0.58 instead of the latest: error: failed to select a version for `serde_derive`. ... required by package `cbindgen v0.6.6` ... which is depended on by `dome-key-map v0.0.1 (file:///Users/tw/Documents/Development/dome-key-map)` versions that meet the requirements `= 1.0.58` are: 1.0.58 all possible versions conflict with previously selected packages. previously selected package `serde_derive v1.0.80` ... which is depended on by `dome-key-map v0.0.1 (file:///Users/tw/Documents/Development/dome-key-map)` failed to select a version for `serde_derive` which could resolve this conflict
2018-10-18Add functions to get a `CGEventFlags` from a slice of `autopilot::Flag`Teddy Wing
These two functions are copied from `autopilot-rs/src/key.rs` at ab7ebc9fd8bc210717c0210aa20335ea9ceed200. I wanted to use the library directly, but they're private functions. I need this code in order to be able to convert the 'autopilot' `Flag` type into `CGEventFlags`. I need a `CGEventFlags` because my media key simulator, `dkess_press_key()` (an FFI function), requires a `CGEventFlags`-typed value. Needed to change the `from` function to a top-level function due to this error: error[E0117]: only traits defined in the current crate can be implemented for arbitrary types --> src/autopilot_internal.rs:213:1 | 213 | impl From<Flag> for CGEventFlags { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate | = note: the impl does not reference any types defined in this crate = note: define and implement a trait or new type instead Bringing the code into the project seemed to be the easiest way to leverage the existing 'autopilot' work.
2018-10-13Try to send media key events using `NSEvent` and `CGevent`Teddy Wing
Segfaults on key_code.rs:184: error: process didn't exit successfully: `.../dome-key-map/target/debug/deps/dome_key_map-0efa5c8428fad354 send_media_key --nocapture` (signal: 11, SIGSEGV: invalid memory reference) shell returned 101 This is my attempt to use first the 'cocoa' and 'core-graphics' crates, then the 'objc' crate (because I hoped that would get around the segfault), to simulate a media key event. Once I got the types right and the code compiling, I couldn't get past segfaults. After struggling with this for over a day and not being able to figure out what on earth is going on, I wrote the exact same thing in Objective-C and it just worked. That's it, I'm done with this. This code is going to be expunged and I'm going to take a C function pointer in the function that runs map actions that will simulate media key presses in real Cocoa/Carbon. Enough of this headache.
2018-10-11Add a function to send media key eventsTeddy Wing
Media keys (rewind, play/pause, fast forward on Mac function keys) are different from normal keys. These use a different API, and sending events to simulate those keys is different. Here's a first stab at getting something working for posting simulated media key events. This will be used for new special keys in mapping actions. Haven't tested this at all yet, just happy that it finally compiles. Follow the two Stack Overflow answers referenced in the comments (from Albert https://stackoverflow.com/users/133374/albert and Nick Sweeting https://stackoverflow.com/users/2156113/nick-sweeting). Add the `core-graphics` crate to give us access to `CGEvent`, `CGKeyCode`, and related types. Also include some commented `CGKeyCode` definitions for future use.
2018-10-05Add a command line option parserTeddy Wing
Parse command line options in this library. Need to figure out how to communicate these over FFI to the Objective-C code.
2018-10-03run_key_action_for_mode(): Extract action code to a new methodTeddy Wing
Move the key simulation and command execution code to a new method so that it can be reused in both top-level map and mode branches. This additionally enables command running from top-level maps (previously it only worked for in-mode maps).
2018-09-30parser: Add `key_code` and `special_key` parsersTeddy Wing
Still need to test them though. They'll be used in conjunction with another new parser, `action_map`, which will parser map-type action strings. `key_code` parses `autopilot::key::KeyCode`s, basically all special keys and modifiers. `special_key` parses key codes inside `<` `>` braces. Change the test to use `easy_parse` to give a clear error message. Add `recursion_limit` to the crate on the advice of the compiler in order for the `choice!` macro in `key_code()` to work properly. Otherwise it spits out a giant error message complaining about `rustc --explain E0277`. If I remove a handful of elements from the `choice!`, it compiles, so it seems there's an upper bound restricted by the recursion limit. Modifier keys are included in a commented section as these are part of the 'autopilot' `KeyCode` enum, but they'll be handled in a separate parser.
2018-09-29Change `parser::Action` to an enumTeddy Wing
It will now be a different type of `Vec` depending on whether it represents a string map or a command. We'll have new parsers to parse the action definition in a more fine-grained way. The `String` variant is just there for temporary backward compatibility while I figure out parsing for the other two. Still need to update the tests for this change.
2018-09-25Add `logger_init()` to init 'stderrlog'Teddy Wing
Trying to see if this works. If I call the init function over FFI and then call functions that use the error macro, will logging work?
2018-09-25state_load_map_group(): Log errors instead of panickingTeddy Wing
This allows us to log the errors without exiting the program.
2018-09-25Add `state_load_map_group()` to get `MapGroup` from config fileTeddy Wing
Currently untested. This function is supposed to get a mapping file from the XDG config directory, parse it, and load it into the state.
2018-09-24run_key_action_for_mode(): Simulate key press on `map` type actionTeddy Wing
For now this is just a test and doesn't handle modifier keys. Just trying to see if 'autopilot' will work.
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-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-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-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-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.