spaceapi_server/lib.rs
1//! This crate enables you to create your own SpaceAPI server endpoint using
2//! Rust. In the end you'll get a single binary that you can run on your
3//! server.
4//!
5//!
6//! ## Requirements
7//!
8//! On the build machine:
9//!
10//! - Rust and Cargo ([https://rustup.rs/](https://rustup.rs/))
11//!
12//! On the server:
13//!
14//! - Redis
15//!
16//! The Redis instance will be used to store dynamic data like sensor values,
17//! as well as keys for dynamic data update authentication.
18//!
19//!
20//! ## Getting Started
21//!
22//! Create a new Rust project:
23//!
24//! ```text
25//! cargo new --bin mystatus
26//! ```
27//!
28//! Add the `spaceapi-server` dependency to `Cargo.toml`:
29//!
30//! ```toml
31//! [dependencies]
32//! spaceapi-server = "0.7"
33//! ```
34//!
35//! Create a `main.rs`:
36//!
37//! ```no_run
38//! use spaceapi_server::api::{Contact, Location, StatusBuilder};
39//! use spaceapi_server::SpaceapiServerBuilder;
40//!
41//! fn main() {
42//! // Create new minimal v14 Status instance
43//! let status = StatusBuilder::v14("coredump")
44//! .logo("https://www.coredump.ch/logo.png")
45//! .url("https://www.coredump.ch/")
46//! .location(Location {
47//! address: Some("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".into()),
48//! lat: 47.22936,
49//! lon: 8.82949,
50//! timezone: None,
51//! })
52//! .contact(Contact {
53//! irc: Some("irc://freenode.net/#coredump".into()),
54//! twitter: Some("@coredump_ch".into()),
55//! ..Default::default()
56//! })
57//! .build()
58//! .expect("Creating status failed");
59//!
60//! // Set up server
61//! let server = SpaceapiServerBuilder::new(status)
62//! .redis_connection_info("redis://127.0.0.1/")
63//! .build()
64//! .unwrap();
65//!
66//! // Serve!
67//! let _ = server.serve("127.0.0.1:8000");
68//! }
69//! ```
70//!
71//! Now you can build and run your binary with `cargo run`. Running this code
72//! starts a HTTP server instance on port 8000. You can also override the port
73//! by setting the `PORT` environment variable.
74//!
75//! See the
76//! [`examples/`](https://github.com/spaceapi-community/spaceapi-server-rs/tree/master/examples)
77//! directory for some other examples.
78//!
79//!
80//! ## Sensors
81//!
82//! ### Registering Sensors
83//!
84//! This crate supports updating and retrieving dynamic sensor values (e.g.
85//! temperature or people present). For this, first register a sensor with a
86//! sensor template:
87//!
88//! ```rust
89//! use spaceapi_server::SpaceapiServerBuilder;
90//! use spaceapi_server::api::sensors::{
91//! PeopleNowPresentSensorTemplate, SensorMetadata, SensorMetadataWithLocation, TemperatureSensorTemplate,
92//! };
93//!
94//! # use spaceapi_server::api;
95//! # let status = api::StatusBuilder::v14("aa")
96//! # .logo("https://example.com/logo.png")
97//! # .url("https://example.com/")
98//! # .location(api::Location {
99//! # address: Some("addr".into()),
100//! # lat: 47.0,
101//! # lon: 8.0,
102//! # timezone: None,
103//! # })
104//! # .contact(api::Contact {
105//! # twitter: Some("@example".into()),
106//! # ..Default::default()
107//! # })
108//! # .build()
109//! # .expect("Creating status failed");
110//! #
111//! let server = SpaceapiServerBuilder::new(status)
112//! .redis_connection_info("redis://127.0.0.1/")
113//! .add_sensor(PeopleNowPresentSensorTemplate {
114//! metadata: SensorMetadata {
115//! location: Some("Hackerspace".into()),
116//! ..Default::default()
117//! },
118//! }, "people_now_present".into())
119//! .add_sensor(TemperatureSensorTemplate {
120//! metadata: SensorMetadataWithLocation {
121//! location: "Room 1".into(),
122//! ..Default::default()
123//! },
124//! unit: "°C".into(),
125//! }, "temp_room1".into())
126//! .add_sensor(TemperatureSensorTemplate {
127//! metadata: SensorMetadataWithLocation {
128//! location: "Room 2".into(),
129//! ..Default::default()
130//! },
131//! unit: "°C".into(),
132//! }, "temp_room2".into())
133//! .build()
134//! .expect("Could not initialize server");
135//! ```
136//!
137//! (You can find the full example at
138//! [`examples/with_sensors.rs`](https://github.com/spaceapi-community/spaceapi-server-rs/blob/master/examples/with_sensors.rs).)
139//!
140//! This will register three sensors: One "people now present" sensor and two
141//! "temperature" sensors.
142//!
143//! ### Updating Sensors via HTTP
144//!
145//! If you start the server like that, the JSON output will not yet contain any
146//! sensor data. To update a sensor value, send a HTTP POST request to the
147//! `/sensors/<sensor-id>/` endpoint with the `value` parameter:
148//!
149//! ```text
150//! curl -v -X PUT -d value=42 http://127.0.0.1:8000/sensors/people_now_present/
151//! curl -v -X PUT -d value=13.37 http://127.0.0.1:8000/sensors/temp_room1/
152//! ```
153//!
154//! Now the server response will contain the following key:
155//!
156//! ```json
157//! "sensors": {
158//! "people_now_present": [
159//! {
160//! "location": "Hackerspace",
161//! "value": 42
162//! }
163//! ],
164//! "temperature": [
165//! {
166//! "unit": "°C",
167//! "location": "Room 1",
168//! "value": 13.37
169//! }
170//! ]
171//! },
172//! ```
173//!
174//! ### Updating Sensors via Redis
175//!
176//! Alternatively you can modify the values in Redis directly. You can access
177//! the database with the `redis-cli` tool:
178//!
179//! ```text
180//! % redis-cli
181//! 127.0.0.1:6379> SET people_now_present 1
182//! OK
183//! 127.0.0.1:6379> GET people_now_present
184//! "1"
185//! 127.0.0.1:6379> KEYS *
186//! 1) "people_now_present"
187//! 2) "temp_room1"
188//! ```
189//!
190//! The keys need to match the IDs you used when registering the sensor.
191
192#![deny(missing_docs)]
193#![doc(html_root_url = "https://docs.rs/spaceapi-server")]
194
195pub use spaceapi as api;
196
197pub use iron::error::HttpResult;
198pub use iron::Listening;
199
200mod errors;
201pub mod modifiers;
202mod sensors;
203mod server;
204mod types;
205
206pub use crate::errors::SpaceapiServerError;
207pub use crate::server::SpaceapiServer;
208pub use crate::server::SpaceapiServerBuilder;
209
210/// Return own crate version. Used in API responses.
211pub fn get_version() -> &'static str {
212 env!("CARGO_PKG_VERSION")
213}
214
215#[cfg(test)]
216mod test {
217 use super::get_version;
218
219 #[test]
220 fn test_get_version() {
221 let version = get_version();
222 assert_eq!(3, version.split('.').count());
223 }
224}