├── .gitignore ├── meta └── logo.png ├── .travis.yml ├── examples ├── registration.rs ├── save_bridge.rs └── basic_cli.rs ├── tests ├── deserialization.rs ├── json_examples │ ├── lightstrip.json │ └── light.json └── mod.rs ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── Cargo.toml ├── src ├── lib.rs ├── color.rs ├── lights.rs ├── helpers.rs └── bridge.rs ├── README.md ├── LICENSE └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /meta/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/finnkauski/lighthouse/HEAD/meta/logo.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | jobs: 5 | fast_finish: true 6 | 7 | cache: cargo 8 | script: 9 | - cargo build --verbose --all 10 | - cargo test --verbose --all --all-features 11 | -------------------------------------------------------------------------------- /examples/registration.rs: -------------------------------------------------------------------------------- 1 | // Example showing how to register a new bridge. 2 | fn main() { 3 | use lighthouse::*; 4 | 5 | // Create bridge by registering with the Philips Hue Bridge 6 | let (b, token) = bridge::Bridge::try_register(true).unwrap(); 7 | 8 | // Print out the whole bridge 9 | println!("Created bridge: {:#?}", b); 10 | } 11 | -------------------------------------------------------------------------------- /examples/save_bridge.rs: -------------------------------------------------------------------------------- 1 | // Save the bridge to file and load it back in; 2 | fn main() { 3 | use lighthouse::*; 4 | 5 | let filename = "test_bridge"; 6 | // Create bridge from an IP and a Key. 7 | let b = bridge::Bridge::new("127.0.0.1".parse().unwrap(), "".to_owned()).unwrap(); 8 | 9 | b.to_file(filename).expect("Could not write to file!"); 10 | bridge::Bridge::from_file(filename).expect("Could not read from file!"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/deserialization.rs: -------------------------------------------------------------------------------- 1 | use lighthouse::lights::*; 2 | 3 | #[test] 4 | fn lightbulb_from_json() { 5 | if let Ok(Light::LightBulb { .. }) = 6 | serde_json::from_str(include_str!("json_examples/light.json")) 7 | { 8 | } else { 9 | panic!("Could not deserialize the lights into the correct struct type: LightBulb") 10 | } 11 | } 12 | 13 | #[test] 14 | fn lightstrip_from_json() { 15 | if let Ok(Light::LightStrip { .. }) = 16 | serde_json::from_str(include_str!("json_examples/lightstrip.json")) 17 | { 18 | } else { 19 | panic!("Could not deserialize the lights into the correct struct type: LightBulb") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /examples/basic_cli.rs: -------------------------------------------------------------------------------- 1 | // A simple example of a command line tool to turn off and on 2 | // the lights 3 | fn main() { 4 | use lighthouse::*; 5 | use std::env; 6 | 7 | // Create bridge from an IP and a Key. 8 | let mut b = bridge::Bridge::new("".parse().unwrap(), "".to_owned()).unwrap(); 9 | 10 | // See if the user passed on or of 11 | let on_off = match &env::args().collect::>()[1][..] { 12 | "on" => true, 13 | "off" => false, 14 | _ => panic!("Unknown command. Use: on / off"), 15 | }; 16 | let s = state!(on: on_off, bri:254); 17 | b.state_to_multiple(vec![1, 2, 3], vec![s; 3]) 18 | .expect("Could not send all states!"); 19 | } 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lighthouse" 3 | version = "0.2.1" 4 | authors = ["Finnkauski "] 5 | description="Command line tool and Philips Hue light control framework wrapping up the Hue API" 6 | license="AGPL-3.0-or-later" 7 | homepage="https://finnkauski.com" 8 | documentation="https://docs.rs/lighthouse/" 9 | repository="https://github.com/finnkauski/lighthouse" 10 | edition = "2018" 11 | readme="README.md" 12 | 13 | [features] 14 | default=[] 15 | persist = [] 16 | color = ["palette"] 17 | 18 | 19 | [dependencies] 20 | reqwest = {version = "0.10.6", features=["json"], default-features=false} 21 | tokio = {version = "0.2", features=["rt-core", "stream"]} 22 | futures = "0.3.5" 23 | ssdp = "0.7.0" 24 | url = "2.1.1" 25 | serde = {version = "1.0.114", features = ["derive"]} 26 | serde_json = "1.0.56" 27 | 28 | # Optional dependencies 29 | palette = {version ="0.5.0" , optional = true} 30 | 31 | [[example]] 32 | name = "registration" 33 | path = "examples/registration.rs" 34 | 35 | [[example]] 36 | name = "basic-cli" 37 | path = "examples/basic_cli.rs" 38 | 39 | [[example]] 40 | name = "save_bridge" 41 | path = "examples/save_bridge.rs" 42 | required-features = ["persist"] 43 | 44 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # lighthouse 2 | //! 3 | //! The `lighthouse` crate provides a wrapper for the Philips Hue REST API provided on a local 4 | //! network by the Philips Hue Bridge. 5 | //! 6 | //! ## Constucting the Bridge client and finding your lights 7 | //! 8 | //! ```no_run 9 | //! use std::net::{IpAddr, Ipv4Addr}; 10 | //! use lighthouse::bridge::Bridge; 11 | //! let ip_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)); 12 | //! let bridge_token = String::from("my-example-token"); 13 | //! let mut bridge = Bridge::new(ip_addr, bridge_token).unwrap(); 14 | //! let lights = bridge.get_lights(); 15 | //! ``` 16 | //! 17 | //! ## Controlling individual lights 18 | //! 19 | //! ```no_run 20 | //! use lighthouse::state; 21 | //! use lighthouse::bridge::Bridge; 22 | //! let mut bridge = Bridge::new("192.168.1.10".parse().unwrap(), "token".to_string()).unwrap(); 23 | //! bridge.state_to(1, state!(on: true, bri: 128)); 24 | //! ``` 25 | 26 | // TODO: Implement a Bridge Builder and move the building functions out of the actual bridge 27 | // TODO: Add validation check for when making a bridge - ping some API endpoint to collect data. Good way to get more info as well about the bridge 28 | 29 | pub mod bridge; 30 | #[cfg(feature = "color")] 31 | pub mod color; 32 | pub mod helpers; 33 | pub mod lights; 34 | -------------------------------------------------------------------------------- /tests/json_examples/lightstrip.json: -------------------------------------------------------------------------------- 1 | { 2 | "capabilities": { 3 | "certified": true, 4 | "control": { 5 | "colorgamut": [ 6 | [0.6915, 0.3083], 7 | [0.17, 0.7], 8 | [0.1532, 0.0475] 9 | ], 10 | "colorgamuttype": "C", 11 | "ct": { 12 | "max": 500, 13 | "min": 153 14 | }, 15 | "maxlumen": 1600, 16 | "mindimlevel": 25 17 | }, 18 | "streaming": { 19 | "proxy": true, 20 | "renderer": true 21 | } 22 | }, 23 | "config": { 24 | "archetype": "huelightstrip", 25 | "direction": "omnidirectional", 26 | "function": "mixed", 27 | "startup": { 28 | "configured": true, 29 | "mode": "safety" 30 | } 31 | }, 32 | "manufacturername": "Signify Netherlands B.V.", 33 | "modelid": "LST002", 34 | "name": "xxx", 35 | "productname": "Hue lightstrip plus", 36 | "state": { 37 | "alert": "none", 38 | "bri": 1, 39 | "colormode": "xy", 40 | "ct": 396, 41 | "effect": "none", 42 | "hue": 7790, 43 | "mode": "homeautomation", 44 | "on": false, 45 | "reachable": true, 46 | "sat": 160, 47 | "xy": [0.4764, 0.4087] 48 | }, 49 | "swupdate": { 50 | "lastinstall": "2020-03-04T13:57:09", 51 | "state": "noupdates" 52 | }, 53 | "swversion": "5.130.1.30000", 54 | "type": "Extended color light", 55 | "uniqueid": "xxx" 56 | } 57 | -------------------------------------------------------------------------------- /tests/json_examples/light.json: -------------------------------------------------------------------------------- 1 | { 2 | "capabilities": { 3 | "certified": true, 4 | "control": { 5 | "colorgamut": [ 6 | [0.6915, 0.3083], 7 | [0.17, 0.7], 8 | [0.1532, 0.0475] 9 | ], 10 | "colorgamuttype": "C", 11 | "ct": { 12 | "max": 500, 13 | "min": 153 14 | }, 15 | "maxlumen": 806, 16 | "mindimlevel": 1000 17 | }, 18 | "streaming": { 19 | "proxy": true, 20 | "renderer": true 21 | } 22 | }, 23 | "config": { 24 | "archetype": "sultanbulb", 25 | "direction": "omnidirectional", 26 | "function": "mixed", 27 | "startup": { 28 | "configured": true, 29 | "mode": "safety" 30 | } 31 | }, 32 | "manufacturername": "Signify Netherlands B.V.", 33 | "modelid": "LCT015", 34 | "name": "xxx", 35 | "productid": "Philips-LCT015-1-A19ECLv5", 36 | "productname": "Hue color lamp", 37 | "state": { 38 | "alert": "select", 39 | "bri": 198, 40 | "colormode": "ct", 41 | "ct": 370, 42 | "effect": "none", 43 | "hue": 8382, 44 | "mode": "homeautomation", 45 | "on": false, 46 | "reachable": false, 47 | "sat": 143, 48 | "xy": [0.4594, 0.4104] 49 | }, 50 | "swconfigid": "772B0E5E", 51 | "swupdate": { 52 | "lastinstall": "2020-03-03T00:11:09", 53 | "state": "noupdates" 54 | }, 55 | "swversion": "1.50.2_r30933", 56 | "type": "Extended color light", 57 | "uniqueid": "xxx" 58 | } 59 | -------------------------------------------------------------------------------- /src/color.rs: -------------------------------------------------------------------------------- 1 | /// # Color module (UNDERDEVELOPED) 2 | /// 3 | /// This module (gated under the `color` feature) contains helpers in converting 4 | /// colors to the required representations for the HUE API. 5 | /// 6 | /// **NOTE:** Currently untested and work in progress. If you want to please submit 7 | /// a PR with improvements. 8 | use palette::{rgb::Srgb, Hsl}; 9 | 10 | /// Convert from 'rgb' to the 'xy' values that can be sent to the 11 | /// hue lights. Does not internally use color gamut. 12 | /// 13 | /// **NOTE:** Currently no gamma correction is used. This was implemented based on the 14 | /// gist found [here](https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d). 15 | pub fn rgb_to_xy(rgb: Vec) -> [f32; 2] { 16 | // NOTE: more information https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d 17 | let standardise = |c: u8| { 18 | let val = (c as f32) / 255.0; 19 | if val > 0.04045 { 20 | ((val + 0.055) / (1.0 + 0.055)).powf(2.4) 21 | } else { 22 | val / 12.92 23 | } 24 | }; 25 | 26 | let cnv: Vec = rgb.into_iter().map(standardise).collect(); 27 | let (red, green, blue) = (cnv[0], cnv[1], cnv[2]); 28 | 29 | let x = red * 0.664_511 + green * 0.154_324 + blue * 0.162_028; 30 | let y = red * 0.283_881 + green * 0.668_433 + blue * 0.047_685; 31 | let z = red * 0.000_088 + green * 0.072_310 + blue * 0.986_039; 32 | let denominator = x + y + z; 33 | 34 | // TODO: if the z is truly the brightness we need to return it 35 | [x / denominator, y / denominator] 36 | } 37 | 38 | /// Convert from 'rgb' to the 'hsl' values that can be sent to the 39 | /// hue lights. 40 | pub fn rgb_to_hsl(rgb: Vec) -> (u16, u8, u8) { 41 | let standard: Vec = rgb 42 | .into_iter() 43 | .map(|val: u8| (val as f32) / 255.0) 44 | .collect(); 45 | let (red, green, blue) = (standard[0], standard[1], standard[2]); 46 | let hsl: Hsl = Srgb::new(red, green, blue).into(); 47 | let (h, s, l) = hsl.into_components(); 48 | ( 49 | (h.to_positive_degrees() / 360.0 * 65535.0) as u16, 50 | (s * 254.0) as u8, 51 | (l * 254.0) as u8, 52 | ) 53 | } 54 | 55 | /// Convert hex color to `hsl` 56 | pub fn hex_to_hsl(s: &str) -> Result<(u16, u8, u8), std::num::ParseIntError> { 57 | let rgb = hex_to_rgb(s)?; 58 | Ok(rgb_to_hsl(rgb)) 59 | } 60 | 61 | /// Convert hex color string to `rgb` 62 | pub fn hex_to_rgb(s: &str) -> Result, std::num::ParseIntError> { 63 | (0..s.len()) 64 | .step_by(2) 65 | .map(|i| u8::from_str_radix(&s[i..i + 2], 16)) 66 | .collect() 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/finnkauski/lighthouse.svg?branch=main)](https://travis-ci.com/finnkauski/lighthouse) 2 | 3 |

