├── .gitignore ├── src ├── input │ └── mod.rs ├── output │ └── mod.rs ├── mock.rs └── lib.rs ├── .github └── workflows │ └── rust.yml ├── Cargo.toml ├── LICENSE-MIT ├── tests ├── pin_into_switch.rs ├── input.rs └── output.rs ├── README.md ├── .vscode └── launch.json ├── adr └── use-phantom-data-to-reduce-duplication.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /src/input/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ActiveHigh, ActiveLow, InputSwitch, Switch}; 2 | use embedded_hal::digital::v2::InputPin; 3 | 4 | impl InputSwitch for Switch { 5 | type Error = ::Error; 6 | 7 | fn is_active(&self) -> Result { 8 | self.pin.is_high() 9 | } 10 | } 11 | 12 | impl InputSwitch for Switch { 13 | type Error = ::Error; 14 | 15 | fn is_active(&self) -> Result { 16 | self.pin.is_low() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | rust: 18 | - stable 19 | - 1.31.0 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Build 23 | run: cargo build --verbose 24 | - name: Run tests 25 | run: cargo test --verbose 26 | - name: Install ARM toolchain 27 | run: rustup target add thumbv7em-none-eabihf 28 | - name: Build no_std 29 | run: cargo build --target thumbv7em-none-eabihf 30 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "switch-hal" 3 | version = "0.4.0" 4 | authors = ["Christopher J. McClellan "] 5 | edition = "2018" 6 | description = "HAL and basic implementations for input and output switches (buttons, switches, leds, transistors)" 7 | repository = "https://github.com/rubberduck203/switch-hal" 8 | documentation = "https://docs.rs/crate/switch-hal" 9 | categories = ["embedded", "hardware-support", "no-std"] 10 | keywords = ["embedded-hal", "embedded-hal-driver", "led", "button"] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README.md" 13 | exclude = [ 14 | ".vscode/*", 15 | ] 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies.embedded-hal] 20 | version = "0.2.5" 21 | features = [ "unproven" ] -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Christopher McClellan 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /tests/pin_into_switch.rs: -------------------------------------------------------------------------------- 1 | use switch_hal::mock::{Pin, State}; 2 | use switch_hal::IntoSwitch; 3 | 4 | mod output_pin { 5 | use super::*; 6 | 7 | use embedded_hal::digital::v2::InputPin; 8 | use switch_hal::OutputSwitch; 9 | 10 | #[test] 11 | fn active_high() { 12 | let pin = Pin::new(); 13 | let mut switch = pin.into_active_high_switch(); 14 | switch.on().unwrap(); 15 | 16 | let pin = switch.into_pin(); 17 | assert_eq!(true, pin.is_high().unwrap()); 18 | } 19 | 20 | #[test] 21 | fn active_low() { 22 | let pin = Pin::new(); 23 | let mut switch = pin.into_active_low_switch(); 24 | switch.on().unwrap(); 25 | 26 | let pin = switch.into_pin(); 27 | assert_eq!(true, pin.is_low().unwrap()); 28 | } 29 | } 30 | 31 | mod input_pin { 32 | use super::*; 33 | use switch_hal::InputSwitch; 34 | 35 | #[test] 36 | fn active_high() { 37 | let pin = Pin::with_state(State::High); 38 | let switch = pin.into_active_high_switch(); 39 | assert_eq!(true, switch.is_active().unwrap()); 40 | } 41 | 42 | #[test] 43 | fn active_low() { 44 | let pin = Pin::with_state(State::Low); 45 | let switch = pin.into_active_low_switch(); 46 | assert_eq!(true, switch.is_active().unwrap()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/output/mod.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::digital::v2::{OutputPin, StatefulOutputPin, ToggleableOutputPin}; 2 | 3 | use crate::{ActiveHigh, ActiveLow, OutputSwitch, Switch, StatefulOutputSwitch, ToggleableOutputSwitch}; 4 | 5 | impl OutputSwitch for Switch { 6 | type Error = ::Error; 7 | 8 | fn on(&mut self) -> Result<(), Self::Error> { 9 | self.pin.set_high() 10 | } 11 | 12 | fn off(&mut self) -> Result<(), Self::Error> { 13 | self.pin.set_low() 14 | } 15 | } 16 | 17 | impl OutputSwitch for Switch { 18 | type Error = ::Error; 19 | 20 | fn on(&mut self) -> Result<(), Self::Error> { 21 | self.pin.set_low() 22 | } 23 | 24 | fn off(&mut self) -> Result<(), Self::Error> { 25 | self.pin.set_high() 26 | } 27 | } 28 | 29 | impl ToggleableOutputSwitch 30 | for Switch 31 | { 32 | type Error = ::Error; 33 | 34 | fn toggle(&mut self) -> Result<(), Self::Error> { 35 | self.pin.toggle() 36 | } 37 | } 38 | 39 | impl StatefulOutputSwitch 40 | for Switch 41 | { 42 | type Error = ::Error; 43 | 44 | fn is_on(&mut self) -> Result { 45 | self.pin.is_set_low() 46 | } 47 | 48 | fn is_off(&mut self) -> Result { 49 | self.pin.is_set_high() 50 | } 51 | } 52 | 53 | impl StatefulOutputSwitch 54 | for Switch 55 | { 56 | type Error = ::Error; 57 | 58 | fn is_on(&mut self) -> Result { 59 | self.pin.is_set_high() 60 | } 61 | 62 | fn is_off(&mut self) -> Result { 63 | self.pin.is_set_low() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/input.rs: -------------------------------------------------------------------------------- 1 | extern crate switch_hal; 2 | 3 | use switch_hal::mock::{Pin, State}; 4 | use switch_hal::{InputSwitch, Switch}; 5 | 6 | mod active_high_switch { 7 | use super::*; 8 | 9 | use switch_hal::ActiveHigh; 10 | 11 | mod is_active { 12 | use super::*; 13 | 14 | #[test] 15 | fn true_when_pin_high() { 16 | let pin = Pin::with_state(State::High); 17 | 18 | let button = Switch::<_, ActiveHigh>::new(pin); 19 | assert_eq!(true, button.is_active().unwrap()); 20 | } 21 | 22 | #[test] 23 | fn false_when_pin_low() { 24 | let pin = Pin::with_state(State::Low); 25 | 26 | let button = Switch::<_, ActiveHigh>::new(pin); 27 | assert_eq!(false, button.is_active().unwrap()); 28 | } 29 | 30 | #[test] 31 | fn propagates_errors_from_pin() { 32 | let pin = Pin::new(); 33 | let button = Switch::<_, ActiveHigh>::new(pin); 34 | button.is_active().expect_err("Expected uninitialized error"); 35 | } 36 | } 37 | } 38 | 39 | mod active_low_switch { 40 | use super::*; 41 | 42 | use switch_hal::ActiveLow; 43 | 44 | mod is_active { 45 | use super::*; 46 | 47 | #[test] 48 | fn false_when_pin_high() { 49 | let pin = Pin::with_state(State::High); 50 | 51 | let button = Switch::<_, ActiveLow>::new(pin); 52 | assert_eq!(false, button.is_active().unwrap()); 53 | } 54 | 55 | #[test] 56 | fn true_when_pin_low() { 57 | let pin = Pin::with_state(State::Low); 58 | 59 | let button = Switch::<_, ActiveLow>::new(pin); 60 | assert_eq!(true, button.is_active().unwrap()); 61 | } 62 | 63 | #[test] 64 | fn propagates_errors_from_pin() { 65 | let pin = Pin::new(); 66 | let button = Switch::<_, ActiveLow>::new(pin); 67 | button.is_active().expect_err("Expected uninitialized error"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # switch-hal 2 | 3 | ![Rust](https://github.com/rubberduck203/switch-hal/workflows/Rust/badge.svg) 4 | [![crates.io](https://img.shields.io/crates/d/switch-hal.svg)](https://crates.io/crates/switch-hal) 5 | [![crates.io](https://img.shields.io/crates/v/switch-hal.svg)](https://crates.io/crates/switch-hal) 6 | [![docs.rs](https://docs.rs/switch-hal/badge.svg)](https://docs.rs/switch-hal) 7 | 8 | Switch-HAL is a `no_std` embedded Rust library for working with buttons, switches, LEDs, and transistors. 9 | Basically, anything that acts like a switch, whether an input or output. 10 | 11 | It is both a driver that uses the `embedded-hal::digital` traits and is an abstraction in it's own right. 12 | It provides a simple, zero-cost, abstraction to clarify the _intent_ of your application code. 13 | 14 | ## Why Switch-HAL? Why not just use raw GPIO? 15 | 16 | Did you mean to drive that line high? 17 | Or did you mean to _turn that LED off_? 18 | Wait a second... is that LED active _high_? 19 | Where's the schematic? 20 | Okay... cathode is wired to the input line... that means it's active low. 21 | 22 | Now repeat this every place in your code where you need to turn that LED on or off. 23 | What happens when the hardware changes? 24 | Using the raw GPIO to set pins high and low will have you making changes all over your code base. 25 | 26 | Wouldn't it be nicer if you only had to think about that once, when you initialize your application, 27 | and from then on out, simply called `led.on()` or `led.off()`. 28 | Having an abstraction at the proper level reduces cognitive load. 29 | Specifying whether a simple peripheral is active high or low in a single place in your application reduces the maintenance burden. 30 | 31 | ## Documentation 32 | 33 | https://docs.rs/crate/switch-hal 34 | 35 | or build a local copy 36 | 37 | ```sh 38 | cargo docs 39 | ``` 40 | 41 | and open `target/doc/switch_hal/index.html` in your browser. 42 | 43 | ## License 44 | 45 | Licensed under either of 46 | 47 | - Apache License, Version 2.0 ([LICENSE-APACHE](./LICENSE-APACHE) or 48 | http://www.apache.org/licenses/LICENSE-2.0) 49 | - MIT license ([LICENSE-MIT](./LICENSE-MIT) or http://opensource.org/licenses/MIT) 50 | 51 | at your option. 52 | 53 | ## Architectural Decision Records 54 | 55 | Major design decisions are tracked in the [adr](./adr) directory. 56 | 57 | ## Changelog 58 | 59 | ### 0.4.0 60 | 61 | Adds `StatefulOutputSwitch` thanks to [forkbomb9](https://github.com/forkbomb9) 62 | 63 | ### 0.3.3 64 | 65 | Update embedded-hal to 0.2.5 66 | 67 | ### 0.3.2 68 | 69 | - Add convenience converter functions `into_active_low_switch()` and `into_active_high_switch()` 70 | 71 | ### 0.3.1 72 | 73 | - Bug Fix: `is_active()` was returning the wrong result for `Switch` -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'switch-hal'", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib", 16 | "--package=switch-hal" 17 | ], 18 | "filter": { 19 | "name": "switch-hal", 20 | "kind": "lib" 21 | } 22 | }, 23 | "args": [], 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Debug integration test 'pin_into_switch'", 30 | "cargo": { 31 | "args": [ 32 | "test", 33 | "--no-run", 34 | "--test=pin_into_switch", 35 | "--package=switch-hal" 36 | ], 37 | "filter": { 38 | "name": "pin_into_switch", 39 | "kind": "test" 40 | } 41 | }, 42 | "args": [], 43 | "cwd": "${workspaceFolder}" 44 | }, 45 | { 46 | "type": "lldb", 47 | "request": "launch", 48 | "name": "Debug integration test 'output'", 49 | "cargo": { 50 | "args": [ 51 | "test", 52 | "--no-run", 53 | "--test=output", 54 | "--package=switch-hal" 55 | ], 56 | "filter": { 57 | "name": "output", 58 | "kind": "test" 59 | } 60 | }, 61 | "args": [], 62 | "cwd": "${workspaceFolder}" 63 | }, 64 | { 65 | "type": "lldb", 66 | "request": "launch", 67 | "name": "Debug integration test 'input'", 68 | "cargo": { 69 | "args": [ 70 | "test", 71 | "--no-run", 72 | "--test=input", 73 | "--package=switch-hal" 74 | ], 75 | "filter": { 76 | "name": "input", 77 | "kind": "test" 78 | } 79 | }, 80 | "args": [], 81 | "cwd": "${workspaceFolder}" 82 | } 83 | ] 84 | } -------------------------------------------------------------------------------- /adr/use-phantom-data-to-reduce-duplication.md: -------------------------------------------------------------------------------- 1 | # Use PhantomData to Reduce Duplication 2 | 3 | ## Status 4 | 5 | Accepted 6 | 7 | ## Context 8 | 9 | The current implementation requires identical structs to hold the `OutputPin` for `ActiveHighOutputSwitch` and `ActiveLowOutputSwitch`. 10 | This also means there are identical implementations of `new()`, `into_pin()`, `ToggleableOutputSwitch`. 11 | 12 | ## Decision 13 | 14 | This could be cleaned up by using a single struct, with a second type parameter. 15 | 16 | ```rust 17 | pub struct ActiveHigh; 18 | pub struct ActiveLow; 19 | 20 | pub struct Switch 21 | where 22 | T: OutputPin, 23 | { 24 | pin: T, 25 | active: PhantomData, 26 | } 27 | ``` 28 | 29 | This is a zero-cost abstraction, because `PhanotomData` doesn't allocate any memory. 30 | However, this means that construction of an `OutputSwitch` becomes more complicated for the library consumer. 31 | 32 | Before the change: 33 | 34 | ```rust 35 | let led = ActiveHighOutputSwitch::new( 36 | gpioe 37 | .pe9 38 | .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper) 39 | ); 40 | ``` 41 | 42 | After the change: 43 | 44 | ```rust 45 | let led = output::Switch::<_, ActiveHigh>::new( 46 | gpioe 47 | .pe9 48 | .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper) 49 | ); 50 | ``` 51 | 52 | ## Consequences 53 | 54 | By rejecting this proposal, we are adding more (and duplicated!) code to the library, increasing maintenance burden. 55 | 56 | By accepting this proposal, we reduce the maintenance burden, but are making the library more difficult to consume by a little bit. 57 | 58 | This does indeed seem to be a zero cost abstraction. 59 | The examples in [stm32f3-discovery](https://github.com/rubberduck203/stm32f3-discovery) were compiled in release mode and compared. 60 | 61 | ```sh 62 | cargo build --release --examples 63 | ls examples/ | sed s/.rs// | sed s,^,target/thumbv7em-none-eabihf/release/examples/, | xargs cargo size 64 | ``` 65 | 66 | ### Old Version 67 | 68 | ```txt 69 | text data bss dec hex filename 70 | 4784 0 4 4788 12b4 target/thumbv7em-none-eabihf/release/examples/blinky 71 | 4732 0 4 4736 1280 target/thumbv7em-none-eabihf/release/examples/button 72 | 4564 0 4 4568 11d8 target/thumbv7em-none-eabihf/release/examples/button_int 73 | 5460 0 4 5464 1558 target/thumbv7em-none-eabihf/release/examples/roulette 74 | ``` 75 | 76 | ### New (PhantomData) Version 77 | 78 | ```txt 79 | text data bss dec hex filename 80 | 4784 0 4 4788 12b4 target/thumbv7em-none-eabihf/release/examples/blinky 81 | 4732 0 4 4736 1280 target/thumbv7em-none-eabihf/release/examples/button 82 | 4564 0 4 4568 11d8 target/thumbv7em-none-eabihf/release/examples/button_int 83 | 5460 0 4 5464 1558 target/thumbv7em-none-eabihf/release/examples/roulette 84 | ``` 85 | -------------------------------------------------------------------------------- /tests/output.rs: -------------------------------------------------------------------------------- 1 | extern crate switch_hal; 2 | 3 | use switch_hal::mock; 4 | 5 | mod active_high_switch { 6 | use super::*; 7 | use embedded_hal::digital::v2::InputPin; 8 | use switch_hal::{ActiveHigh, OutputSwitch, Switch}; 9 | 10 | #[test] 11 | fn when_on_pin_is_high() { 12 | let pin = mock::Pin::new(); 13 | 14 | let mut led = Switch::<_, ActiveHigh>::new(pin); 15 | led.on().unwrap(); 16 | 17 | let pin = led.into_pin(); 18 | assert_eq!(true, pin.is_high().unwrap()); 19 | } 20 | 21 | #[test] 22 | fn when_off_pin_is_low() { 23 | let pin = mock::Pin::new(); 24 | 25 | let mut led = Switch::<_, ActiveHigh>::new(pin); 26 | led.off().unwrap(); 27 | 28 | let pin = led.into_pin(); 29 | assert_eq!(true, pin.is_low().unwrap()); 30 | } 31 | 32 | #[test] 33 | fn is_toggleable() { 34 | use switch_hal::ToggleableOutputSwitch; 35 | 36 | let pin = mock::Pin::new(); 37 | 38 | let mut led = Switch::<_, ActiveHigh>::new(pin); 39 | led.off().unwrap(); 40 | 41 | led.toggle().unwrap(); 42 | 43 | let pin = led.into_pin(); 44 | assert_eq!(true, pin.is_high().unwrap()); 45 | } 46 | 47 | #[test] 48 | fn not_on_when_low() { 49 | use switch_hal::StatefulOutputSwitch; 50 | 51 | let pin = mock::Pin::new(); 52 | 53 | let mut led = Switch::<_, ActiveHigh>::new(pin); 54 | led.off().unwrap(); 55 | 56 | assert_eq!(false, led.is_on().unwrap()); 57 | 58 | let pin = led.into_pin(); 59 | assert_eq!(false, pin.is_high().unwrap()); 60 | } 61 | 62 | #[test] 63 | fn is_on_when_high() { 64 | use switch_hal::StatefulOutputSwitch; 65 | 66 | let pin = mock::Pin::new(); 67 | 68 | let mut led = Switch::<_, ActiveHigh>::new(pin); 69 | led.on().unwrap(); 70 | 71 | assert_eq!(true, led.is_on().unwrap()); 72 | 73 | let pin = led.into_pin(); 74 | assert_eq!(true, pin.is_high().unwrap()); 75 | } 76 | } 77 | 78 | mod active_low_switch { 79 | use super::*; 80 | use embedded_hal::digital::v2::InputPin; 81 | use switch_hal::{ActiveLow, OutputSwitch, Switch}; 82 | 83 | #[test] 84 | fn when_on_pin_is_low() { 85 | let pin = mock::Pin::new(); 86 | 87 | let mut led = Switch::<_, ActiveLow>::new(pin); 88 | led.on().unwrap(); 89 | 90 | let pin = led.into_pin(); 91 | assert_eq!(true, pin.is_low().unwrap()); 92 | } 93 | 94 | #[test] 95 | fn when_off_pin_is_high() { 96 | let pin = mock::Pin::new(); 97 | 98 | let mut led = Switch::<_, ActiveLow>::new(pin); 99 | led.off().unwrap(); 100 | 101 | let pin = led.into_pin(); 102 | assert_eq!(true, pin.is_high().unwrap()); 103 | } 104 | 105 | #[test] 106 | fn is_toggleable() { 107 | use switch_hal::ToggleableOutputSwitch; 108 | 109 | let pin = mock::Pin::new(); 110 | 111 | let mut led = Switch::<_, ActiveLow>::new(pin); 112 | led.off().unwrap(); 113 | 114 | led.toggle().unwrap(); 115 | 116 | let pin = led.into_pin(); 117 | assert_eq!(true, pin.is_low().unwrap()); 118 | } 119 | 120 | #[test] 121 | fn not_on_when_high() { 122 | use switch_hal::StatefulOutputSwitch; 123 | 124 | let pin = mock::Pin::new(); 125 | 126 | let mut led = Switch::<_, ActiveLow>::new(pin); 127 | led.off().unwrap(); 128 | 129 | assert_eq!(false, led.is_on().unwrap()); 130 | 131 | let pin = led.into_pin(); 132 | assert_eq!(false, pin.is_low().unwrap()); 133 | } 134 | 135 | #[test] 136 | fn is_on_when_low() { 137 | use switch_hal::StatefulOutputSwitch; 138 | 139 | let pin = mock::Pin::new(); 140 | 141 | let mut led = Switch::<_, ActiveLow>::new(pin); 142 | led.on().unwrap(); 143 | 144 | assert_eq!(true, led.is_on().unwrap()); 145 | 146 | let pin = led.into_pin(); 147 | assert_eq!(true, pin.is_low().unwrap()); 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/mock.rs: -------------------------------------------------------------------------------- 1 | //! Mock implementations of [InputPin](embedded_hal::digital::v2::InputPin) and [OutputPin](embedded_hal::digital::v2::OutputPin). 2 | //! 3 | //! WARNING: May be removed if `embedded_hal_mock` crate is improved. 4 | //! https://github.com/dbrgn/embedded-hal-mock/issues/30 5 | //! 6 | //! This is part of the main crate so it is accessible to doctests. 7 | //! Otherwise, I would have created a tests/mock/mod.rs file. 8 | use embedded_hal::digital::v2::toggleable; 9 | use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; 10 | 11 | #[derive(PartialEq, Eq, Debug)] 12 | pub enum State { 13 | Low, 14 | High, 15 | } 16 | 17 | pub struct Pin { 18 | state: Option, 19 | } 20 | 21 | impl Pin { 22 | pub fn new() -> Self { 23 | Pin { state: None } 24 | } 25 | 26 | pub fn with_state(state: State) -> Self { 27 | Pin { state: Some(state) } 28 | } 29 | } 30 | 31 | type MockError = &'static str; 32 | 33 | impl InputPin for Pin { 34 | type Error = MockError; 35 | 36 | fn is_high(&self) -> Result { 37 | match self.state { 38 | Some(State::High) => Ok(true), 39 | Some(State::Low) => Ok(false), 40 | None => Err("state not set"), 41 | } 42 | } 43 | 44 | fn is_low(&self) -> Result { 45 | match self.is_high() { 46 | Ok(v) => Ok(!v), 47 | Err(e) => Err(e), 48 | } 49 | } 50 | } 51 | 52 | impl OutputPin for Pin { 53 | type Error = MockError; 54 | 55 | fn set_low(&mut self) -> Result<(), Self::Error> { 56 | self.state = Some(State::Low); 57 | Ok(()) 58 | } 59 | 60 | fn set_high(&mut self) -> Result<(), Self::Error> { 61 | self.state = Some(State::High); 62 | Ok(()) 63 | } 64 | } 65 | 66 | impl StatefulOutputPin for Pin { 67 | fn is_set_low(&self) -> Result { 68 | self.is_low() 69 | } 70 | 71 | fn is_set_high(&self) -> Result { 72 | self.is_high() 73 | } 74 | } 75 | 76 | impl toggleable::Default for Pin {} 77 | 78 | #[cfg(test)] 79 | mod test { 80 | use super::*; 81 | 82 | mod new { 83 | use super::*; 84 | 85 | #[test] 86 | fn state_is_uninitialized() { 87 | let pin = Pin::new(); 88 | assert_eq!(None, pin.state); 89 | pin.is_low().expect_err("Expected uninitialized pin"); 90 | } 91 | } 92 | 93 | mod input_pin { 94 | use super::*; 95 | 96 | #[test] 97 | fn error_when_uninitialized() { 98 | let pin = Pin { state: None }; 99 | pin.is_high().expect_err("Expected uninitialized pin"); 100 | } 101 | 102 | mod is_high { 103 | use super::*; 104 | 105 | #[test] 106 | fn returns_true_when_state_is_high() { 107 | let pin = Pin::with_state(State::High); 108 | assert_eq!(true, pin.is_high().unwrap()); 109 | } 110 | 111 | #[test] 112 | fn returns_false_when_state_is_low() { 113 | let pin = Pin::with_state(State::Low); 114 | assert_eq!(false, pin.is_high().unwrap()); 115 | } 116 | } 117 | 118 | mod is_low { 119 | use super::*; 120 | 121 | #[test] 122 | fn returns_false_when_state_is_high() { 123 | let pin = Pin::with_state(State::High); 124 | assert_eq!(false, pin.is_low().unwrap()); 125 | } 126 | 127 | #[test] 128 | fn returns_true_when_state_is_high() { 129 | let pin = Pin::with_state(State::Low); 130 | assert_eq!(true, pin.is_low().unwrap()); 131 | } 132 | } 133 | } 134 | 135 | mod output_pin { 136 | use super::*; 137 | 138 | #[test] 139 | fn set_low() { 140 | let mut pin = Pin::new(); 141 | pin.set_low().unwrap(); 142 | 143 | assert_eq!(true, pin.is_low().unwrap()); 144 | } 145 | 146 | #[test] 147 | fn set_high() { 148 | let mut pin = Pin::new(); 149 | pin.set_high().unwrap(); 150 | 151 | assert_eq!(true, pin.is_high().unwrap()); 152 | } 153 | } 154 | 155 | mod stateful_output_pin { 156 | use super::*; 157 | 158 | #[test] 159 | fn error_when_uninitialized() { 160 | let pin = Pin { state: None }; 161 | pin.is_set_high().expect_err("Expected uninitialized pin"); 162 | } 163 | 164 | mod is_set_low { 165 | use super::*; 166 | 167 | #[test] 168 | fn returns_false_when_state_is_high() { 169 | let pin = Pin::with_state(State::High); 170 | assert_eq!(false, pin.is_set_low().unwrap()); 171 | } 172 | 173 | #[test] 174 | fn returns_true_when_state_is_high() { 175 | let pin = Pin::with_state(State::Low); 176 | assert_eq!(true, pin.is_set_low().unwrap()); 177 | } 178 | } 179 | 180 | mod is_set_high { 181 | use super::*; 182 | 183 | #[test] 184 | fn returns_true_when_state_is_high() { 185 | let pin = Pin::with_state(State::High); 186 | assert_eq!(true, pin.is_set_high().unwrap()); 187 | } 188 | 189 | #[test] 190 | fn returns_false_when_state_is_low() { 191 | let pin = Pin::with_state(State::Low); 192 | assert_eq!(false, pin.is_set_high().unwrap()); 193 | } 194 | } 195 | 196 | mod toggleable { 197 | use super::*; 198 | use embedded_hal::digital::v2::ToggleableOutputPin; 199 | 200 | #[test] 201 | fn default_toggleable_impl() { 202 | let mut pin = Pin::with_state(State::Low); 203 | pin.toggle().unwrap(); 204 | assert_eq!(true, pin.is_set_high().unwrap()); 205 | } 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | mod input; 4 | mod output; 5 | 6 | pub mod mock; 7 | 8 | /// Represents an input switch, such as a button or a switch 9 | pub trait InputSwitch { 10 | type Error; 11 | 12 | /// Returns true if the switch has been activated, otherwise false 13 | /// i.e. if a button is currently pressed, returns true 14 | /// 15 | /// # Examples 16 | /// 17 | /// ``` 18 | /// # use switch_hal::mock; 19 | /// use switch_hal::{InputSwitch, OutputSwitch, Switch, IntoSwitch}; 20 | /// # let pin = mock::Pin::with_state(mock::State::High); 21 | /// # let mut status_led = mock::Pin::new().into_active_high_switch(); 22 | /// let button = pin.into_active_low_switch(); 23 | /// match button.is_active() { 24 | /// Ok(true) => { status_led.on().ok(); } 25 | /// Ok(false) => { status_led.off().ok(); } 26 | /// Err(_) => { panic!("Failed to read button state"); } 27 | /// } 28 | /// ``` 29 | fn is_active(&self) -> Result; 30 | } 31 | 32 | /// Represents an output switch, such as a LED "switch" or transistor 33 | pub trait OutputSwitch { 34 | type Error; 35 | 36 | /// Turns the switch on 37 | /// 38 | /// # Examples 39 | /// 40 | /// ``` 41 | /// # use switch_hal::mock; 42 | /// use switch_hal::{OutputSwitch, Switch, IntoSwitch}; 43 | /// # let pin = mock::Pin::new(); 44 | /// let mut led = pin.into_active_high_switch(); 45 | /// led.on().ok(); 46 | /// ``` 47 | fn on(&mut self) -> Result<(), Self::Error>; 48 | 49 | /// Turns the switch off 50 | /// 51 | /// # Examples 52 | /// 53 | /// ``` 54 | /// # use switch_hal::mock; 55 | /// use switch_hal::{OutputSwitch, Switch, IntoSwitch}; 56 | /// # let pin = mock::Pin::new(); 57 | /// let mut led = pin.into_active_high_switch(); 58 | /// led.off().ok(); 59 | /// ``` 60 | fn off(&mut self) -> Result<(), Self::Error>; 61 | } 62 | 63 | /// Toggles the switch from it's current state to it's opposite state. 64 | /// 65 | /// # Notes 66 | /// This is only available if the underlying hal has implemented [ToggleableOutputPin](embedded_hal::digital::v2::ToggleableOutputPin) 67 | pub trait ToggleableOutputSwitch { 68 | type Error; 69 | 70 | /// Toggles the current state of the [OutputSwitch](OutputSwitch) 71 | /// 72 | /// # Examples 73 | /// 74 | /// ``` 75 | /// # use switch_hal::mock; 76 | /// use switch_hal::{OutputSwitch, ToggleableOutputSwitch, Switch, IntoSwitch}; 77 | /// # let pin = mock::Pin::new(); 78 | /// let mut led = pin.into_active_high_switch(); 79 | /// led.toggle().ok(); 80 | /// ``` 81 | fn toggle(&mut self) -> Result<(), Self::Error>; 82 | } 83 | 84 | /// Checks current switch state 85 | /// 86 | /// # Notes 87 | /// This is only available if the underlying hal has implemented [StatefulOutputPin](embedded_hal::digital::v2::StatefulOutputPin) 88 | pub trait StatefulOutputSwitch { 89 | type Error; 90 | 91 | /// Checks whether the switch is on 92 | /// 93 | /// # Examples 94 | /// 95 | /// ``` 96 | /// # use switch_hal::mock; 97 | /// use switch_hal::{OutputSwitch, Switch, StatefulOutputSwitch, IntoSwitch}; 98 | /// # let pin = mock::Pin::new(); 99 | /// let mut led = pin.into_active_high_switch(); 100 | /// led.off().ok(); 101 | /// assert!(!led.is_on().unwrap()); 102 | /// ``` 103 | fn is_on(&mut self) -> Result; 104 | 105 | /// Checks whether the switch is off 106 | /// 107 | /// # Examples 108 | /// 109 | /// ``` 110 | /// # use switch_hal::mock; 111 | /// use switch_hal::{OutputSwitch, Switch, StatefulOutputSwitch, IntoSwitch}; 112 | /// # let pin = mock::Pin::new(); 113 | /// let mut led = pin.into_active_high_switch(); 114 | /// led.off().ok(); 115 | /// assert!(led.is_off().unwrap()); 116 | /// ``` 117 | fn is_off(&mut self) -> Result; 118 | } 119 | 120 | /// Zero sized struct for signaling to [Switch](struct.Switch.html) that it is active high 121 | pub struct ActiveHigh; 122 | /// Zero sized struct for signaling to [Switch](struct.Switch.html) that it is active low 123 | pub struct ActiveLow; 124 | 125 | use core::marker::PhantomData; 126 | 127 | /// Concrete implementation for [InputSwitch](trait.InputSwitch.html) and [OutputSwitch](trait.OutputSwitch.html) 128 | /// 129 | /// # Type Params 130 | /// - `IoPin` must be a type that implements either of the [InputPin](embedded_hal::digital::v2::InputPin) or [OutputPin](embedded_hal::digital::v2::OutputPin) traits. 131 | /// - `ActiveLevel` indicates whether the `Switch` is [ActiveHigh](ActiveHigh) or [ActiveLow](ActiveLow). 132 | /// `ActiveLevel` is not actually stored in the struct. 133 | /// It's [PhantomData](core::marker::PhantomData) used to indicate which implementation to use. 134 | pub struct Switch { 135 | pin: IoPin, 136 | active: PhantomData, 137 | } 138 | 139 | impl Switch { 140 | /// Constructs a new [Switch](struct.Switch.html) from a concrete implementation of an 141 | /// [InputPin](embedded_hal::digital::v2::InputPin) or [OutputPin](embedded_hal::digital::v2::OutputPin) 142 | /// 143 | /// **Prefer the [IntoSwitch](trait.IntoSwitch.html) trait over calling [new](#method.new) directly.** 144 | /// 145 | /// # Examples 146 | /// 147 | /// Active High 148 | /// 149 | /// ``` 150 | /// # use switch_hal::mock; 151 | /// use switch_hal::{ActiveHigh, OutputSwitch, Switch}; 152 | /// # let pin = mock::Pin::new(); 153 | /// let mut led = Switch::<_, ActiveHigh>::new(pin); 154 | /// ``` 155 | /// 156 | /// ActiveLow 157 | /// 158 | /// ``` 159 | /// # use switch_hal::mock; 160 | /// use switch_hal::{ActiveLow, OutputSwitch, Switch}; 161 | /// # let pin = mock::Pin::new(); 162 | /// let mut led = Switch::<_, ActiveLow>::new(pin); 163 | /// ``` 164 | /// 165 | /// stm32f3xx-hal 166 | /// 167 | /// ```ignore 168 | /// // Example for the stm32f303 169 | /// use stm32f3xx_hal::gpio::gpioe; 170 | /// use stm32f3xx_hal::gpio::{PushPull, Output}; 171 | /// use stm32f3xx_hal::stm32; 172 | /// 173 | /// use switch_hal::{ActiveHigh, Switch}; 174 | /// 175 | /// let device_periphs = stm32::Peripherals::take().unwrap(); 176 | /// let gpioe = device_periphs.GPIOE.split(&mut reset_control_clock.ahb); 177 | /// 178 | /// let led = Switch::<_, ActiveHigh>::new( 179 | /// gpioe 180 | /// .pe9 181 | /// .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper) 182 | /// ) 183 | /// ``` 184 | pub fn new(pin: IoPin) -> Self { 185 | Switch { 186 | pin: pin, 187 | active: PhantomData::, 188 | } 189 | } 190 | 191 | /// Consumes the [Switch](struct.Switch.html) and returns the underlying [InputPin](embedded_hal::digital::v2::InputPin) or [OutputPin](embedded_hal::digital::v2::OutputPin). 192 | /// 193 | /// This is useful fore retrieving the underlying pin to use it for a different purpose. 194 | /// 195 | /// # Examples 196 | /// 197 | /// ``` 198 | /// # use switch_hal::mock; 199 | /// use switch_hal::{OutputSwitch, Switch, IntoSwitch}; 200 | /// # let pin = mock::Pin::new(); 201 | /// let mut led = pin.into_active_high_switch(); 202 | /// led.on().ok(); 203 | /// let mut pin = led.into_pin(); 204 | /// // do something else with the pin 205 | /// ``` 206 | pub fn into_pin(self) -> IoPin { 207 | self.pin 208 | } 209 | } 210 | 211 | /// Convenience functions for converting [InputPin](embedded_hal::digital::v2::InputPin) 212 | /// and [OutputPin](embedded_hal::digital::v2::OutputPin) to a [Switch](struct.Switch.html). 213 | /// 214 | /// The type of [Switch](struct.Switch.html) returned, 215 | /// [InputSwitch](trait.InputSwitch.html) or [OutputSwitch](trait.OutputSwitch.html) is 216 | /// determined by whether the `IoPin` being consumed is an [InputPin](embedded_hal::digital::v2::InputPin) 217 | /// or [OutputPin](embedded_hal::digital::v2::OutputPin). 218 | pub trait IntoSwitch { 219 | 220 | /// Consumes the `IoPin` returning a [Switch](struct.Switch.html) of the appropriate `ActiveLevel`. 221 | /// 222 | /// This method exists so other, more convenient functions, can have blanket implementations. 223 | /// Prefer [into_active_low_switch](#method.into_active_low_switch) and [into_active_high_switch](#method.into_active_high_switch). 224 | /// 225 | /// # Examples 226 | /// 227 | /// ## Active High 228 | /// 229 | /// ``` 230 | /// # use switch_hal::mock; 231 | /// use switch_hal::{ActiveHigh, OutputSwitch, IntoSwitch}; 232 | /// # let pin = mock::Pin::new(); 233 | /// let led = pin.into_switch::(); 234 | /// ``` 235 | /// 236 | /// ## Active Low 237 | /// 238 | /// ``` 239 | /// # use switch_hal::mock; 240 | /// use switch_hal::{ActiveLow, InputSwitch, IntoSwitch}; 241 | /// # let pin = mock::Pin::new(); 242 | /// let button = pin.into_switch::(); 243 | /// ``` 244 | fn into_switch(self) -> Switch 245 | where 246 | Self: core::marker::Sized; 247 | 248 | /// Consumes the `IoPin` returning a `Switch`. 249 | /// 250 | /// # Examples 251 | /// 252 | /// ``` 253 | /// # use switch_hal::mock; 254 | /// use switch_hal::IntoSwitch; 255 | /// # let pin = mock::Pin::new(); 256 | /// let led = pin.into_active_low_switch(); 257 | /// ``` 258 | fn into_active_low_switch(self) -> Switch 259 | where 260 | Self: core::marker::Sized, 261 | { 262 | self.into_switch::() 263 | } 264 | 265 | /// Consumes the `IoPin` returning a `Switch`. 266 | /// 267 | /// # Examples 268 | /// 269 | /// ``` 270 | /// # use switch_hal::mock; 271 | /// use switch_hal::IntoSwitch; 272 | /// # let pin = mock::Pin::new(); 273 | /// let button = pin.into_active_high_switch(); 274 | /// ``` 275 | fn into_active_high_switch(self) -> Switch 276 | where 277 | Self: core::marker::Sized, 278 | { 279 | self.into_switch::() 280 | } 281 | } 282 | 283 | impl IntoSwitch for T { 284 | fn into_switch(self) -> Switch { 285 | Switch::::new(self) 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2020 Christopher McClellan 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------