├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── hitcounter.rs └── variable_passing.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /target/ 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | rust: 4 | - stable 5 | - nightly 6 | env: 7 | global: 8 | secure: PqrYTLi97gu+3ABt7ODlekU8TPC6Rs7bXIXGa6+BArn6W+0gPja6XVuxsly+8oUq7p38RyK5m6YghJmTQp27DEXyVTh9SGUlP7wVwrwTo8zydjDSn6TGzWIoSM0j7duHf9dAXP6+TEknueR/fj6c72koxawYdRPs+pL4ekULNFY= 9 | script: 10 | - cargo build 11 | - cargo test 12 | - cargo bench 13 | after_success: 'curl https://raw.githubusercontent.com/iron/build-doc/master/build-doc.sh 14 | | sh ' 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "persistent" 4 | authors = ["Jonathan Reem "] 5 | version = "0.4.0" 6 | description = "A set of middleware for sharing server-global data in Iron." 7 | repository = "https://github.com/iron/persistent" 8 | documentation = "http://ironframework.io/doc/persistent/index.html" 9 | license = "MIT" 10 | keywords = ["iron", "web", "persistence", "sharing", "global"] 11 | 12 | [dependencies] 13 | iron = { git = "https://github.com/iron/iron", branch = "master" } 14 | plugin = "0.2" 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | persistent [![Build Status](https://secure.travis-ci.org/iron/persistent.png?branch=master)](https://travis-ci.org/iron/persistent) 2 | ==== 3 | 4 | > Persistent storage as middleware for the [Iron](https://github.com/iron/iron) web framework. 5 | 6 | - Share persistent data across requests 7 | - Read or modify locally stored data 8 | 9 | Use this if you are currently thinking about using `std::sync::Arc` to share 10 | state between request handlers. 11 | 12 | ## Installation 13 | 14 | If you're using a `Cargo.toml` to manage dependencies, just add persistent to the toml: 15 | 16 | ```toml 17 | [dependencies] 18 | persistent = "x.y.z" # Insert current version here 19 | ``` 20 | 21 | Otherwise, `cargo build`, and the rlib will be in your `target` directory. 22 | 23 | ## [Documentation](http://ironframework.io/doc/persistent) 24 | 25 | Along with the [online documentation](http://ironframework.io/doc/persistent), 26 | you can build a local copy with `make doc`. 27 | 28 | ## [Examples](/examples) 29 | 30 | ## Get Help 31 | 32 | One of us ([@reem](https://github.com/reem/), [@zzmp](https://github.com/zzmp/), 33 | [@theptrk](https://github.com/theptrk/), [@mcreinhard](https://github.com/mcreinhard)) 34 | is usually on `#iron` on the mozilla irc. Come say hi and ask any questions you might have. 35 | We are also usually on `#rust` and `#rust-webdev`. 36 | -------------------------------------------------------------------------------- /examples/hitcounter.rs: -------------------------------------------------------------------------------- 1 | extern crate iron; 2 | extern crate persistent; 3 | 4 | use iron::prelude::*; 5 | 6 | use persistent::Write; 7 | use iron::typemap::Key; 8 | use iron::StatusCode; 9 | 10 | #[derive(Copy, Clone)] 11 | pub struct HitCounter; 12 | 13 | impl Key for HitCounter { type Value = usize; } 14 | 15 | fn serve_hits(req: &mut Request) -> IronResult { 16 | let mutex = req.get::>().unwrap(); 17 | let mut count = mutex.lock().unwrap(); 18 | 19 | *count += 1; 20 | Ok(Response::with((StatusCode::OK, format!("Hits: {}", *count)))) 21 | } 22 | 23 | fn main() { 24 | let mut chain = Chain::new(serve_hits); 25 | chain.link(Write::::both(0)); 26 | Iron::new(chain).http("localhost:3000"); 27 | } 28 | 29 | -------------------------------------------------------------------------------- /examples/variable_passing.rs: -------------------------------------------------------------------------------- 1 | extern crate iron; 2 | extern crate persistent; 3 | 4 | use std::string::String; 5 | 6 | use iron::prelude::*; 7 | 8 | use persistent::Read; 9 | use iron::typemap::Key; 10 | use iron::StatusCode; 11 | 12 | #[derive(Copy, Clone)] 13 | pub struct Log; 14 | impl Key for Log { type Value = String; } 15 | 16 | 17 | fn serve_hits(req: &mut Request) -> IronResult { 18 | let arc = req.get::>().unwrap(); 19 | let log_path = arc.as_ref(); 20 | 21 | Ok(Response::with((StatusCode::OK, format!("Hits: {}", log_path)))) 22 | } 23 | 24 | fn main() { 25 | // This can be passed from command line arguments for example. 26 | let log_path = String::from("/path/to/a/log/file.log"); 27 | let mut chain = Chain::new(serve_hits); 28 | chain.link(Read::::both(log_path)); 29 | Iron::new(chain).http("localhost:3000"); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(test, deny(warnings))] 2 | #![deny(missing_docs)] 3 | 4 | //! A set of middleware for sharing data between requests in the Iron 5 | //! framework. 6 | 7 | extern crate iron; 8 | extern crate plugin; 9 | 10 | use iron::{Request, Response, BeforeMiddleware, AfterMiddleware, IronResult}; 11 | use iron::typemap::Key; 12 | use std::sync::{Arc, RwLock, Mutex}; 13 | use std::fmt; 14 | use std::error::Error; 15 | use plugin::Plugin; 16 | 17 | /// The type that can be returned by `eval` to indicate error. 18 | #[derive(Clone, Debug)] 19 | pub enum PersistentError { 20 | /// The value was not found. 21 | NotFound 22 | } 23 | 24 | impl Error for PersistentError { 25 | fn description(&self) -> &str { 26 | match *self { 27 | PersistentError::NotFound => "Value not found in extensions." 28 | } 29 | } 30 | } 31 | 32 | impl fmt::Display for PersistentError { 33 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 34 | self.description().fmt(f) 35 | } 36 | } 37 | 38 | /// Helper trait for overloading the constructors of `Read`/`Write`/`State`. 39 | /// This is an implementation detail, and should not be used for any other 40 | /// purpose. 41 | /// 42 | /// For example, this trait lets you construct a `Read` from either a `T` or 43 | /// an `Arc`. 44 | pub trait PersistentInto { 45 | /// Convert `self` into a value of type `T`. 46 | fn persistent_into(self) -> T; 47 | } 48 | 49 | impl PersistentInto for T { 50 | fn persistent_into(self) -> T { self } 51 | } 52 | 53 | impl PersistentInto> for T { 54 | fn persistent_into(self) -> Arc { 55 | Arc::new(self) 56 | } 57 | } 58 | 59 | impl PersistentInto>> for T { 60 | fn persistent_into(self) -> Arc> { 61 | Arc::new(Mutex::new(self)) 62 | } 63 | } 64 | 65 | impl PersistentInto>> for T { 66 | fn persistent_into(self) -> Arc> { 67 | Arc::new(RwLock::new(self)) 68 | } 69 | } 70 | 71 | /// Middleware for data that persists between requests with read and write capabilities. 72 | /// 73 | /// The data is stored behind a `RwLock`, so multiple read locks 74 | /// can be taken out concurrently. 75 | /// 76 | /// If most threads need to take out a write lock, you may want to 77 | /// consider `Write`, which stores the data behind a `Mutex`, which 78 | /// has a faster locking speed. 79 | /// 80 | /// `State` can be linked as `BeforeMiddleware` to add data to the `Request` 81 | /// extensions and it can be linked as an `AfterMiddleware` to add data to 82 | /// the `Response` extensions. 83 | /// 84 | /// `State` also implements `Plugin`, so the data stored within can be 85 | /// accessed through `request.get::>()` as an `Arc>`. 86 | pub struct State { 87 | data: Arc> 88 | } 89 | 90 | /// Middleware for data that persists between Requests with read-only capabilities. 91 | /// 92 | /// The data is stored behind an Arc, so multiple threads can have 93 | /// concurrent, non-blocking access. 94 | /// 95 | /// `Read` can be linked as `BeforeMiddleware` to add data to the `Request` 96 | /// extensions and it can be linked as an `AfterMiddleware` to add data to 97 | /// the `Response` extensions. 98 | /// 99 | /// `Read` also implements `Plugin`, so the data stored within can be 100 | /// accessed through `request.get::>()` as an `Arc`. 101 | pub struct Read { 102 | data: Arc 103 | } 104 | 105 | /// Middleware for data that persists between Requests for data which mostly 106 | /// needs to be written instead of read. 107 | /// 108 | /// The data is stored behind a `Mutex`, so only one request at a time can 109 | /// access the data. This is more performant than `State` in the case where 110 | /// most uses of the data require a write lock. 111 | /// 112 | /// `Write` can be linked as `BeforeMiddleware` to add data to the `Request` 113 | /// extensions and it can be linked as an `AfterMiddleware` to add data to 114 | /// the `Response` extensions. 115 | /// 116 | /// `Write` also implements `Plugin`, so the data stored within can be 117 | /// accessed through `request.get::>()` as an `Arc>`. 118 | pub struct Write { 119 | data: Arc> 120 | } 121 | 122 | impl Clone for Read

where P::Value: Send + Sync { 123 | fn clone(&self) -> Read

{ 124 | Read { data: self.data.clone() } 125 | } 126 | } 127 | 128 | impl Clone for State

where P::Value: Send + Sync { 129 | fn clone(&self) -> State

{ 130 | State { data: self.data.clone() } 131 | } 132 | } 133 | 134 | impl Clone for Write

where P::Value: Send { 135 | fn clone(&self) -> Write

{ 136 | Write { data: self.data.clone() } 137 | } 138 | } 139 | 140 | impl Key for State

where P::Value: 'static { 141 | type Value = Arc>; 142 | } 143 | 144 | impl Key for Read

where P::Value: 'static { 145 | type Value = Arc; 146 | } 147 | 148 | impl Key for Write

where P::Value: 'static { 149 | type Value = Arc>; 150 | } 151 | 152 | impl Plugin for State

where P::Value: Send + Sync { 153 | type Error = PersistentError; 154 | fn eval(req: &mut Request) -> Result>, PersistentError> { 155 | req.extensions.get::>().cloned().ok_or(PersistentError::NotFound) 156 | } 157 | } 158 | 159 | impl Plugin for Read

where P::Value: Send + Sync { 160 | type Error = PersistentError; 161 | fn eval(req: &mut Request) -> Result, PersistentError> { 162 | req.extensions.get::>().cloned().ok_or(PersistentError::NotFound) 163 | } 164 | } 165 | 166 | impl Plugin for Write

where P::Value: Send { 167 | type Error = PersistentError; 168 | fn eval(req: &mut Request) -> Result>, PersistentError> { 169 | req.extensions.get::>().cloned().ok_or(PersistentError::NotFound) 170 | } 171 | } 172 | 173 | impl BeforeMiddleware for State

where P::Value: Send + Sync { 174 | fn before(&self, req: &mut Request) -> IronResult<()> { 175 | req.extensions.insert::>(self.data.clone()); 176 | Ok(()) 177 | } 178 | } 179 | 180 | impl BeforeMiddleware for Read

where P::Value: Send + Sync { 181 | fn before(&self, req: &mut Request) -> IronResult<()> { 182 | req.extensions.insert::>(self.data.clone()); 183 | Ok(()) 184 | } 185 | } 186 | 187 | impl BeforeMiddleware for Write

where P::Value: Send { 188 | fn before(&self, req: &mut Request) -> IronResult<()> { 189 | req.extensions.insert::>(self.data.clone()); 190 | Ok(()) 191 | } 192 | } 193 | 194 | impl AfterMiddleware for State

where P::Value: Send + Sync { 195 | fn after(&self, _: &mut Request, mut res: Response) -> IronResult { 196 | res.extensions.insert::>(self.data.clone()); 197 | Ok(res) 198 | } 199 | } 200 | 201 | impl AfterMiddleware for Read

where P::Value: Send + Sync { 202 | fn after(&self, _: &mut Request, mut res: Response) -> IronResult { 203 | res.extensions.insert::>(self.data.clone()); 204 | Ok(res) 205 | } 206 | } 207 | 208 | impl AfterMiddleware for Write

where P::Value: Send { 209 | fn after(&self, _: &mut Request, mut res: Response) -> IronResult { 210 | res.extensions.insert::>(self.data.clone()); 211 | Ok(res) 212 | } 213 | } 214 | 215 | impl State

where P::Value: Send + Sync { 216 | /// Construct a new pair of `State` that can be passed directly to `Chain::link`. 217 | /// 218 | /// The data is initialized with the passed-in value. 219 | pub fn both(start: T) -> (State

, State

) where T: PersistentInto>> { 220 | let x = State { data: start.persistent_into() }; 221 | (x.clone(), x) 222 | } 223 | 224 | /// Construct a new `State` that can be passed directly to 225 | /// `Chain::link_before` or `Chain::link_after`. 226 | /// 227 | /// The data is initialized with the passed-in value. 228 | pub fn one(start: T) -> State

where T: PersistentInto>> { 229 | State { data: start.persistent_into() } 230 | } 231 | } 232 | 233 | impl Read

where P::Value: Send + Sync { 234 | /// Construct a new pair of `Read` that can be passed directly to `Chain::link`. 235 | /// 236 | /// The data is initialized with the passed-in value. 237 | pub fn both(start: T) -> (Read

, Read

) where T: PersistentInto> { 238 | let x = Read { data: start.persistent_into() }; 239 | (x.clone(), x) 240 | } 241 | 242 | /// Construct a new `Read` that can be passed directly to 243 | /// `Chain::link_before` or `Chain::link_after`. 244 | /// 245 | /// The data is initialized with the passed-in value. 246 | pub fn one(start: T) -> Read

where T: PersistentInto> { 247 | Read { data: start.persistent_into() } 248 | } 249 | } 250 | 251 | impl Write

where P::Value: Send { 252 | /// Construct a new pair of `Write` that can be passed directly to `Chain::link`. 253 | /// 254 | /// The data is initialized with the passed-in value. 255 | pub fn both(start: T) -> (Write

, Write

) where T: PersistentInto>> { 256 | let x = Write { data: start.persistent_into() }; 257 | (x.clone(), x) 258 | } 259 | 260 | /// Construct a new `Write` that can be passed directly to 261 | /// `Chain::link_before` or `Chain::link_after`. 262 | /// 263 | /// The data is initialized with the passed-in value. 264 | pub fn one(start: T) -> Write

where T: PersistentInto>> { 265 | Write { data: start.persistent_into() } 266 | } 267 | } 268 | --------------------------------------------------------------------------------