4 | 5 | # Lighthouse 6 | 7 | Control your Philips Hue lights with this API wrapper! 8 | 9 | 10 | 11 | **NOTE:** 12 | This wrapper is under active redevelopment, see the older commits in order to get 13 | the previous iterations of the API. Also this is not a complete API wrapper for the HUE API as I do not have the time to expand the wrapper. If you would like to contribute please consider making a PR. 14 | 15 | ## Usage 16 | 17 | Adding the dependency: 18 | 19 | ```toml 20 | [dependencies] 21 | lighthouse = "0.2.1" 22 | ``` 23 | 24 | And then in your application: 25 | 26 | ```rust 27 | use std::net::{IpAddr, Ipv4Addr}; 28 | use lighthouse::bridge::Bridge; 29 | // Acquire your IP address and substitute here 30 | let ip_addr = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)); 31 | // Get an API token from your bridge, requires proof of physical access 32 | let bridge_token = String::from("my-example-token"); 33 | let mut bridge = Bridge::new(ip_addr, bridge_token).unwrap(); 34 | let lights = bridge.get_lights(); 35 | ``` 36 | 37 | If you haven't set up a user token or discovered your bridge yet, you can do so with the interactive `try_register` function: 38 | 39 | ```rust 40 | use lighthouse::*; 41 | // Discovers the bridge's IP and registers a user token 42 | // This requires physical access to the Bridge! 43 | let bridge = bridge::Bridge::try_register(true).unwrap(); 44 | ``` 45 | 46 | See the `./examples/` directory for more examples. 47 | 48 | **NOTE:** 49 | The features for color conversion and serialisation to and from files are now behind 50 | feature flags. Available flags are: 51 | 52 | - color - adds the color conversion module 53 | - persist - adds the ability to serialise to and from files and also to create bridges from environment variables 54 | 55 | ## Command line tool 56 | 57 | The previous releases of this library came with a binary that allowed users to control their lights from the command line. 58 | The crate has been refactored and simplified. The binary will have to be refactored as well. However the priority is to 59 | finished a more sensible API wrapper before moving onto the binary. 60 | 61 | ## Also see: 62 | 63 | [lighthouse.el](https://github.com/finnkauski/lighthouse.el) - an Emacs package 64 | wrapping the functionality of `lighthouse` (uses older version of the library) 65 | 66 | [lightshow](https://github.com/finnkauski/lightshow) - A simple scripting language 67 | allowing you to script your lights into lightshows (uses older version of the library) 68 | 69 | ## Contributions 70 | 71 | I don't have the time to wrap absolutely all the endpoints and the data structures required for the API. 72 | 73 | I would really love people to chip in over time and keep adding new functionality through extra endpoints wrapped. 74 | -------------------------------------------------------------------------------- /tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_generate_target() { 3 | use std::net::IpAddr; 4 | use url::Url; 5 | 6 | let addr: IpAddr = "192.168.0.1".parse().unwrap(); 7 | assert_eq!( 8 | lighthouse::helpers::generate_target(addr, "tokengoeshere1234").unwrap(), 9 | Url::parse("http://192.168.0.1/api/tokengoeshere1234/").unwrap() 10 | ) 11 | } 12 | 13 | #[test] 14 | fn test_httpbin_get() { 15 | use lighthouse::helpers::network::{send_requests, AllowedMethod}; 16 | let targets = vec![ 17 | ( 18 | url::Url::parse("http://httpbin.org/get").unwrap(), 19 | AllowedMethod::GET, 20 | ), 21 | ( 22 | url::Url::parse("http://httpbin.org/post").unwrap(), 23 | AllowedMethod::POST, 24 | ), 25 | ( 26 | url::Url::parse("http://httpbin.org/put").unwrap(), 27 | AllowedMethod::PUT, 28 | ), 29 | ]; 30 | let states = vec![None, None, None]; 31 | let client = reqwest::Client::new(); 32 | let correct: bool = tokio::runtime::Runtime::new() 33 | .unwrap() 34 | .block_on(send_requests(targets, states, &client)) 35 | .into_iter() 36 | .all(|r| r.unwrap().status() == 200); 37 | assert!(correct); 38 | } 39 | 40 | #[test] 41 | fn test_empty_state_macro() { 42 | use lighthouse::{lights::*, *}; 43 | let ref_state = state!(); 44 | let nonref_state = state!(); 45 | assert_eq!(ref_state, &SendableState::default()); 46 | assert_eq!(nonref_state, &SendableState::default()); 47 | } 48 | 49 | #[test] 50 | fn test_state_macro() { 51 | use lighthouse::{lights::*, *}; 52 | let ref_state = state!(on: true, 53 | bri: 230, 54 | hue: 100, 55 | sat: 20, 56 | effect: String::from("none"), 57 | xy: [1.0, 1.0], 58 | alert: String::from("none"), 59 | colormode: String::from("xy"), 60 | transitiontime: 2); 61 | let nonref_state = state!(nonref; 62 | on: true, 63 | bri: 230, 64 | hue: 100, 65 | sat: 20, 66 | effect: String::from("none"), 67 | xy: [1.0, 1.0], 68 | alert: String::from("none"), 69 | colormode: String::from("xy"), 70 | transitiontime: 2); 71 | let truth = SendableState { 72 | on: Some(true), 73 | bri: Some(230), 74 | hue: Some(100), 75 | sat: Some(20), 76 | effect: Some(String::from("none")), 77 | xy: Some([1.0, 1.0]), 78 | alert: Some(String::from("none")), 79 | colormode: Some(String::from("xy")), 80 | transitiontime: Some(2), 81 | }; 82 | assert_eq!(ref_state, &truth); 83 | assert_eq!(nonref_state, truth); 84 | } 85 | 86 | #[test] 87 | fn test_from_state() { 88 | use lighthouse::{lights::*, *}; 89 | let mut s = State { 90 | on: true, 91 | bri: Some(100), 92 | hue: Some(240), 93 | sat: Some(20), 94 | effect: Some(String::from("none")), 95 | xy: Some([2.0, 2.0]), 96 | ct: Some(200), 97 | alert: String::from("select"), 98 | colormode: Some(String::from("somemode")), 99 | mode: String::from("mode"), 100 | reachable: true, 101 | }; 102 | let state_default = state!(from: s.clone();); 103 | let state_changed = state!(from: s.clone(); on: false); 104 | 105 | assert_eq!(SendableState::from(s.clone()), state_default); 106 | 107 | s.on = false; 108 | assert_eq!(SendableState::from(s), state_changed); 109 | } 110 | 111 | #[test] 112 | #[cfg(feature = "persist")] 113 | fn test_bridge_serialization() { 114 | use lighthouse::*; 115 | 116 | let filename = "test_bridge"; 117 | // Create bridge from an IP and a Key. 118 | let b = bridge::Bridge::new("127.0.0.1".parse().unwrap(), "".to_owned()).unwrap(); 119 | 120 | b.to_file(filename).expect("Could not save bridge to file"); 121 | 122 | let b2 = bridge::Bridge::from_file(filename).unwrap(); 123 | 124 | assert!(b.target == b2.target); 125 | } 126 | -------------------------------------------------------------------------------- /src/lights.rs: -------------------------------------------------------------------------------- 1 | /// # Lights module 2 | /// 3 | /// This module contains the core representations for the lights and responses 4 | /// from the API. 5 | // imports 6 | use serde::{Deserialize, Serialize}; 7 | use serde_json::Value; 8 | 9 | /// Struct that can be sent to the Hue lights. It mirrors closely the 10 | /// `State`. 11 | /// 12 | /// ``` 13 | /// use lighthouse::{lights::*, state}; 14 | /// let state_1: SendableState = serde_json::from_str(r#"{"on":true}"#).unwrap(); 15 | /// let state_2: SendableState = SendableState {on:Some(true), ..SendableState::default()}; 16 | /// let state_3: &SendableState = state!(on: true, xy: [1.0, 0.123]); 17 | /// let state_4: SendableState = state!(nonref; on: true, xy: [1.0, 0.123]); 18 | /// ``` 19 | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] 20 | pub struct SendableState { 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub on: Option, 23 | #[serde(skip_serializing_if = "Option::is_none")] 24 | pub bri: Option, 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | pub hue: Option, 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub sat: Option, 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | pub effect: Option, 31 | #[serde(skip_serializing_if = "Option::is_none")] 32 | pub xy: Option<[f32; 2]>, 33 | #[serde(skip_serializing_if = "Option::is_none")] 34 | pub alert: Option, 35 | #[serde(skip_serializing_if = "Option::is_none")] 36 | pub transitiontime: Option, 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | pub colormode: Option, 39 | } 40 | 41 | /// This would be good to reimplement in some way to respect the current state of a given light. 42 | /// TODO: Add a - delta state type which is just state change 43 | impl Default for SendableState { 44 | /// Initialises the state as Nones 45 | fn default() -> Self { 46 | Self { 47 | on: None, 48 | bri: None, 49 | hue: None, 50 | sat: None, 51 | effect: None, 52 | xy: None, 53 | alert: None, 54 | transitiontime: Some(1), 55 | colormode: None, 56 | } 57 | } 58 | } 59 | 60 | /// This object contains the state part of each light 61 | #[derive(Serialize, Deserialize, Debug, Clone)] 62 | pub struct State { 63 | pub on: bool, 64 | #[serde(skip_serializing_if = "Option::is_none")] 65 | pub bri: Option, 66 | #[serde(skip_serializing_if = "Option::is_none")] 67 | pub hue: Option, 68 | #[serde(skip_serializing_if = "Option::is_none")] 69 | pub sat: Option, 70 | #[serde(skip_serializing_if = "Option::is_none")] 71 | pub effect: Option, 72 | #[serde(skip_serializing_if = "Option::is_none")] 73 | pub xy: Option<[f32; 2]>, 74 | #[serde(skip_serializing_if = "Option::is_none")] 75 | pub ct: Option, 76 | pub alert: String, 77 | #[serde(skip_serializing_if = "Option::is_none")] 78 | pub colormode: Option, 79 | pub mode: String, 80 | pub reachable: bool, 81 | } 82 | 83 | impl From for SendableState { 84 | fn from(state: State) -> Self { 85 | Self { 86 | on: Some(state.on), 87 | bri: state.bri, 88 | hue: state.hue, 89 | sat: state.sat, 90 | effect: state.effect, 91 | xy: state.xy, 92 | alert: None, 93 | transitiontime: Some(1), 94 | colormode: state.colormode, 95 | } 96 | } 97 | } 98 | 99 | impl std::fmt::Display for Light { 100 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 101 | match &*self { 102 | Self::LightBulb { state, name, .. } => write!(f, "{{ on: {} }} : {}", state.on, name), 103 | Self::LightStrip { state, name, .. } => write!(f, "{{ on: {} }} : {}", state.on, name), 104 | } 105 | } 106 | } 107 | 108 | /// Light enum representing the complete state of possible lights 109 | #[derive(Serialize, Deserialize, Debug, Clone)] 110 | #[serde(untagged)] 111 | pub enum Light { 112 | LightBulb { 113 | state: State, 114 | swupdate: Value, 115 | r#type: String, 116 | name: String, 117 | modelid: String, 118 | manufacturername: String, 119 | productname: String, 120 | capabilities: Value, 121 | config: Value, 122 | uniqueid: String, 123 | swversion: String, 124 | swconfigid: String, 125 | productid: String, 126 | }, 127 | LightStrip { 128 | state: State, 129 | swupdate: Value, 130 | r#type: String, 131 | name: String, 132 | modelid: String, 133 | manufacturername: String, 134 | productname: String, 135 | capabilities: Value, 136 | config: Value, 137 | uniqueid: String, 138 | swversion: String, 139 | }, 140 | } 141 | 142 | /// Super useful macro to create `SendableState` 143 | /// ``` 144 | /// use lighthouse::{lights::*, state}; 145 | /// // Usage examples 146 | /// // Returns a reference, most useful as a default 147 | /// let sendable_state: &SendableState = state!(on: true, xy: [1.0, 0.0]); 148 | /// 149 | /// // Returns a value, still useful. 150 | /// let sendable_state: SendableState = state!(nonref; on: true, xy: [1.0, 0.0]); 151 | /// ``` 152 | #[macro_export] 153 | macro_rules! state { 154 | ($($i:ident:$v:expr), *) => { 155 | &$crate::lights::SendableState { 156 | $($i: Some($v),) * 157 | ..$crate::lights::SendableState::default() 158 | } 159 | }; 160 | 161 | (nonref; $($i:ident:$v:expr),*) => { 162 | $crate::lights::SendableState { 163 | $($i: Some($v),)* 164 | ..$crate::lights::SendableState::default() 165 | } 166 | }; 167 | 168 | (from: $state:expr; $($i:ident:$v:expr),*) => {{ 169 | let mut sendable = $crate::lights::SendableState::from($state); 170 | $(sendable.$i = Some($v);)* 171 | sendable 172 | }}; 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | /// # Helpers 2 | /// 3 | /// The helpers module contains functions that assist the rest of the codebase 4 | /// it is unlikely that any of these internals will have to be used manually. pub mod helpers {// imports use std::net::IpAddr; use url::Url; /// Generates the target URL for the bridge pub fn generate_target(address: IpAddr, token: &str) -> Result {let mut target = Url::parse("http://localhost").unwrap(); // Unwrap as it can't fail in parsing let path = format!("api/{}/", token); target.set_path(&path[..]); if target.set_ip_host(address).is_ok() {return Ok(target);} Err(())} pub mod network {use crate::lights::SendableState; use url::Url; /// Defines the allowed methods to be sent to bridge pub enum AllowedMethod {GET, PUT, POST,} /// Implementated to allow controlled conversion into reqwest /// methods and not allow undefined methods to be sent to bridge impl std::convert::From for reqwest::Method {fn from(value: AllowedMethod) -> Self {match value {AllowedMethod::GET => reqwest::Method::GET, AllowedMethod::POST => reqwest::Method::POST, AllowedMethod::PUT => reqwest::Method::PUT,}}} /// This type alias is a URL and a type of Request to be sent pub type RequestTarget = (Url, AllowedMethod); /// Convenience type alias for a possible Result from the reqwest client type ResponseResult = Result; type IndexedResponseResult = (usize, ResponseResult); /// Function wrapping the request sending functionality /// to a location. pub async fn send_request(request_target: RequestTarget, state: Option<&SendableState>, client: &reqwest::Client,) -> ResponseResult {let (target, method) = request_target; match method {AllowedMethod::POST => client.post(target).json(&state).send().await, AllowedMethod::GET => client.get(target).send().await, AllowedMethod::PUT => client.put(target).json(&state).send().await,}} pub async fn send_request_indexed(index: usize, request_target: RequestTarget, state: Option<&SendableState>, client: &reqwest::Client,) -> IndexedResponseResult {(index, send_request(request_target, state, client).await)} /// Function that sends off several states to the lights /// This is much more key than individual requests functionality provided by the /// send_request function as this is allowing us to do this asynchronously across /// an arbitrary selection of lights. pub async fn send_requests(request_targets: impl IntoIterator, states: impl IntoIterator>, client: &reqwest::Client,) -> Vec {use tokio::stream::StreamExt; let mut f: futures::stream::FuturesUnordered<_> = request_targets .into_iter() .zip(states.into_iter()) .enumerate() .map(|(i, (target, state))| send_request_indexed(i, target, state, client)) .collect(); let mut res = Vec::with_capacity(f.len()); while let Some(tup) = f.next().await {res.push(tup);} res.sort_by_key(|tuple| tuple.0); res.into_iter().map(|tup| tup.1).collect()}}} 5 | // imports 6 | use std::net::IpAddr; 7 | use url::Url; 8 | 9 | /// Generates the target URL for the bridge 10 | pub fn generate_target(address: IpAddr, token: &str) -> Result { 11 | let mut target = Url::parse("http://localhost").unwrap(); // Unwrap as it can't fail in parsing 12 | let path = format!("api/{}/", token); 13 | target.set_path(&path[..]); 14 | if target.set_ip_host(address).is_ok() { 15 | return Ok(target); 16 | } 17 | Err(()) 18 | } 19 | 20 | pub mod network { 21 | use crate::lights::SendableState; 22 | use url::Url; 23 | 24 | /// Defines the allowed methods to be sent to bridge 25 | pub enum AllowedMethod { 26 | GET, 27 | PUT, 28 | POST, 29 | } 30 | 31 | /// Implementated to allow controlled conversion into reqwest 32 | /// methods and not allow undefined methods to be sent to bridge 33 | impl std::convert::From for reqwest::Method { 34 | fn from(value: AllowedMethod) -> Self { 35 | match value { 36 | AllowedMethod::GET => reqwest::Method::GET, 37 | AllowedMethod::POST => reqwest::Method::POST, 38 | AllowedMethod::PUT => reqwest::Method::PUT, 39 | } 40 | } 41 | } 42 | 43 | /// This type alias is a URL and a type of Request to be sent 44 | pub type RequestTarget = (Url, AllowedMethod); 45 | 46 | /// Convenience type alias for a possible Result from the reqwest client 47 | type ResponseResult = Result; 48 | type IndexedResponseResult = (usize, ResponseResult); 49 | 50 | /// Function wrapping the request sending functionality 51 | /// to a location. 52 | pub async fn send_request( 53 | request_target: RequestTarget, 54 | state: Option<&SendableState>, 55 | client: &reqwest::Client, 56 | ) -> ResponseResult { 57 | let (target, method) = request_target; 58 | match method { 59 | AllowedMethod::POST => client.post(target).json(&state).send().await, 60 | AllowedMethod::GET => client.get(target).send().await, 61 | AllowedMethod::PUT => client.put(target).json(&state).send().await, 62 | } 63 | } 64 | 65 | pub async fn send_request_indexed( 66 | index: usize, 67 | request_target: RequestTarget, 68 | state: Option<&SendableState>, 69 | client: &reqwest::Client, 70 | ) -> IndexedResponseResult { 71 | (index, send_request(request_target, state, client).await) 72 | } 73 | 74 | /// Function that sends off several states to the lights 75 | /// This is much more key than individual requests functionality provided by the 76 | /// send_request function as this is allowing us to do this asynchronously across 77 | /// an arbitrary selection of lights. 78 | pub async fn send_requests( 79 | request_targets: impl IntoIterator, 80 | states: impl IntoIterator>, 81 | client: &reqwest::Client, 82 | ) -> Vec { 83 | use tokio::stream::StreamExt; 84 | let mut f: futures::stream::FuturesUnordered<_> = request_targets 85 | .into_iter() 86 | .zip(states.into_iter()) 87 | .enumerate() 88 | .map(|(i, (target, state))| send_request_indexed(i, target, state, client)) 89 | .collect(); 90 | let mut res = Vec::with_capacity(f.len()); 91 | while let Some(tup) = f.next().await { 92 | res.push(tup); 93 | } 94 | res.sort_by_key(|tuple| tuple.0); 95 | res.into_iter().map(|tup| tup.1).collect() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/bridge.rs: -------------------------------------------------------------------------------- 1 | /// # Bridge module 2 | /// 3 | /// This module contains the Bridge and related functionality 4 | // imports 5 | use super::{ 6 | helpers::{network::*, *}, 7 | lights::*, 8 | }; 9 | use std::cell::RefCell; 10 | use std::collections::BTreeMap; 11 | use std::net::IpAddr; 12 | use tokio::runtime::Runtime; 13 | use url::Url; 14 | 15 | /// # Take it to the Bridge! 16 | /// 17 | /// This is the Bridge object - the core of the library. 18 | /// 19 | /// This is the manager struct that implements all the core methods that are required to interact 20 | /// with the Hue Bridge. It has a bunch of convenience functions such as sending state, scanning for lights, 21 | /// getting the information about your existing lights and finding bridge IP addresses with SSDP. 22 | /// 23 | /// Additional features can be enabled: 24 | /// - `persist` - enables building a bridge from environment variables and serialising to file 25 | #[derive(Debug)] 26 | pub struct Bridge { 27 | pub target: Url, 28 | ip: IpAddr, 29 | token: String, 30 | client: reqwest::Client, 31 | // TODO: The use of refcells here does not make it multithread safe. Might be worth adding that later with a feature flag? 32 | runtime: RefCell, 33 | // TODO: Unclear if this is the best way to structure this. Should refcell be inside? 34 | lights: RefCell>>, 35 | light_ids: RefCell>>, 36 | } 37 | 38 | impl Bridge { 39 | /// Constructor for a bridge from and IP and a token 40 | pub fn new(ip: IpAddr, token: String) -> Result { 41 | let target = generate_target(ip, &token)?; 42 | let client = reqwest::Client::new(); 43 | let runtime = RefCell::new( 44 | tokio::runtime::Builder::new() 45 | .basic_scheduler() 46 | .enable_all() 47 | .build() 48 | .expect("Could not create tokio runtime during the creation of the Bridge"), 49 | ); 50 | let lights = RefCell::new(None); 51 | let light_ids = RefCell::new(None); 52 | Ok(Bridge { 53 | target, 54 | ip, 55 | token, 56 | client, 57 | runtime, 58 | lights, 59 | light_ids, 60 | }) 61 | } 62 | 63 | /// Scan the existing lights on the network. Returns the light id 64 | /// mapped to the light object. 65 | fn scan(&self) -> BTreeMap { 66 | let endpoint = self.get_endpoint("./lights", AllowedMethod::GET); 67 | let fut = send_request(endpoint, None, &self.client); 68 | let lights: BTreeMap = self 69 | .runtime 70 | .borrow_mut() 71 | .block_on(async { fut.await?.json().await }) 72 | .expect("Could not completely decode/send request"); 73 | lights 74 | } 75 | 76 | /// Updates the lights in the system by scanning them if 77 | /// the user either forces it through the bool parameter or 78 | /// the inner option of the RefCell is None. 79 | fn update_lights(&self, force: bool) { 80 | if self.lights.borrow().is_none() || force { 81 | let lights = self.scan(); 82 | let ids = lights.keys().cloned().collect(); 83 | self.lights.replace(Some(lights)); 84 | self.light_ids.replace(Some(ids)); 85 | } 86 | } 87 | 88 | /// Get the lights from the bridge struct. It performs a rescan and 89 | /// updates a private lights field in the Bridge if that has not been 90 | /// done yet. 91 | /// 92 | /// This performs a clone rather than returning a reference. 93 | pub fn get_lights(&self) -> BTreeMap { 94 | self.update_lights(false); 95 | self.lights 96 | .borrow() 97 | .clone() 98 | .expect("This should always be some since we update before accessing") 99 | } 100 | 101 | /// Sends a state to a given light by its ID on the system. 102 | /// 103 | /// This is useful when you want to send a given state to one light 104 | /// on the network. 105 | pub fn state_to(&self, id: u8, new_state: &SendableState) -> reqwest::Response { 106 | let endpoint = self.get_endpoint(&format!("./lights/{}/state", id)[..], AllowedMethod::PUT); 107 | self.runtime 108 | .borrow_mut() 109 | .block_on(send_request(endpoint, Some(new_state), &self.client)) 110 | .expect(&format!("Could not send state to light: {}", id)[..]) 111 | } 112 | 113 | /// Sends a state to all lights in the system 114 | /// 115 | /// The method sends a given state change to all possible lights on the system. 116 | /// It also performs an update using `Self::update_lights` in order to make sure all 117 | /// lights have been retrieved at least once. 118 | pub fn to_all( 119 | &self, 120 | new_state: &SendableState, 121 | ) -> Result, reqwest::Error> { 122 | self.update_lights(false); 123 | let endpoints: Vec<_> = self 124 | .light_ids // get lights 125 | .borrow() 126 | .as_ref() 127 | .map(|ids| { 128 | ids.iter().map(|id| { 129 | self.get_endpoint(&format!("./lights/{}/state", id)[..], AllowedMethod::PUT) 130 | }) 131 | }) 132 | .expect("No values in lights") 133 | .collect(); 134 | self.runtime 135 | .borrow_mut() 136 | .block_on(send_requests( 137 | endpoints, 138 | std::iter::repeat(Some(new_state)), 139 | &self.client, 140 | )) 141 | .into_iter() 142 | .collect() 143 | } 144 | 145 | /// Send a state object to all lights on the network. 146 | pub fn state_to_multiple<'a>( 147 | &self, 148 | ids: impl IntoIterator, 149 | new_states: impl IntoIterator, 150 | ) -> Result, reqwest::Error> { 151 | let endpoints: Vec<_> = ids 152 | .into_iter() 153 | .map(|id| self.get_endpoint(&format!("./lights/{}/state", id)[..], AllowedMethod::PUT)) 154 | .collect(); 155 | let states = new_states.into_iter().map(Some); 156 | 157 | self.runtime 158 | .borrow_mut() 159 | .block_on(send_requests(endpoints, states, &self.client)) 160 | .into_iter() 161 | .collect() 162 | } 163 | 164 | /// Provided an endpoint string, and a method it will create a `RequestTarget` that can 165 | /// be sent a request. The final URI will depend on the `self.target` field and the string 166 | /// provided. 167 | fn get_endpoint(&self, s: &str, method: AllowedMethod) -> RequestTarget { 168 | (self.target.join(s).unwrap(), method) 169 | } 170 | 171 | /// Method to interactively register a new bridge. 172 | /// 173 | /// This interacts with the user and guides them through the authentication flow to instantiate 174 | /// a new bridge. 175 | /// 176 | /// The interactive parameter, if true, will enable printing out of instructions to 177 | /// stdout. 178 | /// 179 | /// This returns a [Bridge](struct.Bridge.html) and a token. The reason for a 180 | /// returned token is that a user might want to store a token, however the struct 181 | /// field is private by default on the bridge, so we expose the token upon registration 182 | /// for the user to store it as they might see fit. 183 | pub fn try_register(interactive: bool) -> Result<(Self, String), String> { 184 | use serde_json::Value; 185 | 186 | let client = reqwest::Client::new(); 187 | let mut runtime = tokio::runtime::Builder::new() 188 | .basic_scheduler() 189 | .enable_all() 190 | .build() 191 | .expect("Could not create tokio runtime during registration"); 192 | 193 | let bridges = Self::find_bridges(); // find any bridges present on network 194 | 195 | if interactive { 196 | println!("Found the following bridges:\n{:#?}", bridges); 197 | } 198 | 199 | let body = serde_json::json!({ "devicetype": "lighthouse" }); 200 | let mut check = |ip: IpAddr| -> Value { 201 | runtime.block_on(async { 202 | client 203 | .post(&format!("http://{}/api", ip)) 204 | .json(&body) 205 | .send() 206 | .await 207 | .unwrap() 208 | .json() 209 | .await 210 | .unwrap() 211 | }) 212 | }; 213 | 214 | let bridge_ip; 215 | let mut response; 216 | 217 | if bridges.is_empty() { 218 | return Err(String::from("Could not find any bridges on the network")); 219 | } else { 220 | if interactive { 221 | println!("Will try to register. Please press the connection button on your bridge"); 222 | } 223 | 'wait_for_button: loop { 224 | if interactive { 225 | println!("Waiting for button press..."); 226 | } 227 | std::thread::sleep(std::time::Duration::from_secs(3)); 228 | for ip in &bridges { 229 | response = check(*ip); 230 | if response[0]["error"]["type"] == 101 { 231 | continue; 232 | } else { 233 | bridge_ip = ip; 234 | break 'wait_for_button; 235 | } 236 | } 237 | } 238 | } 239 | 240 | let token = response[0]["success"]["username"].to_string(); 241 | let target = generate_target(*bridge_ip, &token) 242 | .expect("Could not create the required target after registration"); 243 | 244 | Ok(( 245 | Bridge { 246 | target, 247 | ip: *bridge_ip, 248 | token: token.clone(), 249 | client, 250 | runtime: RefCell::new(runtime), 251 | lights: RefCell::new(None), 252 | light_ids: RefCell::new(None), 253 | }, 254 | token, 255 | )) 256 | } 257 | 258 | /// Method to find bridge IP addressed on the network. 259 | /// 260 | /// If multiple are found, they are all returned. 261 | pub fn find_bridges() -> Vec { 262 | use ssdp::header::{HeaderMut, Man, MX, ST}; 263 | use ssdp::message::{Multicast, SearchRequest}; 264 | 265 | println!("Searching for bridges (5s)..."); 266 | // create request with required headers for the sddp search 267 | let mut request = SearchRequest::new(); 268 | request.set(Man); 269 | request.set(MX(5)); 270 | request.set(ST::Target(ssdp::FieldMap::URN( 271 | "urn:schemas-upnp-org:device:Basic:1".into(), 272 | ))); 273 | 274 | // Find devices 275 | let devices = request 276 | .multicast() 277 | .expect("Could not perform multicast request"); 278 | 279 | // Coerce this into a vector 280 | let mut result: Vec = devices.into_iter().map(|(_, src)| src.ip()).collect(); 281 | 282 | result.sort(); // TODO: see if this is necessary 283 | result.dedup(); 284 | 285 | result 286 | } 287 | 288 | /// Print useful information about the state of your system 289 | /// 290 | /// Namely, asks for all available lights and print a JSON representation 291 | /// of the system to STDOUT. 292 | pub fn system_info(&self) { 293 | let lights = self.scan(); 294 | println!("{}", serde_json::to_string_pretty(&lights).unwrap()); 295 | } 296 | 297 | /// Conditional feature: 298 | /// 299 | /// If `persist` feature is enabled, then this allows creating a bridge from environment 300 | /// variables. 301 | /// 302 | /// The variables that will be looked up are: 303 | /// - HUE_BRIDGE_IP - the IP of the bridge on the local network 304 | /// - HUE_BRIDGE_KEY - the KEY that you get when you register to the bridge. 305 | #[cfg(feature = "persist")] 306 | pub fn from_env() -> Bridge { 307 | let ip = std::env::var("HUE_BRIDGE_IP") 308 | .expect("Could not find `HUE_BRIDGE_IP` environment variable.") 309 | .parse() 310 | .expect("Could not parse the address in the variable: HUE_BRIDGE_IP."); 311 | let key = std::env::var("HUE_BRIDGE_KEY") 312 | .expect("Could not find `HUE_BRIDGE_KEY` environment variable."); 313 | 314 | Bridge::new(ip, key).expect(&format!("Could not create new bridge (IP {})", ip)[..]) 315 | } 316 | 317 | /// Conditional feature: 318 | /// 319 | /// Allows serializing the bridge to a text file. It is super simple 320 | /// and basically ends up writing out the bridge target to a text file. 321 | /// 322 | /// TODO: Move to using serde serialization 323 | #[cfg(feature = "persist")] 324 | pub fn to_file(&self, filename: &str) -> std::io::Result<()> { 325 | use std::io::prelude::*; 326 | let mut file = std::fs::File::create(filename)?; 327 | file.write_all(format!("{}\n{}", self.ip, self.token).as_ref())?; 328 | Ok(()) 329 | } 330 | 331 | /// Conditional feature: 332 | /// 333 | /// Allows loading a Bridge from a text file. 334 | #[cfg(feature = "persist")] 335 | pub fn from_file(filename: &str) -> std::io::Result { 336 | use std::io::{BufRead, BufReader}; 337 | let file = std::fs::File::open(filename)?; 338 | let reader = BufReader::new(file); 339 | 340 | let lines: Vec = reader 341 | .lines() 342 | .enumerate() 343 | .map(|(idx, line)| line.unwrap_or_else(|_| format!("Could not read line {}", idx))) 344 | .collect(); 345 | 346 | assert!(lines.len() == 2); 347 | Ok(Bridge::new( 348 | lines[0].parse().expect("Could not parse the provided IP"), 349 | lines[1].clone(), 350 | ) 351 | .expect("Could not create Bridge")) 352 | } 353 | } 354 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "addr2line" 5 | version = "0.13.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" 8 | dependencies = [ 9 | "gimli", 10 | ] 11 | 12 | [[package]] 13 | name = "adler" 14 | version = "0.2.3" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 17 | 18 | [[package]] 19 | name = "approx" 20 | version = "0.3.2" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" 23 | dependencies = [ 24 | "num-traits", 25 | ] 26 | 27 | [[package]] 28 | name = "autocfg" 29 | version = "1.0.0" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 32 | 33 | [[package]] 34 | name = "backtrace" 35 | version = "0.3.50" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" 38 | dependencies = [ 39 | "addr2line", 40 | "cfg-if", 41 | "libc", 42 | "miniz_oxide", 43 | "object", 44 | "rustc-demangle", 45 | ] 46 | 47 | [[package]] 48 | name = "base64" 49 | version = "0.9.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" 52 | dependencies = [ 53 | "byteorder", 54 | "safemem", 55 | ] 56 | 57 | [[package]] 58 | name = "base64" 59 | version = "0.12.3" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 62 | 63 | [[package]] 64 | name = "bitflags" 65 | version = "1.2.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 68 | 69 | [[package]] 70 | name = "bumpalo" 71 | version = "3.4.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" 74 | 75 | [[package]] 76 | name = "byteorder" 77 | version = "1.3.4" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 80 | 81 | [[package]] 82 | name = "bytes" 83 | version = "0.5.5" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "118cf036fbb97d0816e3c34b2d7a1e8cfc60f68fcf63d550ddbe9bd5f59c213b" 86 | dependencies = [ 87 | "loom", 88 | ] 89 | 90 | [[package]] 91 | name = "c_linked_list" 92 | version = "1.1.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" 95 | 96 | [[package]] 97 | name = "cc" 98 | version = "1.0.58" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" 101 | 102 | [[package]] 103 | name = "cfg-if" 104 | version = "0.1.10" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 107 | 108 | [[package]] 109 | name = "dtoa" 110 | version = "0.4.6" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" 113 | 114 | [[package]] 115 | name = "encoding_rs" 116 | version = "0.8.23" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" 119 | dependencies = [ 120 | "cfg-if", 121 | ] 122 | 123 | [[package]] 124 | name = "error-chain" 125 | version = "0.10.0" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" 128 | dependencies = [ 129 | "backtrace", 130 | ] 131 | 132 | [[package]] 133 | name = "fnv" 134 | version = "1.0.7" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 137 | 138 | [[package]] 139 | name = "fuchsia-zircon" 140 | version = "0.3.3" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 143 | dependencies = [ 144 | "bitflags", 145 | "fuchsia-zircon-sys", 146 | ] 147 | 148 | [[package]] 149 | name = "fuchsia-zircon-sys" 150 | version = "0.3.3" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 153 | 154 | [[package]] 155 | name = "futures" 156 | version = "0.3.5" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" 159 | dependencies = [ 160 | "futures-channel", 161 | "futures-core", 162 | "futures-executor", 163 | "futures-io", 164 | "futures-sink", 165 | "futures-task", 166 | "futures-util", 167 | ] 168 | 169 | [[package]] 170 | name = "futures-channel" 171 | version = "0.3.5" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" 174 | dependencies = [ 175 | "futures-core", 176 | "futures-sink", 177 | ] 178 | 179 | [[package]] 180 | name = "futures-core" 181 | version = "0.3.5" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" 184 | 185 | [[package]] 186 | name = "futures-executor" 187 | version = "0.3.5" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" 190 | dependencies = [ 191 | "futures-core", 192 | "futures-task", 193 | "futures-util", 194 | ] 195 | 196 | [[package]] 197 | name = "futures-io" 198 | version = "0.3.5" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" 201 | 202 | [[package]] 203 | name = "futures-macro" 204 | version = "0.3.5" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" 207 | dependencies = [ 208 | "proc-macro-hack", 209 | "proc-macro2", 210 | "quote", 211 | "syn", 212 | ] 213 | 214 | [[package]] 215 | name = "futures-sink" 216 | version = "0.3.5" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" 219 | 220 | [[package]] 221 | name = "futures-task" 222 | version = "0.3.5" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" 225 | dependencies = [ 226 | "once_cell", 227 | ] 228 | 229 | [[package]] 230 | name = "futures-util" 231 | version = "0.3.5" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" 234 | dependencies = [ 235 | "futures-channel", 236 | "futures-core", 237 | "futures-io", 238 | "futures-macro", 239 | "futures-sink", 240 | "futures-task", 241 | "memchr", 242 | "pin-project", 243 | "pin-utils", 244 | "proc-macro-hack", 245 | "proc-macro-nested", 246 | "slab", 247 | ] 248 | 249 | [[package]] 250 | name = "gcc" 251 | version = "0.3.55" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" 254 | 255 | [[package]] 256 | name = "generator" 257 | version = "0.6.21" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "add72f17bb81521258fcc8a7a3245b1e184e916bfbe34f0ea89558f440df5c68" 260 | dependencies = [ 261 | "cc", 262 | "libc", 263 | "log 0.4.8", 264 | "rustc_version", 265 | "winapi 0.3.9", 266 | ] 267 | 268 | [[package]] 269 | name = "get_if_addrs" 270 | version = "0.5.3" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "abddb55a898d32925f3148bd281174a68eeb68bbfd9a5938a57b18f506ee4ef7" 273 | dependencies = [ 274 | "c_linked_list", 275 | "get_if_addrs-sys", 276 | "libc", 277 | "winapi 0.2.8", 278 | ] 279 | 280 | [[package]] 281 | name = "get_if_addrs-sys" 282 | version = "0.1.1" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "0d04f9fb746cf36b191c00f3ede8bde9c8e64f9f4b05ae2694a9ccf5e3f5ab48" 285 | dependencies = [ 286 | "gcc", 287 | "libc", 288 | ] 289 | 290 | [[package]] 291 | name = "getrandom" 292 | version = "0.1.14" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 295 | dependencies = [ 296 | "cfg-if", 297 | "libc", 298 | "wasi", 299 | ] 300 | 301 | [[package]] 302 | name = "gimli" 303 | version = "0.22.0" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" 306 | 307 | [[package]] 308 | name = "h2" 309 | version = "0.2.5" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" 312 | dependencies = [ 313 | "bytes", 314 | "fnv", 315 | "futures-core", 316 | "futures-sink", 317 | "futures-util", 318 | "http", 319 | "indexmap", 320 | "log 0.4.8", 321 | "slab", 322 | "tokio", 323 | "tokio-util", 324 | ] 325 | 326 | [[package]] 327 | name = "hermit-abi" 328 | version = "0.1.15" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" 331 | dependencies = [ 332 | "libc", 333 | ] 334 | 335 | [[package]] 336 | name = "http" 337 | version = "0.2.1" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 340 | dependencies = [ 341 | "bytes", 342 | "fnv", 343 | "itoa", 344 | ] 345 | 346 | [[package]] 347 | name = "http-body" 348 | version = "0.3.1" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 351 | dependencies = [ 352 | "bytes", 353 | "http", 354 | ] 355 | 356 | [[package]] 357 | name = "httparse" 358 | version = "1.3.4" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 361 | 362 | [[package]] 363 | name = "hyper" 364 | version = "0.10.16" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" 367 | dependencies = [ 368 | "base64 0.9.3", 369 | "httparse", 370 | "language-tags", 371 | "log 0.3.9", 372 | "mime 0.2.6", 373 | "num_cpus", 374 | "time", 375 | "traitobject", 376 | "typeable", 377 | "unicase 1.4.2", 378 | "url 1.7.2", 379 | ] 380 | 381 | [[package]] 382 | name = "hyper" 383 | version = "0.13.6" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "a6e7655b9594024ad0ee439f3b5a7299369dc2a3f459b47c696f9ff676f9aa1f" 386 | dependencies = [ 387 | "bytes", 388 | "futures-channel", 389 | "futures-core", 390 | "futures-util", 391 | "h2", 392 | "http", 393 | "http-body", 394 | "httparse", 395 | "itoa", 396 | "log 0.4.8", 397 | "pin-project", 398 | "socket2", 399 | "time", 400 | "tokio", 401 | "tower-service", 402 | "want", 403 | ] 404 | 405 | [[package]] 406 | name = "idna" 407 | version = "0.1.5" 408 | source = "registry+https://github.com/rust-lang/crates.io-index" 409 | checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" 410 | dependencies = [ 411 | "matches", 412 | "unicode-bidi", 413 | "unicode-normalization", 414 | ] 415 | 416 | [[package]] 417 | name = "idna" 418 | version = "0.2.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 421 | dependencies = [ 422 | "matches", 423 | "unicode-bidi", 424 | "unicode-normalization", 425 | ] 426 | 427 | [[package]] 428 | name = "indexmap" 429 | version = "1.4.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" 432 | dependencies = [ 433 | "autocfg", 434 | ] 435 | 436 | [[package]] 437 | name = "iovec" 438 | version = "0.1.4" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 441 | dependencies = [ 442 | "libc", 443 | ] 444 | 445 | [[package]] 446 | name = "itoa" 447 | version = "0.4.6" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 450 | 451 | [[package]] 452 | name = "js-sys" 453 | version = "0.3.41" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "c4b9172132a62451e56142bff9afc91c8e4a4500aa5b847da36815b63bfda916" 456 | dependencies = [ 457 | "wasm-bindgen", 458 | ] 459 | 460 | [[package]] 461 | name = "kernel32-sys" 462 | version = "0.2.2" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 465 | dependencies = [ 466 | "winapi 0.2.8", 467 | "winapi-build", 468 | ] 469 | 470 | [[package]] 471 | name = "language-tags" 472 | version = "0.2.2" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" 475 | 476 | [[package]] 477 | name = "lazy_static" 478 | version = "1.4.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 481 | 482 | [[package]] 483 | name = "libc" 484 | version = "0.2.72" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" 487 | 488 | [[package]] 489 | name = "lighthouse" 490 | version = "0.2.1" 491 | dependencies = [ 492 | "futures", 493 | "palette", 494 | "reqwest", 495 | "serde", 496 | "serde_json", 497 | "ssdp", 498 | "tokio", 499 | "url 2.1.1", 500 | ] 501 | 502 | [[package]] 503 | name = "log" 504 | version = "0.3.9" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" 507 | dependencies = [ 508 | "log 0.4.8", 509 | ] 510 | 511 | [[package]] 512 | name = "log" 513 | version = "0.4.8" 514 | source = "registry+https://github.com/rust-lang/crates.io-index" 515 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 516 | dependencies = [ 517 | "cfg-if", 518 | ] 519 | 520 | [[package]] 521 | name = "loom" 522 | version = "0.3.4" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "4ecc775857611e1df29abba5c41355cdf540e7e9d4acfdf0f355eefee82330b7" 525 | dependencies = [ 526 | "cfg-if", 527 | "generator", 528 | "scoped-tls", 529 | ] 530 | 531 | [[package]] 532 | name = "matches" 533 | version = "0.1.8" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 536 | 537 | [[package]] 538 | name = "memchr" 539 | version = "2.3.3" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 542 | 543 | [[package]] 544 | name = "mime" 545 | version = "0.2.6" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" 548 | dependencies = [ 549 | "log 0.3.9", 550 | ] 551 | 552 | [[package]] 553 | name = "mime" 554 | version = "0.3.16" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 557 | 558 | [[package]] 559 | name = "mime_guess" 560 | version = "2.0.3" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 563 | dependencies = [ 564 | "mime 0.3.16", 565 | "unicase 2.6.0", 566 | ] 567 | 568 | [[package]] 569 | name = "miniz_oxide" 570 | version = "0.4.0" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" 573 | dependencies = [ 574 | "adler", 575 | ] 576 | 577 | [[package]] 578 | name = "mio" 579 | version = "0.6.22" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 582 | dependencies = [ 583 | "cfg-if", 584 | "fuchsia-zircon", 585 | "fuchsia-zircon-sys", 586 | "iovec", 587 | "kernel32-sys", 588 | "libc", 589 | "log 0.4.8", 590 | "miow", 591 | "net2", 592 | "slab", 593 | "winapi 0.2.8", 594 | ] 595 | 596 | [[package]] 597 | name = "miow" 598 | version = "0.2.1" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 601 | dependencies = [ 602 | "kernel32-sys", 603 | "net2", 604 | "winapi 0.2.8", 605 | "ws2_32-sys", 606 | ] 607 | 608 | [[package]] 609 | name = "net2" 610 | version = "0.2.34" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" 613 | dependencies = [ 614 | "cfg-if", 615 | "libc", 616 | "winapi 0.3.9", 617 | ] 618 | 619 | [[package]] 620 | name = "num-traits" 621 | version = "0.2.12" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 624 | dependencies = [ 625 | "autocfg", 626 | ] 627 | 628 | [[package]] 629 | name = "num_cpus" 630 | version = "1.13.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 633 | dependencies = [ 634 | "hermit-abi", 635 | "libc", 636 | ] 637 | 638 | [[package]] 639 | name = "object" 640 | version = "0.20.0" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" 643 | 644 | [[package]] 645 | name = "once_cell" 646 | version = "1.4.0" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 649 | 650 | [[package]] 651 | name = "palette" 652 | version = "0.5.0" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "a05c0334468e62a4dfbda34b29110aa7d70d58c7fdb2c9857b5874dd9827cc59" 655 | dependencies = [ 656 | "approx", 657 | "num-traits", 658 | "palette_derive", 659 | "phf", 660 | "phf_codegen", 661 | ] 662 | 663 | [[package]] 664 | name = "palette_derive" 665 | version = "0.5.0" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "0b4b5f600e60dd3a147fb57b4547033d382d1979eb087af310e91cb45a63b1f4" 668 | dependencies = [ 669 | "proc-macro2", 670 | "quote", 671 | "syn", 672 | ] 673 | 674 | [[package]] 675 | name = "percent-encoding" 676 | version = "1.0.1" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" 679 | 680 | [[package]] 681 | name = "percent-encoding" 682 | version = "2.1.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 685 | 686 | [[package]] 687 | name = "phf" 688 | version = "0.8.0" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" 691 | dependencies = [ 692 | "phf_shared", 693 | ] 694 | 695 | [[package]] 696 | name = "phf_codegen" 697 | version = "0.8.0" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" 700 | dependencies = [ 701 | "phf_generator", 702 | "phf_shared", 703 | ] 704 | 705 | [[package]] 706 | name = "phf_generator" 707 | version = "0.8.0" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" 710 | dependencies = [ 711 | "phf_shared", 712 | "rand", 713 | ] 714 | 715 | [[package]] 716 | name = "phf_shared" 717 | version = "0.8.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 720 | dependencies = [ 721 | "siphasher", 722 | ] 723 | 724 | [[package]] 725 | name = "pin-project" 726 | version = "0.4.22" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "12e3a6cdbfe94a5e4572812a0201f8c0ed98c1c452c7b8563ce2276988ef9c17" 729 | dependencies = [ 730 | "pin-project-internal", 731 | ] 732 | 733 | [[package]] 734 | name = "pin-project-internal" 735 | version = "0.4.22" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "6a0ffd45cf79d88737d7cc85bfd5d2894bee1139b356e616fe85dc389c61aaf7" 738 | dependencies = [ 739 | "proc-macro2", 740 | "quote", 741 | "syn", 742 | ] 743 | 744 | [[package]] 745 | name = "pin-project-lite" 746 | version = "0.1.7" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" 749 | 750 | [[package]] 751 | name = "pin-utils" 752 | version = "0.1.0" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 755 | 756 | [[package]] 757 | name = "ppv-lite86" 758 | version = "0.2.8" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" 761 | 762 | [[package]] 763 | name = "proc-macro-hack" 764 | version = "0.5.16" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" 767 | 768 | [[package]] 769 | name = "proc-macro-nested" 770 | version = "0.1.6" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 773 | 774 | [[package]] 775 | name = "proc-macro2" 776 | version = "1.0.18" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" 779 | dependencies = [ 780 | "unicode-xid", 781 | ] 782 | 783 | [[package]] 784 | name = "quote" 785 | version = "1.0.7" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 788 | dependencies = [ 789 | "proc-macro2", 790 | ] 791 | 792 | [[package]] 793 | name = "rand" 794 | version = "0.7.3" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 797 | dependencies = [ 798 | "getrandom", 799 | "libc", 800 | "rand_chacha", 801 | "rand_core", 802 | "rand_hc", 803 | "rand_pcg", 804 | ] 805 | 806 | [[package]] 807 | name = "rand_chacha" 808 | version = "0.2.2" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 811 | dependencies = [ 812 | "ppv-lite86", 813 | "rand_core", 814 | ] 815 | 816 | [[package]] 817 | name = "rand_core" 818 | version = "0.5.1" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 821 | dependencies = [ 822 | "getrandom", 823 | ] 824 | 825 | [[package]] 826 | name = "rand_hc" 827 | version = "0.2.0" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 830 | dependencies = [ 831 | "rand_core", 832 | ] 833 | 834 | [[package]] 835 | name = "rand_pcg" 836 | version = "0.2.1" 837 | source = "registry+https://github.com/rust-lang/crates.io-index" 838 | checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" 839 | dependencies = [ 840 | "rand_core", 841 | ] 842 | 843 | [[package]] 844 | name = "redox_syscall" 845 | version = "0.1.57" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 848 | 849 | [[package]] 850 | name = "reqwest" 851 | version = "0.10.6" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "3b82c9238b305f26f53443e3a4bc8528d64b8d0bee408ec949eb7bf5635ec680" 854 | dependencies = [ 855 | "base64 0.12.3", 856 | "bytes", 857 | "encoding_rs", 858 | "futures-core", 859 | "futures-util", 860 | "http", 861 | "http-body", 862 | "hyper 0.13.6", 863 | "js-sys", 864 | "lazy_static", 865 | "log 0.4.8", 866 | "mime 0.3.16", 867 | "mime_guess", 868 | "percent-encoding 2.1.0", 869 | "pin-project-lite", 870 | "serde", 871 | "serde_json", 872 | "serde_urlencoded", 873 | "tokio", 874 | "url 2.1.1", 875 | "wasm-bindgen", 876 | "wasm-bindgen-futures", 877 | "web-sys", 878 | "winreg", 879 | ] 880 | 881 | [[package]] 882 | name = "rustc-demangle" 883 | version = "0.1.16" 884 | source = "registry+https://github.com/rust-lang/crates.io-index" 885 | checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 886 | 887 | [[package]] 888 | name = "rustc_version" 889 | version = "0.2.3" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 892 | dependencies = [ 893 | "semver", 894 | ] 895 | 896 | [[package]] 897 | name = "ryu" 898 | version = "1.0.5" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 901 | 902 | [[package]] 903 | name = "safemem" 904 | version = "0.3.3" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 907 | 908 | [[package]] 909 | name = "scoped-tls" 910 | version = "0.1.2" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" 913 | 914 | [[package]] 915 | name = "semver" 916 | version = "0.9.0" 917 | source = "registry+https://github.com/rust-lang/crates.io-index" 918 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 919 | dependencies = [ 920 | "semver-parser", 921 | ] 922 | 923 | [[package]] 924 | name = "semver-parser" 925 | version = "0.7.0" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 928 | 929 | [[package]] 930 | name = "serde" 931 | version = "1.0.114" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" 934 | dependencies = [ 935 | "serde_derive", 936 | ] 937 | 938 | [[package]] 939 | name = "serde_derive" 940 | version = "1.0.114" 941 | source = "registry+https://github.com/rust-lang/crates.io-index" 942 | checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" 943 | dependencies = [ 944 | "proc-macro2", 945 | "quote", 946 | "syn", 947 | ] 948 | 949 | [[package]] 950 | name = "serde_json" 951 | version = "1.0.56" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3" 954 | dependencies = [ 955 | "itoa", 956 | "ryu", 957 | "serde", 958 | ] 959 | 960 | [[package]] 961 | name = "serde_urlencoded" 962 | version = "0.6.1" 963 | source = "registry+https://github.com/rust-lang/crates.io-index" 964 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 965 | dependencies = [ 966 | "dtoa", 967 | "itoa", 968 | "serde", 969 | "url 2.1.1", 970 | ] 971 | 972 | [[package]] 973 | name = "siphasher" 974 | version = "0.3.3" 975 | source = "registry+https://github.com/rust-lang/crates.io-index" 976 | checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" 977 | 978 | [[package]] 979 | name = "slab" 980 | version = "0.4.2" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 983 | 984 | [[package]] 985 | name = "socket2" 986 | version = "0.3.12" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 989 | dependencies = [ 990 | "cfg-if", 991 | "libc", 992 | "redox_syscall", 993 | "winapi 0.3.9", 994 | ] 995 | 996 | [[package]] 997 | name = "ssdp" 998 | version = "0.7.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "8c020eda46782b417016b689d4d409c8d1b3b4f98bbd51ff5003bb1b0a9041fa" 1001 | dependencies = [ 1002 | "error-chain", 1003 | "get_if_addrs", 1004 | "hyper 0.10.16", 1005 | "log 0.3.9", 1006 | "net2", 1007 | "time", 1008 | ] 1009 | 1010 | [[package]] 1011 | name = "syn" 1012 | version = "1.0.33" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" 1015 | dependencies = [ 1016 | "proc-macro2", 1017 | "quote", 1018 | "unicode-xid", 1019 | ] 1020 | 1021 | [[package]] 1022 | name = "time" 1023 | version = "0.1.43" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" 1026 | dependencies = [ 1027 | "libc", 1028 | "winapi 0.3.9", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "tinyvec" 1033 | version = "0.3.3" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "53953d2d3a5ad81d9f844a32f14ebb121f50b650cd59d0ee2a07cf13c617efed" 1036 | 1037 | [[package]] 1038 | name = "tokio" 1039 | version = "0.2.21" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" 1042 | dependencies = [ 1043 | "bytes", 1044 | "fnv", 1045 | "futures-core", 1046 | "iovec", 1047 | "lazy_static", 1048 | "memchr", 1049 | "mio", 1050 | "pin-project-lite", 1051 | "slab", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "tokio-util" 1056 | version = "0.3.1" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1059 | dependencies = [ 1060 | "bytes", 1061 | "futures-core", 1062 | "futures-sink", 1063 | "log 0.4.8", 1064 | "pin-project-lite", 1065 | "tokio", 1066 | ] 1067 | 1068 | [[package]] 1069 | name = "tower-service" 1070 | version = "0.3.0" 1071 | source = "registry+https://github.com/rust-lang/crates.io-index" 1072 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1073 | 1074 | [[package]] 1075 | name = "traitobject" 1076 | version = "0.1.0" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" 1079 | 1080 | [[package]] 1081 | name = "try-lock" 1082 | version = "0.2.3" 1083 | source = "registry+https://github.com/rust-lang/crates.io-index" 1084 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1085 | 1086 | [[package]] 1087 | name = "typeable" 1088 | version = "0.1.2" 1089 | source = "registry+https://github.com/rust-lang/crates.io-index" 1090 | checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" 1091 | 1092 | [[package]] 1093 | name = "unicase" 1094 | version = "1.4.2" 1095 | source = "registry+https://github.com/rust-lang/crates.io-index" 1096 | checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" 1097 | dependencies = [ 1098 | "version_check 0.1.5", 1099 | ] 1100 | 1101 | [[package]] 1102 | name = "unicase" 1103 | version = "2.6.0" 1104 | source = "registry+https://github.com/rust-lang/crates.io-index" 1105 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1106 | dependencies = [ 1107 | "version_check 0.9.2", 1108 | ] 1109 | 1110 | [[package]] 1111 | name = "unicode-bidi" 1112 | version = "0.3.4" 1113 | source = "registry+https://github.com/rust-lang/crates.io-index" 1114 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1115 | dependencies = [ 1116 | "matches", 1117 | ] 1118 | 1119 | [[package]] 1120 | name = "unicode-normalization" 1121 | version = "0.1.13" 1122 | source = "registry+https://github.com/rust-lang/crates.io-index" 1123 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 1124 | dependencies = [ 1125 | "tinyvec", 1126 | ] 1127 | 1128 | [[package]] 1129 | name = "unicode-xid" 1130 | version = "0.2.1" 1131 | source = "registry+https://github.com/rust-lang/crates.io-index" 1132 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1133 | 1134 | [[package]] 1135 | name = "url" 1136 | version = "1.7.2" 1137 | source = "registry+https://github.com/rust-lang/crates.io-index" 1138 | checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" 1139 | dependencies = [ 1140 | "idna 0.1.5", 1141 | "matches", 1142 | "percent-encoding 1.0.1", 1143 | ] 1144 | 1145 | [[package]] 1146 | name = "url" 1147 | version = "2.1.1" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1150 | dependencies = [ 1151 | "idna 0.2.0", 1152 | "matches", 1153 | "percent-encoding 2.1.0", 1154 | ] 1155 | 1156 | [[package]] 1157 | name = "version_check" 1158 | version = "0.1.5" 1159 | source = "registry+https://github.com/rust-lang/crates.io-index" 1160 | checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" 1161 | 1162 | [[package]] 1163 | name = "version_check" 1164 | version = "0.9.2" 1165 | source = "registry+https://github.com/rust-lang/crates.io-index" 1166 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1167 | 1168 | [[package]] 1169 | name = "want" 1170 | version = "0.3.0" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1173 | dependencies = [ 1174 | "log 0.4.8", 1175 | "try-lock", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "wasi" 1180 | version = "0.9.0+wasi-snapshot-preview1" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1183 | 1184 | [[package]] 1185 | name = "wasm-bindgen" 1186 | version = "0.2.64" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "6a634620115e4a229108b71bde263bb4220c483b3f07f5ba514ee8d15064c4c2" 1189 | dependencies = [ 1190 | "cfg-if", 1191 | "serde", 1192 | "serde_json", 1193 | "wasm-bindgen-macro", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "wasm-bindgen-backend" 1198 | version = "0.2.64" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "3e53963b583d18a5aa3aaae4b4c1cb535218246131ba22a71f05b518098571df" 1201 | dependencies = [ 1202 | "bumpalo", 1203 | "lazy_static", 1204 | "log 0.4.8", 1205 | "proc-macro2", 1206 | "quote", 1207 | "syn", 1208 | "wasm-bindgen-shared", 1209 | ] 1210 | 1211 | [[package]] 1212 | name = "wasm-bindgen-futures" 1213 | version = "0.4.14" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "dba48d66049d2a6cc8488702e7259ab7afc9043ad0dc5448444f46f2a453b362" 1216 | dependencies = [ 1217 | "cfg-if", 1218 | "js-sys", 1219 | "wasm-bindgen", 1220 | "web-sys", 1221 | ] 1222 | 1223 | [[package]] 1224 | name = "wasm-bindgen-macro" 1225 | version = "0.2.64" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "3fcfd5ef6eec85623b4c6e844293d4516470d8f19cd72d0d12246017eb9060b8" 1228 | dependencies = [ 1229 | "quote", 1230 | "wasm-bindgen-macro-support", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "wasm-bindgen-macro-support" 1235 | version = "0.2.64" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "9adff9ee0e94b926ca81b57f57f86d5545cdcb1d259e21ec9bdd95b901754c75" 1238 | dependencies = [ 1239 | "proc-macro2", 1240 | "quote", 1241 | "syn", 1242 | "wasm-bindgen-backend", 1243 | "wasm-bindgen-shared", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "wasm-bindgen-shared" 1248 | version = "0.2.64" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "7f7b90ea6c632dd06fd765d44542e234d5e63d9bb917ecd64d79778a13bd79ae" 1251 | 1252 | [[package]] 1253 | name = "web-sys" 1254 | version = "0.3.41" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "863539788676619aac1a23e2df3655e96b32b0e05eb72ca34ba045ad573c625d" 1257 | dependencies = [ 1258 | "js-sys", 1259 | "wasm-bindgen", 1260 | ] 1261 | 1262 | [[package]] 1263 | name = "winapi" 1264 | version = "0.2.8" 1265 | source = "registry+https://github.com/rust-lang/crates.io-index" 1266 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1267 | 1268 | [[package]] 1269 | name = "winapi" 1270 | version = "0.3.9" 1271 | source = "registry+https://github.com/rust-lang/crates.io-index" 1272 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1273 | dependencies = [ 1274 | "winapi-i686-pc-windows-gnu", 1275 | "winapi-x86_64-pc-windows-gnu", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "winapi-build" 1280 | version = "0.1.1" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1283 | 1284 | [[package]] 1285 | name = "winapi-i686-pc-windows-gnu" 1286 | version = "0.4.0" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1289 | 1290 | [[package]] 1291 | name = "winapi-x86_64-pc-windows-gnu" 1292 | version = "0.4.0" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1295 | 1296 | [[package]] 1297 | name = "winreg" 1298 | version = "0.7.0" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" 1301 | dependencies = [ 1302 | "winapi 0.3.9", 1303 | ] 1304 | 1305 | [[package]] 1306 | name = "ws2_32-sys" 1307 | version = "0.2.1" 1308 | source = "registry+https://github.com/rust-lang/crates.io-index" 1309 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1310 | dependencies = [ 1311 | "winapi 0.2.8", 1312 | "winapi-build", 1313 | ] 1314 | --------------------------------------------------------------------------------