<commit_before>use std::error::Error;
use std::collections::HashMap;
use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{Config, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a typed field from a JSON Value.
///
/// Will return from the caller with a BrokerError if the field is missing or its value is an
/// incompatible type. `descr` is used to format the error message.
///
/// ```
/// let foo = try_get_json_field!(value, "foo", as_str, "example document");
/// ```
macro_rules! try_get_json_field {
    ( $input:expr, $key:tt, $conv:ident, $descr:expr ) => {
        try!($input.find($key).and_then(|v| v.$conv()).ok_or_else(|| {
            BrokerError::Provider(format!("{} missing from {}", $key, $descr))
        }))
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `Config`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &Config, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &Url)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &redirect_uri.to_string()),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery_url
        ).map_err(|e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          domain, e.description()))
        })
    );
    let descr = format!("{}'s discovery document", domain);
    let authz_base = try_get_json_field!(config_obj, "authorization_endpoint", as_str, descr);

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.public_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
        "&prompt=consent",
    ].join("")).map_err(|_| {
        BrokerError::Provider(format!("failed to build valid authorization URL from {}'s 'authorization_endpoint'", domain))
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find('@').unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find('+') {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &Config, stored: &HashMap<String, String>, code: &str)
              -> BrokerResult<(String, String)> {

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: domain },
            &client,
            &provider.discovery_url
        ).map_err(|e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          domain, e.description()))
        })
    );
    let descr = format!("{}'s discovery document", domain);
    let token_url = try_get_json_field!(config_obj, "token_endpoint", as_str, descr);
    let jwks_url = try_get_json_field!(config_obj, "jwks_uri", as_str, descr);

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.public_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        try!(
            client.post(token_url).headers(headers).body(&body).send()
                .map_err(BrokerError::Http)
                .and_then(|rsp| from_reader(rsp).map_err(|_| {
                    BrokerError::Provider("failed to parse response as JSON".to_string())
                }))
                .map_err(|e| {
                    BrokerError::Provider(format!("{} token request failed: {}",
                                                  domain, e.description()))
                })
        )
    };
    let descr = format!("{}'s token response", domain);
    let id_token = try_get_json_field!(token_obj, "id_token", as_str, descr);

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let keys_obj: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: domain },
                &client,
                &jwks_url
            ).map_err(|e| {
                BrokerError::Provider(format!("could not fetch {}'s keys: {}",
                                              domain, e.description()))
            })
        );
        try!(
            verify_jws(id_token, &keys_obj).map_err(|_| {
                BrokerError::Provider(format!("could not verify the token received from {}", domain))
            })
        )
    };

    // Verify the claims contained in the token.
    let descr = format!("{}'s token payload", domain);
    let iss = try_get_json_field!(jwt_payload, "iss", as_str, descr);
    let aud = try_get_json_field!(jwt_payload, "aud", as_str, descr);
    let token_addr = try_get_json_field!(jwt_payload, "email", as_str, descr);
    let exp = try_get_json_field!(jwt_payload, "exp", as_i64, descr);
    let issuer_origin = vec!["https://", &provider.issuer_domain].join("");
    assert!(iss == provider.issuer_domain || iss == issuer_origin);
    assert!(aud == provider.client_id);
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<commit_msg>Revert "Require consent for oidc flow."<commit_after>use std::error::Error;
use std::collections::HashMap;
use emailaddress::EmailAddress;
use iron::Url;
use serde_json::de::from_reader;
use serde_json::value::Value;
use super::error::{BrokerError, BrokerResult};
use super::hyper::client::Client as HttpClient;
use super::hyper::header::ContentType as HyContentType;
use super::hyper::header::Headers;
use super::{Config, create_jwt};
use super::crypto::{session_id, verify_jws};
use super::store_cache::CacheKey;
use time::now_utc;
use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

/// Macro used to extract a typed field from a JSON Value.
///
/// Will return from the caller with a BrokerError if the field is missing or its value is an
/// incompatible type. `descr` is used to format the error message.
///
/// ```
/// let foo = try_get_json_field!(value, "foo", as_str, "example document");
/// ```
macro_rules! try_get_json_field {
    ( $input:expr, $key:tt, $conv:ident, $descr:expr ) => {
        try!($input.find($key).and_then(|v| v.$conv()).ok_or_else(|| {
            BrokerError::Provider(format!("{} missing from {}", $key, $descr))
        }))
    }
}

/// Helper method to issue an OAuth authorization request.
///
/// When an authentication request comes in and matches one of the "famous"
/// identity providers configured in the `Config`, we redirect the client
/// to an Authentication Request URL, which we discover by reading the
/// provider's configured Discovery URL. We pass in the client ID we received
/// when pre-registering for the provider, as well as a callback URL which the
/// user will be redirected back to after confirming (or denying) the
/// Authentication Request. Included in the request is a nonce which we can
/// later use to definitively match the callback to this request.
pub fn request(app: &Config, email_addr: EmailAddress, client_id: &str, nonce: &str, redirect_uri: &Url)
               -> BrokerResult<Url> {

    let session = session_id(&email_addr, client_id);

    // Store the nonce and the RP's `redirect_uri` in Redis for use in the
    // callback handler.
    try!(app.store.store_session(&session, &[
        ("type", "oidc"),
        ("email", &email_addr.to_string()),
        ("client_id", client_id),
        ("nonce", nonce),
        ("redirect", &redirect_uri.to_string()),
    ]));

    let client = HttpClient::new();

    // Retrieve the provider's Discovery document and extract the
    // `authorization_endpoint` from it.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: &email_addr.domain },
            &client,
            &provider.discovery_url
        ).map_err(|e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          domain, e.description()))
        })
    );
    let descr = format!("{}'s discovery document", domain);
    let authz_base = try_get_json_field!(config_obj, "authorization_endpoint", as_str, descr);

    // Create the URL to redirect to, properly escaping all parameters.
    Url::parse(&vec![
        authz_base,
        "?",
        "client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&response_type=code",
        "&scope=",
        &utf8_percent_encode("openid email", QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.public_url),
                             QUERY_ENCODE_SET).to_string(),
        "&state=",
        &utf8_percent_encode(&session, QUERY_ENCODE_SET).to_string(),
        "&login_hint=",
        &utf8_percent_encode(&email_addr.to_string(), QUERY_ENCODE_SET).to_string(),
    ].join("")).map_err(|_| {
        BrokerError::Provider(format!("failed to build valid authorization URL from {}'s 'authorization_endpoint'", domain))
    })

}

pub fn canonicalize_google(email: String) -> String {
    let at = email.find('@').unwrap();
    let (user, domain) = email.split_at(at);
    let domain = &domain[1..];
    let user = &user.replace(".", ""); // Ignore dots

    // Trim plus addresses
    let user = match user.find('+') {
        Some(pos) => user.split_at(pos).0,
        None => user,
    };

    // Normalize googlemail.com to gmail.com
    let domain = if domain == "googlemail.com" {
        "gmail.com"
    } else {
        domain
    };
    user.to_string() + "@" + domain
}

pub fn canonicalized(email: &str) -> String {
    let normalized = email.to_lowercase();
    if normalized.ends_with("@gmail.com") || normalized.ends_with("@googlemail.com") {
        canonicalize_google(normalized)
    } else {
        normalized
    }
}

/// Helper method to verify OAuth authentication result.
///
/// Match the returned email address and nonce against our Redis data, then
/// extract the identity token returned by the provider and verify it. Return
/// an identity token for the RP if successful, or an error message otherwise.
pub fn verify(app: &Config, stored: &HashMap<String, String>, code: &str)
              -> BrokerResult<(String, String)> {

    let email_addr = EmailAddress::new(&stored["email"]).unwrap();
    let origin = &stored["client_id"];
    let nonce = &stored["nonce"];

    let client = HttpClient::new();

    // Request the provider's Discovery document to get the
    // `token_endpoint` and `jwks_uri` values from it. TODO: save these
    // when requesting the Discovery document in the `oauth_request()`
    // function, and/or cache them by provider host.
    let domain = &email_addr.domain;
    let provider = &app.providers[domain];
    let config_obj: Value = try!(
        app.store.cache.fetch_json_url(
            &app.store,
            CacheKey::Discovery { domain: domain },
            &client,
            &provider.discovery_url
        ).map_err(|e| {
            BrokerError::Provider(format!("could not fetch {}'s discovery document: {}",
                                          domain, e.description()))
        })
    );
    let descr = format!("{}'s discovery document", domain);
    let token_url = try_get_json_field!(config_obj, "token_endpoint", as_str, descr);
    let jwks_url = try_get_json_field!(config_obj, "jwks_uri", as_str, descr);

    // Create form data for the Token Request, where we exchange the code
    // received in this callback request for an identity token (while
    // proving our identity by passing the client secret value).
    let body: String = vec![
        "code=",
        &utf8_percent_encode(code, QUERY_ENCODE_SET).to_string(),
        "&client_id=",
        &utf8_percent_encode(&provider.client_id, QUERY_ENCODE_SET).to_string(),
        "&client_secret=",
        &utf8_percent_encode(&provider.secret, QUERY_ENCODE_SET).to_string(),
        "&redirect_uri=",
        &utf8_percent_encode(&format!("{}/callback", &app.public_url),
                             QUERY_ENCODE_SET).to_string(),
        "&grant_type=authorization_code",
    ].join("");

    // Send the Token Request and extract the `id_token` from the response.
    let token_obj: Value = {
        let mut headers = Headers::new();
        headers.set(HyContentType::form_url_encoded());
        try!(
            client.post(token_url).headers(headers).body(&body).send()
                .map_err(BrokerError::Http)
                .and_then(|rsp| from_reader(rsp).map_err(|_| {
                    BrokerError::Provider("failed to parse response as JSON".to_string())
                }))
                .map_err(|e| {
                    BrokerError::Provider(format!("{} token request failed: {}",
                                                  domain, e.description()))
                })
        )
    };
    let descr = format!("{}'s token response", domain);
    let id_token = try_get_json_field!(token_obj, "id_token", as_str, descr);

    // Grab the keys from the provider, then verify the signature.
    let jwt_payload = {
        let keys_obj: Value = try!(
            app.store.cache.fetch_json_url(
                &app.store,
                CacheKey::KeySet { domain: domain },
                &client,
                &jwks_url
            ).map_err(|e| {
                BrokerError::Provider(format!("could not fetch {}'s keys: {}",
                                              domain, e.description()))
            })
        );
        try!(
            verify_jws(id_token, &keys_obj).map_err(|_| {
                BrokerError::Provider(format!("could not verify the token received from {}", domain))
            })
        )
    };

    // Verify the claims contained in the token.
    let descr = format!("{}'s token payload", domain);
    let iss = try_get_json_field!(jwt_payload, "iss", as_str, descr);
    let aud = try_get_json_field!(jwt_payload, "aud", as_str, descr);
    let token_addr = try_get_json_field!(jwt_payload, "email", as_str, descr);
    let exp = try_get_json_field!(jwt_payload, "exp", as_i64, descr);
    let issuer_origin = vec!["https://", &provider.issuer_domain].join("");
    assert!(iss == provider.issuer_domain || iss == issuer_origin);
    assert!(aud == provider.client_id);
    assert!(canonicalized(token_addr) == canonicalized(&email_addr.to_string()));
    let now = now_utc().to_timespec().sec;
    assert!(now < exp);

    // If everything is okay, build a new identity token and send it
    // to the relying party.
    let id_token = create_jwt(app, &email_addr.to_string(), origin, nonce);
    let redirect = &stored["redirect"];
    Ok((id_token, redirect.to_string()))

}

#[cfg(test)]
mod tests {
    use super::canonicalized;
    #[test]
    fn test_canonicalized_google() {
        assert_eq!(canonicalized("example.foo+bar@example.com"),
                   "example.foo+bar@example.com");
        assert_eq!(canonicalized("example@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example@googlemail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo@gmail.com"),
                   "examplefoo@gmail.com");
        assert_eq!(canonicalized("example+bar@gmail.com"),
                   "example@gmail.com");
        assert_eq!(canonicalized("example.foo+bar@googlemail.com"),
                   "examplefoo@gmail.com");
    }
}
<|endoftext|>


<commit_before>
use super::raw_vec::*;
use super::plan::*;
use super::enums::*;
use super::r2r::*;

use num_traits::Zero;
use std::ops::Mul;

pub struct Pair<A, B> {
    pub field: RawVec<A>,
    pub coef: RawVec<B>,
    logical_size: usize,
    forward: Plan<A, B>,
    backward: Plan<B, A>,
}

impl<A, B> Pair<A, B> {
    pub fn logical_size(&self) -> usize { self.logical_size }

    pub fn forward(&mut self) {
        unsafe {
            self.forward.execute();
        }
    }

    pub fn backward(&mut self) {
        unsafe {
            self.backward.execute();
        }
    }

    pub fn normalize_field_by(&mut self, factor: f64)
        where A: Mul<f64, Output = A> + Copy
    {
        for val in self.field.iter_mut() {
            *val = *val * factor;
        }
    }

    pub fn normalize_coef_by(&mut self, factor: f64)
        where B: Mul<f64, Output = B> + Copy
    {
        for val in self.coef.iter_mut() {
            *val = *val * factor;
        }
    }
}

impl<R> Pair<R, R>
    where R: R2RPlanCreate + AlignedAllocable + Zero
{
    pub fn r2r_1d(n: usize, kind: R2R_KIND, flag: FLAG) -> Self {
        let mut field = RawVec::new(n);
        let mut coef = RawVec::new(n);
        let forward = Plan::r2r_1d(n, &mut field, &mut coef, forward(kind), flag);
        let backward = Plan::r2r_1d(n, &mut coef, &mut field, backward(kind), flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: logical_size(n, kind),
            forward: forward,
            backward: backward,
        }
    }
}

impl<C> Pair<C, C>
    where C: C2CPlanCreate + AlignedAllocable + Zero
{
    pub fn c2c_1d(n: usize, sign: SIGN, flag: FLAG) -> Self {
        let mut field = RawVec::new(n);
        let mut coef = RawVec::new(n);
        let forward = Plan::c2c_1d(n, &mut field, &mut coef, sign, flag);
        let backward = Plan::c2c_1d(n, &mut coef, &mut field, -sign, flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: n,
            forward: forward,
            backward: backward,
        }
    }
}

impl<R, C> Pair<R, C>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>,
          R: AlignedAllocable + Zero,
          C: AlignedAllocable + Zero
{
    pub fn r2c_1d(n: usize, flag: FLAG) -> Self {
        let mut field = RawVec::<R>::new(n);
        let mut coef = RawVec::<C>::new(n / 2 + 1);
        let forward = Plan::r2c_1d(n, &mut field, &mut coef, flag);
        let backward = Plan::c2r_1d(n, &mut coef, &mut field, flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: n,
            forward: forward,
            backward: backward,
        }
    }
}
<commit_msg>Remove normalized_*_by<commit_after>
use super::raw_vec::*;
use super::plan::*;
use super::enums::*;
use super::r2r::*;

use num_traits::Zero;

pub struct Pair<A, B> {
    pub field: RawVec<A>,
    pub coef: RawVec<B>,
    logical_size: usize,
    forward: Plan<A, B>,
    backward: Plan<B, A>,
}

impl<A, B> Pair<A, B> {
    pub fn logical_size(&self) -> usize { self.logical_size }

    pub fn forward(&mut self) {
        unsafe {
            self.forward.execute();
        }
    }

    pub fn backward(&mut self) {
        unsafe {
            self.backward.execute();
        }
    }
}

impl<R> Pair<R, R>
    where R: R2RPlanCreate + AlignedAllocable + Zero
{
    pub fn r2r_1d(n: usize, kind: R2R_KIND, flag: FLAG) -> Self {
        let mut field = RawVec::new(n);
        let mut coef = RawVec::new(n);
        let forward = Plan::r2r_1d(n, &mut field, &mut coef, forward(kind), flag);
        let backward = Plan::r2r_1d(n, &mut coef, &mut field, backward(kind), flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: logical_size(n, kind),
            forward: forward,
            backward: backward,
        }
    }
}

impl<C> Pair<C, C>
    where C: C2CPlanCreate + AlignedAllocable + Zero
{
    pub fn c2c_1d(n: usize, sign: SIGN, flag: FLAG) -> Self {
        let mut field = RawVec::new(n);
        let mut coef = RawVec::new(n);
        let forward = Plan::c2c_1d(n, &mut field, &mut coef, sign, flag);
        let backward = Plan::c2c_1d(n, &mut coef, &mut field, -sign, flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: n,
            forward: forward,
            backward: backward,
        }
    }
}

impl<R, C> Pair<R, C>
    where (C, R): C2RPlanCreate<Real = R, Complex = C>,
          R: AlignedAllocable + Zero,
          C: AlignedAllocable + Zero
{
    pub fn r2c_1d(n: usize, flag: FLAG) -> Self {
        let mut field = RawVec::<R>::new(n);
        let mut coef = RawVec::<C>::new(n / 2 + 1);
        let forward = Plan::r2c_1d(n, &mut field, &mut coef, flag);
        let backward = Plan::c2r_1d(n, &mut coef, &mut field, flag);
        Pair {
            field: field,
            coef: coef,
            logical_size: n,
            forward: forward,
            backward: backward,
        }
    }
}
<|endoftext|>


<commit_before>//! Use bitkeeper to manage multiple versions of surefiles.
//!
//! Now that BitKeeper is [open source](http://bitkeeper.org/), let's make
//! it available as a store for surefiles.  Some brief experimenting shows
//! that the 'weave' method BitKeeper uses to store multiple file revisions
//! works rather well for surefiles.  One test case, for example, was able
//! to take nearly 2GB of individually compressed surefiles (several
//! hundred), and encode them in less than 50MB.

use regex::Regex;
use Result;
use std::fs::File;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use suretree::SureTree;

/// Initialize a new BitKeeper-based storage directory.  The path should
/// name a directory that is either empty or can be created with a single
/// `mkdir`.
pub fn setup<P: AsRef<Path>>(base: P) -> Result<()> {
    let base = base.as_ref();

    let mut cmd = Command::new("bk");
    // BAM=off is needed to keep BK from storing large files as just whole
    // files.  Surefiles will often be large, and the delta storage is the
    // whole reason we're using BK.
    // checkout=none frees up some space by not leaving uncompressed copies
    // of the surefiles in the work directory.
    cmd.args(&["setup", "-f", "-FBAM=off", "-Fcheckout=none"]);
    cmd.arg(base.as_os_str());
    let status = try!(cmd.status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    // Construct a README file in this directory, since there won't appear
    // to be files in it, other than the BitKeeper directory.
    {
        let mut ofd = try!(File::create(base.join("README")));
        try!(ofd.write_all(include_bytes!("../etc/template-bk-readme.txt")));
    }

    let status = try!(Command::new("bk")
                      .args(&["ci", "-iu", "README"])
                      .current_dir(base)
                      .status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    let status = try!(Command::new("bk")
                      .args(&["commit", "-yInitial README"])
                      .current_dir(base)
                      .status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    Ok(())
}

/// A manager for a tree of surefiles managed under BitKeeper.
pub struct BkDir {
    base: PathBuf,
    change_re: Regex,
}

impl BkDir {
    /// Construct a new `BkDir` that can store and retrieve files from the
    /// given path.  The directory must have already been created with the
    /// `setup` function above.
    pub fn new<P: AsRef<Path>>(base: P) -> Result<BkDir> {
        Ok(BkDir {
            base: base.as_ref().to_owned(),
            change_re: Regex::new(r"^  ([^ ]+) ([\d\.]+) (.*)$").unwrap(),
        })
    }

    /// Write a SureTree to a surefile of the given name (generally of the
    /// convention "fsname.dat").  If this is the first file written by
    /// this name, it will be checked in initially into BitKeeper.  If it
    /// is an update, the BitKeeper file will be edited, and a cset made to
    /// record this.  The `info` text should be a short piece of text to
    /// describe this revision.  It will be used as the commit text within
    /// BitKeeper, and also used (exactly) to retrieve this version later.
    pub fn save(&self, tree: &SureTree, name: &str, info: &str) -> Result<()> {
        let y_arg = format!("-y{}", info);

        // Try checking out the file.  BitKeeper will fail if it doesn't
        // exist.
        let initial = match self.bk_do(&["edit", name]) {
            Ok(_) => false,
            Err(_) => true,
        };

        {
            let mut wr = try!(File::create(&self.base.join(name)));
            try!(tree.save_to(&mut wr));
        }

        if initial {
            try!(self.bk_do(&["ci", "-i", &y_arg, name]));
        } else {
            try!(self.bk_do(&["ci", &y_arg, name]));
        }

        try!(self.bk_do(&["commit", &y_arg, name]));

        Ok(())
    }

    /// Query to determine all file versions that have been saved.
    pub fn query(&self) -> Result<Vec<BkSureFile>> {
        let output = try!(Command::new("bk")
                          .args(&["changes", "-v",
                                "-d:INDENT::DPN: :REV: :C:\n"])
                          .current_dir(&self.base)
                          .output());
        if !output.stderr.is_empty() {
            println!("BK error: {:?}", String::from_utf8_lossy(&output.stderr));
            return Err(format!("Error running bk: {:?}", output.status).into());
        }
        if !output.status.success() {
            return Err(format!("Error running bk: {:?}", output.status).into());
        }

        let mut result = vec![];

        for line in (&output.stdout[..]).lines() {
            let line = try!(line);
            match self.change_re.captures(&line) {
                None => (),
                Some(cap) => {
                    let file = cap.at(1).unwrap();
                    let rev = cap.at(2).unwrap();
                    let name = cap.at(3).unwrap();
                    if !file.ends_with(".dat") {
                        continue;
                    }
                    result.push(BkSureFile {
                        file: file.to_owned(),
                        rev: rev.to_owned(),
                        name: name.to_owned(),
                    });
                },
            }
        }
        Ok(result)
    }

    pub fn load(&self, file: &str, name: &str) -> Result<SureTree> {
        let files = try!(self.query());
        let rev = match files.iter().find(|&x| x.file == file && x.name == name) {
            None => return Err(format!("Couldn't find file: {:?} name: {:?}", file, name).into()),
            Some(x) => &x.rev[..],
        };

        let mut child = try!(Command::new("bk")
                             .args(&["co", "-p",
                                   &format!("-r{}", rev),
                                   file])
                             .current_dir(&self.base)
                             .stdout(Stdio::piped())
                             .spawn());
        let tree = try!(SureTree::load_from(child.stdout.as_mut().unwrap()));
        let status = try!(child.wait());
        if !status.success() {
            return Err(format!("Error running bk: {:?}", status).into());
        }
        Ok(tree)
    }

    fn bk_do(&self, args: &[&str]) -> Result<()> {
        let status = try!(Command::new("bk")
                          .args(args)
                          .current_dir(&self.base)
                          .status());
        if !status.success() {
            return Err(format!("Error running bk: {:?}", status).into());
        }
        Ok(())
    }
}

#[derive(Debug)]
pub struct BkSureFile {
    pub file: String,
    pub rev: String,
    pub name: String,
}
<commit_msg>bk: Force delta even with no changes<commit_after>//! Use bitkeeper to manage multiple versions of surefiles.
//!
//! Now that BitKeeper is [open source](http://bitkeeper.org/), let's make
//! it available as a store for surefiles.  Some brief experimenting shows
//! that the 'weave' method BitKeeper uses to store multiple file revisions
//! works rather well for surefiles.  One test case, for example, was able
//! to take nearly 2GB of individually compressed surefiles (several
//! hundred), and encode them in less than 50MB.

use regex::Regex;
use Result;
use std::fs::File;
use std::io::{BufRead, Write};
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use suretree::SureTree;

/// Initialize a new BitKeeper-based storage directory.  The path should
/// name a directory that is either empty or can be created with a single
/// `mkdir`.
pub fn setup<P: AsRef<Path>>(base: P) -> Result<()> {
    let base = base.as_ref();

    let mut cmd = Command::new("bk");
    // BAM=off is needed to keep BK from storing large files as just whole
    // files.  Surefiles will often be large, and the delta storage is the
    // whole reason we're using BK.
    // checkout=none frees up some space by not leaving uncompressed copies
    // of the surefiles in the work directory.
    cmd.args(&["setup", "-f", "-FBAM=off", "-Fcheckout=none"]);
    cmd.arg(base.as_os_str());
    let status = try!(cmd.status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    // Construct a README file in this directory, since there won't appear
    // to be files in it, other than the BitKeeper directory.
    {
        let mut ofd = try!(File::create(base.join("README")));
        try!(ofd.write_all(include_bytes!("../etc/template-bk-readme.txt")));
    }

    let status = try!(Command::new("bk")
                      .args(&["ci", "-iu", "README"])
                      .current_dir(base)
                      .status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    let status = try!(Command::new("bk")
                      .args(&["commit", "-yInitial README"])
                      .current_dir(base)
                      .status());
    if !status.success() {
        return Err(format!("Error running bk: {:?}", status).into());
    }

    Ok(())
}

/// A manager for a tree of surefiles managed under BitKeeper.
pub struct BkDir {
    base: PathBuf,
    change_re: Regex,
}

impl BkDir {
    /// Construct a new `BkDir` that can store and retrieve files from the
    /// given path.  The directory must have already been created with the
    /// `setup` function above.
    pub fn new<P: AsRef<Path>>(base: P) -> Result<BkDir> {
        Ok(BkDir {
            base: base.as_ref().to_owned(),
            change_re: Regex::new(r"^  ([^ ]+) ([\d\.]+) (.*)$").unwrap(),
        })
    }

    /// Write a SureTree to a surefile of the given name (generally of the
    /// convention "fsname.dat").  If this is the first file written by
    /// this name, it will be checked in initially into BitKeeper.  If it
    /// is an update, the BitKeeper file will be edited, and a cset made to
    /// record this.  The `info` text should be a short piece of text to
    /// describe this revision.  It will be used as the commit text within
    /// BitKeeper, and also used (exactly) to retrieve this version later.
    pub fn save(&self, tree: &SureTree, name: &str, info: &str) -> Result<()> {
        let y_arg = format!("-y{}", info);

        // Try checking out the file.  BitKeeper will fail if it doesn't
        // exist.
        let initial = match self.bk_do(&["edit", name]) {
            Ok(_) => false,
            Err(_) => true,
        };

        {
            let mut wr = try!(File::create(&self.base.join(name)));
            try!(tree.save_to(&mut wr));
        }

        if initial {
            try!(self.bk_do(&["ci", "-i", &y_arg, name]));
        } else {
            try!(self.bk_do(&["ci", "-f", &y_arg, name]));
        }

        try!(self.bk_do(&["commit", &y_arg, name]));

        Ok(())
    }

    /// Query to determine all file versions that have been saved.
    pub fn query(&self) -> Result<Vec<BkSureFile>> {
        let output = try!(Command::new("bk")
                          .args(&["changes", "-v",
                                "-d:INDENT::DPN: :REV: :C:\n"])
                          .current_dir(&self.base)
                          .output());
        if !output.stderr.is_empty() {
            println!("BK error: {:?}", String::from_utf8_lossy(&output.stderr));
            return Err(format!("Error running bk: {:?}", output.status).into());
        }
        if !output.status.success() {
            return Err(format!("Error running bk: {:?}", output.status).into());
        }

        let mut result = vec![];

        for line in (&output.stdout[..]).lines() {
            let line = try!(line);
            match self.change_re.captures(&line) {
                None => (),
                Some(cap) => {
                    let file = cap.at(1).unwrap();
                    let rev = cap.at(2).unwrap();
                    let name = cap.at(3).unwrap();
                    if !file.ends_with(".dat") {
                        continue;
                    }
                    result.push(BkSureFile {
                        file: file.to_owned(),
                        rev: rev.to_owned(),
                        name: name.to_owned(),
                    });
                },
            }
        }
        Ok(result)
    }

    pub fn load(&self, file: &str, name: &str) -> Result<SureTree> {
        let files = try!(self.query());
        let rev = match files.iter().find(|&x| x.file == file && x.name == name) {
            None => return Err(format!("Couldn't find file: {:?} name: {:?}", file, name).into()),
            Some(x) => &x.rev[..],
        };

        let mut child = try!(Command::new("bk")
                             .args(&["co", "-p",
                                   &format!("-r{}", rev),
                                   file])
                             .current_dir(&self.base)
                             .stdout(Stdio::piped())
                             .spawn());
        let tree = try!(SureTree::load_from(child.stdout.as_mut().unwrap()));
        let status = try!(child.wait());
        if !status.success() {
            return Err(format!("Error running bk: {:?}", status).into());
        }
        Ok(tree)
    }

    fn bk_do(&self, args: &[&str]) -> Result<()> {
        let status = try!(Command::new("bk")
                          .args(args)
                          .current_dir(&self.base)
                          .status());
        if !status.success() {
            return Err(format!("Error running bk: {:?}", status).into());
        }
        Ok(())
    }
}

#[derive(Debug)]
pub struct BkSureFile {
    pub file: String,
    pub rev: String,
    pub name: String,
}
<|endoftext|>


<commit_before>use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::ops::{Neg, AddAssign, MulAssign};

use serde::de::{self, Deserialize, DeserializeSeed, Visitor, SeqAccess,
                MapAccess, EnumAccess, VariantAccess, IntoDeserializer};

type Result<T> = ::std::result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    Eof,
    Syntax,
    ExpectedArray,
    ExpectedArrayComma,
    ExpectedArrayEnd,
    ExpectedBoolean,
    ExpectedEnum,
    ExpectedChar,
    ExpectedInteger,
    ExpectedMap,
    ExpectedMapColon,
    ExpectedMapComma,
    ExpectedMapEnd,
    ExpectetUnit,
    ExpectedString,

    /// A custom error emitted by a serialized value.
    Message(String),
    TrailingCharacters,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            Error::Message(ref e) => write!(f, "Cusom message: {}", e),
        }
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error::Message(msg.to_string())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Message(ref e) => e,
        }
    }
}

pub struct Deserializer<'de> {
    input: &'de str,
}

impl<'de> Deserializer<'de> {
    pub fn from_str(input: &'de str) -> Self {
        Deserializer { input: input }
    }
}

pub fn from_str<'a, T>(s: &'a str) -> Result<T>
    where T: Deserialize<'a>
{
    let mut deserializer = Deserializer::from_str(s);
    let t = T::deserialize(&mut deserializer)?;
    if deserializer.input.is_empty() {
        Ok(t)
    } else {
        Err(Error::TrailingCharacters)
    }
}

impl<'de> Deserializer<'de> {
    fn skip(&mut self, bytes: usize) {
        self.input = &self.input[num..];
    }

    fn peek_char(&mut self) -> Result<char> {
        self.input.chars().next().ok_or(Error::Eof)
    }

    fn next_char(&mut self) -> Result<char> {
        let ch = self.peek_char()?;
        self.skip(ch.len_utf8());

        Ok(ch)
    }

    fn consume(&mut self, s: &str) -> bool {
        if self.input.starts_with(s) {
            self.skip(s.len());

            true
        } else {
            false
        }
    }

    fn parse_bool(&mut self) -> Result<bool> {
        if self.consume("true") {
            Ok(true)
        } else if self.consume("false") {
            Ok(false)
        } else {
            Err(Error::ExpectedBoolean)
        }
    }

    fn parse_unsigned<T>(&mut self) -> Result<T>
        where T: AddAssign<T> + MulAssign<T> + From<u8>
    {
        let mut int = match self.next_char()? {
            ch @ '0'...'9' => T::from(ch as u8 - b'0'),
            _ => {
                return Err(Error::ExpectedInteger);
            }
        };
        loop {
            match self.input.chars().next() {
                Some(ch @ '0'...'9') => {
                    self.input = &self.input[1..];
                    int *= T::from(10);
                    int += T::from(ch as u8 - b'0');
                }
                _ => {
                    return Ok(int);
                }
            }
        }
    }

    fn parse_signed_internal<T>(&mut self) -> Result<T>
        where T: AddAssign<T> + MulAssign<T> + From<i8>
    {
        // Uses `i8` instead of `u8`.

        let mut int = match self.next_char()? {
            ch @ '0'...'9' => T::from(ch as i8 - b'0' as i8),
            _ => {
                return Err(Error::ExpectedInteger);
            }
        };
        loop {
            match self.input.chars().next() {
                Some(ch @ '0'...'9') => {
                    self.input = &self.input[1..];
                    int *= T::from(10);
                    int += T::from(ch as i8 - b'0' as i8);
                }
                _ => {
                    return Ok(int);
                }
            }
        }
    }

    fn parse_signed<T>(&mut self) -> Result<T>
        where T: Neg<Output = T> + AddAssign<T> + MulAssign<T> + From<i8>
    {
        if self.consume("-") {
            self.parse_signed_internal::<T>().map(|x| -x)
        } else {
            self.consume("+");

            self.parse_signed_internal()
        }
    }

    fn parse_string(&mut self) -> Result<&'de str> {
        // TODO: Handle escapes

        if !self.consume("\"") {
            return Err(Error::ExpectedString);
        }

        match self.input.find('"') {
            Some(len) => {
                let s = &self.input[..len];
                self.input = &self.input[len + 1..];
                Ok(s)
            }
            None => Err(Error::Eof),
        }
    }
}

impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
    type Error = Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_bool(self.parse_bool()?)
    }

    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i8(self.parse_signed()?)
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i16(self.parse_signed()?)
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i32(self.parse_signed()?)
    }

    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i64(self.parse_signed()?)
    }

    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u8(self.parse_unsigned()?)
    }

    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u16(self.parse_unsigned()?)
    }

    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u32(self.parse_unsigned()?)
    }

    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u64(self.parse_unsigned()?)
    }

    fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        let s = self.parse_string()?;
        if s.len() != 1 {
            Err(Error::ExpectedChar)
        } else {
            visitor.visit_char(s.chars().nth(0).unwrap())
        }
    }

    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_borrowed_str(self.parse_string()?)
    }

    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.deserialize_str(visitor)
    }

    // The `Serializer` implementation on the previous page serialized byte
    // arrays as JSON arrays of bytes. Handle that representation here.
    fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.input.starts_with("null") {
            self.input = &self.input["null".len()..];
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }

    // In Serde, unit means an anonymous value containing no data.
    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("()") {
            visitor.visit_unit()
        } else {
            Err(Error::ExpectetUnit)
        }
    }

    fn deserialize_unit_struct<V>(
        self,
        _name: &'static str,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        // TODO: Should we also allow `MyUnitValue`?

        self.deserialize_unit(visitor)
    }

    fn deserialize_newtype_struct<V>(
        self,
        _name: &'static str,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_newtype_struct(self)
    }

    fn deserialize_seq<V>(mut self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("[") {
            let value = visitor.visit_seq(CommaSeparated::new(&mut self))?;
            if self.consume("]") {
                Ok(value)
            } else {
                Err(Error::ExpectedArrayEnd)
            }
        } else {
            Err(Error::ExpectedArray)
        }
    }

    // Tuples look just like sequences in JSON. Some formats may be able to
    // represent tuples more efficiently.
    //
    // As indicated by the length parameter, the `Deserialize` implementation
    // for a tuple in the Serde data model is required to know the length of the
    // tuple before even looking at the input data.
    fn deserialize_tuple<V>(
        self,
        _len: usize,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("(") {
            let value = visitor.visit_seq(CommaSeparated::new(&mut self))?;
            if self.consume(")") {
                Ok(value)
            } else {
                Err(Error::ExpectedArrayEnd)
            }
        } else {
            Err(Error::ExpectedArray)
        }
    }

    fn deserialize_tuple_struct<V>(
        self,
        name: &'static str,
        len: usize,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.consume(name);

        self.deserialize_tuple(len, visitor)
    }

    fn deserialize_map<V>(mut self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("{") {
            let value = visitor.visit_map(CommaSeparated::new(&mut self))?;

            if self.consume("}") {
                Ok(value)
            } else {
                Err(Error::ExpectedMapEnd)
            }
        } else {
            Err(Error::ExpectedMap)
        }
    }

    fn deserialize_struct<V>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        // TODO

        self.deserialize_map(visitor)
    }

    fn deserialize_enum<V>(
        self,
        _name: &'static str,
        _variants: &'static [&'static str],
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_identifier<V>(
        self,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_ignored_any<V>(
        self,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.deserialize_any(visitor)
    }
}

struct CommaSeparated<'a, 'de: 'a> {
    de: &'a mut Deserializer<'de>,
    first: bool,
}

impl<'a, 'de> CommaSeparated<'a, 'de> {
    fn new(de: &'a mut Deserializer<'de>) -> Self {
        CommaSeparated { de: de, first: true }
    }
}

impl<'de, 'a> SeqAccess<'de> for CommaSeparated<'a, 'de> {
    type Error = Error;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        // Check if there are no more elements.
        if self.de.peek_char()? == ']' {
            return Ok(None);
        }
        // Comma is required before every element except the first.
        if !self.first && self.de.next_char()? != ',' {
            return Err(Error::ExpectedArrayComma);
        }
        self.first = false;
        // Deserialize an array element.
        seed.deserialize(&mut *self.de).map(Some)
    }
}

impl<'de, 'a> MapAccess<'de> for CommaSeparated<'a, 'de> {
    type Error = Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
        where K: DeserializeSeed<'de>
    {
        unimplemented!()
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
        where V: DeserializeSeed<'de>
    {
        unimplemented!()
    }
}

struct Enum<'a, 'de: 'a> {
    de: &'a mut Deserializer<'de>,
}

impl<'a, 'de> Enum<'a, 'de> {
    fn new(de: &'a mut Deserializer<'de>) -> Self {
        Enum { de: de }
    }
}

impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
    type Error = Error;
    type Variant = Self;

    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
        where V: DeserializeSeed<'de>
    {
        unimplemented!()
    }
}

impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
    type Error = Error;

    fn unit_variant(self) -> Result<()> {
        unimplemented!()
    }

    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
        where T: DeserializeSeed<'de>
    {
        unimplemented!()
    }

    // Tuple variants are represented in JSON as `{ NAME: [DATA...] }` so
    // deserialize the sequence of data here.
    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn struct_variant<V>(
        self,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }
}
<commit_msg>Fix compilation error<commit_after>use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::ops::{Neg, AddAssign, MulAssign};

use serde::de::{self, Deserialize, DeserializeSeed, Visitor, SeqAccess,
                MapAccess, EnumAccess, VariantAccess, IntoDeserializer};

type Result<T> = ::std::result::Result<T, Error>;

#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    Eof,
    Syntax,
    ExpectedArray,
    ExpectedArrayComma,
    ExpectedArrayEnd,
    ExpectedBoolean,
    ExpectedEnum,
    ExpectedChar,
    ExpectedInteger,
    ExpectedMap,
    ExpectedMapColon,
    ExpectedMapComma,
    ExpectedMapEnd,
    ExpectetUnit,
    ExpectedString,

    /// A custom error emitted by a serialized value.
    Message(String),
    TrailingCharacters,
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            Error::Message(ref e) => write!(f, "Cusom message: {}", e),
            _ => unimplemented!()
        }
    }
}

impl de::Error for Error {
    fn custom<T: Display>(msg: T) -> Self {
        Error::Message(msg.to_string())
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Message(ref e) => e,
            _ => unimplemented!()
        }
    }
}

pub struct Deserializer<'de> {
    input: &'de str,
}

impl<'de> Deserializer<'de> {
    pub fn from_str(input: &'de str) -> Self {
        Deserializer { input: input }
    }
}

pub fn from_str<'a, T>(s: &'a str) -> Result<T>
    where T: Deserialize<'a>
{
    let mut deserializer = Deserializer::from_str(s);
    let t = T::deserialize(&mut deserializer)?;
    if deserializer.input.is_empty() {
        Ok(t)
    } else {
        Err(Error::TrailingCharacters)
    }
}

impl<'de> Deserializer<'de> {
    fn skip(&mut self, bytes: usize) {
        self.input = &self.input[bytes..];
    }

    fn peek_char(&mut self) -> Result<char> {
        self.input.chars().next().ok_or(Error::Eof)
    }

    fn next_char(&mut self) -> Result<char> {
        let ch = self.peek_char()?;
        self.skip(ch.len_utf8());

        Ok(ch)
    }

    fn consume(&mut self, s: &str) -> bool {
        if self.input.starts_with(s) {
            self.skip(s.len());

            true
        } else {
            false
        }
    }

    fn parse_bool(&mut self) -> Result<bool> {
        if self.consume("true") {
            Ok(true)
        } else if self.consume("false") {
            Ok(false)
        } else {
            Err(Error::ExpectedBoolean)
        }
    }

    fn parse_unsigned<T>(&mut self) -> Result<T>
        where T: AddAssign<T> + MulAssign<T> + From<u8>
    {
        let mut int = match self.next_char()? {
            ch @ '0'...'9' => T::from(ch as u8 - b'0'),
            _ => {
                return Err(Error::ExpectedInteger);
            }
        };
        loop {
            match self.input.chars().next() {
                Some(ch @ '0'...'9') => {
                    self.input = &self.input[1..];
                    int *= T::from(10);
                    int += T::from(ch as u8 - b'0');
                }
                _ => {
                    return Ok(int);
                }
            }
        }
    }

    fn parse_signed_internal<T>(&mut self) -> Result<T>
        where T: AddAssign<T> + MulAssign<T> + From<i8>
    {
        // Uses `i8` instead of `u8`.

        let mut int = match self.next_char()? {
            ch @ '0'...'9' => T::from(ch as i8 - b'0' as i8),
            _ => {
                return Err(Error::ExpectedInteger);
            }
        };
        loop {
            match self.input.chars().next() {
                Some(ch @ '0'...'9') => {
                    self.input = &self.input[1..];
                    int *= T::from(10);
                    int += T::from(ch as i8 - b'0' as i8);
                }
                _ => {
                    return Ok(int);
                }
            }
        }
    }

    fn parse_signed<T>(&mut self) -> Result<T>
        where T: Neg<Output = T> + AddAssign<T> + MulAssign<T> + From<i8>
    {
        if self.consume("-") {
            self.parse_signed_internal::<T>().map(|x| -x)
        } else {
            self.consume("+");

            self.parse_signed_internal()
        }
    }

    fn parse_string(&mut self) -> Result<&'de str> {
        // TODO: Handle escapes

        if !self.consume("\"") {
            return Err(Error::ExpectedString);
        }

        match self.input.find('"') {
            Some(len) => {
                let s = &self.input[..len];
                self.input = &self.input[len + 1..];
                Ok(s)
            }
            None => Err(Error::Eof),
        }
    }
}

impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
    type Error = Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_bool(self.parse_bool()?)
    }

    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i8(self.parse_signed()?)
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i16(self.parse_signed()?)
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i32(self.parse_signed()?)
    }

    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_i64(self.parse_signed()?)
    }

    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u8(self.parse_unsigned()?)
    }

    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u16(self.parse_unsigned()?)
    }

    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u32(self.parse_unsigned()?)
    }

    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_u64(self.parse_unsigned()?)
    }

    fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        let s = self.parse_string()?;
        if s.len() != 1 {
            Err(Error::ExpectedChar)
        } else {
            visitor.visit_char(s.chars().nth(0).unwrap())
        }
    }

    fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_borrowed_str(self.parse_string()?)
    }

    fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.deserialize_str(visitor)
    }

    // The `Serializer` implementation on the previous page serialized byte
    // arrays as JSON arrays of bytes. Handle that representation here.
    fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.input.starts_with("null") {
            self.input = &self.input["null".len()..];
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }

    // In Serde, unit means an anonymous value containing no data.
    fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("()") {
            visitor.visit_unit()
        } else {
            Err(Error::ExpectetUnit)
        }
    }

    fn deserialize_unit_struct<V>(
        self,
        _name: &'static str,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        // TODO: Should we also allow `MyUnitValue`?

        self.deserialize_unit(visitor)
    }

    fn deserialize_newtype_struct<V>(
        self,
        _name: &'static str,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        visitor.visit_newtype_struct(self)
    }

    fn deserialize_seq<V>(mut self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("[") {
            let value = visitor.visit_seq(CommaSeparated::new(&mut self))?;
            if self.consume("]") {
                Ok(value)
            } else {
                Err(Error::ExpectedArrayEnd)
            }
        } else {
            Err(Error::ExpectedArray)
        }
    }

    // Tuples look just like sequences in JSON. Some formats may be able to
    // represent tuples more efficiently.
    //
    // As indicated by the length parameter, the `Deserialize` implementation
    // for a tuple in the Serde data model is required to know the length of the
    // tuple before even looking at the input data.
    fn deserialize_tuple<V>(
        mut self,
        _len: usize,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("(") {
            let value = visitor.visit_seq(CommaSeparated::new(&mut self))?;
            if self.consume(")") {
                Ok(value)
            } else {
                Err(Error::ExpectedArrayEnd)
            }
        } else {
            Err(Error::ExpectedArray)
        }
    }

    fn deserialize_tuple_struct<V>(
        self,
        name: &'static str,
        len: usize,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.consume(name);

        self.deserialize_tuple(len, visitor)
    }

    fn deserialize_map<V>(mut self, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        if self.consume("{") {
            let value = visitor.visit_map(CommaSeparated::new(&mut self))?;

            if self.consume("}") {
                Ok(value)
            } else {
                Err(Error::ExpectedMapEnd)
            }
        } else {
            Err(Error::ExpectedMap)
        }
    }

    fn deserialize_struct<V>(
        self,
        _name: &'static str,
        _fields: &'static [&'static str],
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        // TODO

        self.deserialize_map(visitor)
    }

    fn deserialize_enum<V>(
        self,
        _name: &'static str,
        _variants: &'static [&'static str],
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_identifier<V>(
        self,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn deserialize_ignored_any<V>(
        self,
        visitor: V
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        self.deserialize_any(visitor)
    }
}

struct CommaSeparated<'a, 'de: 'a> {
    de: &'a mut Deserializer<'de>,
    first: bool,
}

impl<'a, 'de> CommaSeparated<'a, 'de> {
    fn new(de: &'a mut Deserializer<'de>) -> Self {
        CommaSeparated { de: de, first: true }
    }
}

impl<'de, 'a> SeqAccess<'de> for CommaSeparated<'a, 'de> {
    type Error = Error;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
        where T: DeserializeSeed<'de>
    {
        // Check if there are no more elements.
        if self.de.peek_char()? == ']' {
            return Ok(None);
        }
        // Comma is required before every element except the first.
        if !self.first && self.de.next_char()? != ',' {
            return Err(Error::ExpectedArrayComma);
        }
        self.first = false;
        // Deserialize an array element.
        seed.deserialize(&mut *self.de).map(Some)
    }
}

impl<'de, 'a> MapAccess<'de> for CommaSeparated<'a, 'de> {
    type Error = Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
        where K: DeserializeSeed<'de>
    {
        unimplemented!()
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
        where V: DeserializeSeed<'de>
    {
        unimplemented!()
    }
}

struct Enum<'a, 'de: 'a> {
    de: &'a mut Deserializer<'de>,
}

impl<'a, 'de> Enum<'a, 'de> {
    fn new(de: &'a mut Deserializer<'de>) -> Self {
        Enum { de: de }
    }
}

impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
    type Error = Error;
    type Variant = Self;

    fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
        where V: DeserializeSeed<'de>
    {
        unimplemented!()
    }
}

impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
    type Error = Error;

    fn unit_variant(self) -> Result<()> {
        unimplemented!()
    }

    fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
        where T: DeserializeSeed<'de>
    {
        unimplemented!()
    }

    // Tuple variants are represented in JSON as `{ NAME: [DATA...] }` so
    // deserialize the sequence of data here.
    fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }

    fn struct_variant<V>(
        self,
        _fields: &'static [&'static str],
        visitor: V,
    ) -> Result<V::Value>
        where V: Visitor<'de>
    {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Logical Volumes

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeMap;

use parser::{
    LvmTextMap,
    TextMapOps,
    Entry,
    status_from_textmap,
};
use Device;
use PV;

/// A Logical Volume that is created from a Volume Group.
#[derive(Debug)]
pub struct LV {
    /// The name.
    pub name: String,
    /// The UUID.
    pub id: String,
    /// The status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Created by this host.
    pub creation_host: String,
    /// Created at this Unix time.
    pub creation_time: i64,
    /// A list of the segments comprising the LV.
    pub segments: Vec<Box<segment::Segment>>,
    /// The major/minor number of the LV.
    pub device: Option<Device>,
}

impl LV {
    /// The total number of extents used by this logical volume.
    pub fn used_extents(&self) -> u64 {
        self.segments
            .iter()
            .map(|x| x.extent_count())
            .sum()
    }
}

impl PartialEq for LV {
    fn eq(&self, other: &LV) -> bool {
        self.name == other.name
    }
}

pub fn used_areas(lv: &LV) -> Vec<(Device, u64, u64)> {
    let mut v = Vec::new();
    for seg in &lv.segments {
        v.extend(seg.used_areas())
    }
    v
}

/// Construct an LV from an LvmTextMap.
pub fn from_textmap(name: &str, map: &LvmTextMap, pvs: &BTreeMap<String, PV>) -> Result<LV> {
    let err = || Error::new(Other, "lv textmap parsing error");

    let id = try!(map.string_from_textmap("id").ok_or(err()));
    let creation_host = try!(map.string_from_textmap("creation_host")
                             .ok_or(err()));
    let creation_time = try!(map.i64_from_textmap("creation_time")
                             .ok_or(err()));
    let segment_count = try!(map.i64_from_textmap("segment_count")
                             .ok_or(err()));

    let segments: Vec<_> = (0..segment_count)
        .map(|num| {
            let name = format!("segment{}", num+1);
            let seg_dict = try!(map.textmap_from_textmap(&name).ok_or(err()));
            segment::from_textmap(seg_dict, pvs)
        })
        .filter_map(|seg| seg.ok())
        .collect();

    let status = try!(status_from_textmap(map));

    let flags: Vec<_> = try!(map.list_from_textmap("flags").ok_or(err()))
        .iter()
        .filter_map(|item| match item {
            &Entry::String(ref x) => Some(x.clone()),
            _ => {None},
        })
        .collect();

    Ok(LV {
        name: name.to_string(),
        id: id.to_string(),
        status: status,
        flags: flags,
        creation_host: creation_host.to_string(),
        creation_time: creation_time,
        segments: segments,
        device: None,
    })
}

pub fn to_textmap(lv: &LV, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap {
    let mut map = LvmTextMap::new();

    map.insert("id".to_string(), Entry::String(lv.id.clone()));

    map.insert("status".to_string(),
               Entry::List(
                   Box::new(
                       lv.status
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("flags".to_string(),
               Entry::List(
                   Box::new(
                       lv.flags
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("creation_host".to_string(),
               Entry::String(lv.creation_host.clone()));
    map.insert("creation_time".to_string(),
               Entry::Number(lv.creation_time as i64));

    map.insert("segment_count".to_string(),
               Entry::Number(lv.segments.len() as i64));

    for (i, seg) in lv.segments.iter().enumerate() {
        map.insert(format!("segment{}", i+1),
                   Entry::TextMap(
                       Box::new(seg.to_textmap(dev_to_idx))));
    }

    map
}

pub mod segment {
    use std::io::Result;
    use std::io::Error;
    use std::io::ErrorKind::Other;
    use std::collections::BTreeMap;
    use std::fmt;

    use parser::{
        LvmTextMap,
        TextMapOps,
        Entry,
    };
    use PV;
    use VG;
    use Device;

    /// Used to treat segment types polymorphically
    pub trait Segment : fmt::Debug {
        /// Convert this segment to an LvmTextMap.
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap;
        /// Returns the first extent of the segment.
        fn start_extent(&self) -> u64;
        /// Returns how many extents are in the segment.
        fn extent_count(&self) -> u64;
        /// Returns which PVs the segment depends on.
        fn pv_dependencies(&self) -> Vec<Device>;
        /// Returns areas that make up the segment.
        fn used_areas(&self) -> Vec<(Device, u64, u64)>;
        /// Returns the name of the DM target that handles this segment.
        fn dm_type(&self) -> &'static str;
        /// Generates the parameters to send to DM for this segment.
        fn dm_params(&self, vg: &VG) -> String;
    }

    pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                        -> Result<Box<Segment>> {
        match map.string_from_textmap("type") {
            Some("striped") => StripedSegment::from_textmap(map, pvs),
            _ => unimplemented!(),
        }
    }

    /// A striped Logical Volume Segment.
    #[derive(Debug, PartialEq)]
    pub struct StripedSegment {
        /// The first extent within the LV this segment comprises.
        pub start_extent: u64,
        /// How many extents this segment comprises
        pub extent_count: u64,
        /// Hoy many 512-byte sectors per stripe
        pub stripe_size: Option<u64>,
        /// Stripes contain the Device and the starting PV extent.
        pub stripes: Vec<(Device, u64)>,
    }

    impl StripedSegment {
        pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                            -> Result<Box<Segment>> {
            let err = || Error::new(Other, "segment textmap parsing error");

            let stripe_list = try!(map.list_from_textmap("stripes").ok_or(err()));

            let mut stripes: Vec<_> = Vec::new();
            for slc in stripe_list.chunks(2) {
                let name = match &slc[0] {
                    &Entry::String(ref x) => {
                        let pv = try!(pvs.get(x).ok_or(err()));
                        pv.device
                    },
                    _ => return Err(err())
                };
                let val = match slc[1] {
                    Entry::Number(x) => x,
                    _ => return Err(err())
                };
                stripes.push((name, val as u64));
            }

            Ok(Box::new(StripedSegment {
                start_extent: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) as u64,
                extent_count: try!(
                    map.i64_from_textmap("extent_count").ok_or(err())) as u64,
                stripes: stripes,
                stripe_size: map.i64_from_textmap("start_extent").map(|x| x as u64),
            }))
        }
    }

    impl Segment for StripedSegment {
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>)
                          -> LvmTextMap {
            let mut map = LvmTextMap::new();

            map.insert("start_extent".to_string(),
                       Entry::Number(self.start_extent as i64));
            map.insert("extent_count".to_string(),
                       Entry::Number(self.extent_count as i64));
            map.insert("type".to_string(),
                       Entry::String("striped".to_string()));
            map.insert("stripe_count".to_string(),
                       Entry::Number(self.stripes.len() as i64));
            if let Some(stripe_size) = self.stripe_size {
                map.insert("stripe_size".to_string(),
                           Entry::Number(stripe_size as i64));
            }

            map.insert("stripes".to_string(),
                       Entry::List(
                           Box::new(
                               self.stripes
                                   .iter()
                                   .map(|&(k, v)| {
                                       let name = format!(
                                           "pv{}", dev_to_idx.get(&k).unwrap());
                                       vec![
                                           Entry::String(name),
                                           Entry::Number(v as i64)
                                               ]
                                           .into_iter()
                                   })
                                   .flat_map(|x| x)
                                   .collect())));
            map
        }

        fn start_extent(&self) -> u64 {
            self.start_extent
        }

        fn extent_count(&self) -> u64 {
            self.extent_count
        }

        fn pv_dependencies(&self) -> Vec<Device> {
            self.stripes.iter()
                .map(|&(dev, _)| dev)
                .collect()
        }

        // returns (device, start_extent, length)
        fn used_areas(&self) -> Vec<(Device, u64, u64)> {
            self.stripes.iter()
                .map(|&(dev, ext)| (dev, ext, self.extent_count))
                .collect()
        }

        fn dm_type(&self) -> &'static str {
            if self.stripes.len() == 1 {
                "linear"
            } else {
                "striped"
            }
        }

        fn dm_params(&self, vg: &VG) -> String {
            if self.stripes.len() == 1 {
                let (dev, start_ext) = self.stripes[0];
                let pv = vg.pv_get(dev).unwrap();
                format!("{}:{} {}", dev.major, dev.minor,
                        (start_ext * vg.extent_size()) + pv.pe_start)
            } else {
                let stripes: Vec<_> = self.stripes.iter()
                    .map(|&(dev, start_ext)| {
                        let pv = vg.pv_get(dev).unwrap();
                        format!("{}:{} {}", dev.major, dev.minor,
                                (start_ext * vg.extent_size()) + pv.pe_start)
                            })
                    .collect();

                format!("{} {} {}",
                        self.stripes.len(),
                        self.stripe_size.unwrap(),
                        stripes.join(" "))
            }
        }
    }
}
<commit_msg>LV: Implement ThinpoolSegment<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//! Logical Volumes

use std::io::Result;
use std::io::Error;
use std::io::ErrorKind::Other;
use std::collections::BTreeMap;

use parser::{
    LvmTextMap,
    TextMapOps,
    Entry,
    status_from_textmap,
};
use Device;
use PV;

/// A Logical Volume that is created from a Volume Group.
#[derive(Debug)]
pub struct LV {
    /// The name.
    pub name: String,
    /// The UUID.
    pub id: String,
    /// The status.
    pub status: Vec<String>,
    /// Flags.
    pub flags: Vec<String>,
    /// Created by this host.
    pub creation_host: String,
    /// Created at this Unix time.
    pub creation_time: i64,
    /// A list of the segments comprising the LV.
    pub segments: Vec<Box<segment::Segment>>,
    /// The major/minor number of the LV.
    pub device: Option<Device>,
}

impl LV {
    /// The total number of extents used by this logical volume.
    pub fn used_extents(&self) -> u64 {
        self.segments
            .iter()
            .map(|x| x.extent_count())
            .sum()
    }
}

impl PartialEq for LV {
    fn eq(&self, other: &LV) -> bool {
        self.name == other.name
    }
}

pub fn used_areas(lv: &LV) -> Vec<(Device, u64, u64)> {
    let mut v = Vec::new();
    for seg in &lv.segments {
        v.extend(seg.used_areas())
    }
    v
}

/// Construct an LV from an LvmTextMap.
pub fn from_textmap(name: &str, map: &LvmTextMap, pvs: &BTreeMap<String, PV>) -> Result<LV> {
    let err = || Error::new(Other, "lv textmap parsing error");

    let id = try!(map.string_from_textmap("id").ok_or(err()));
    let creation_host = try!(map.string_from_textmap("creation_host")
                             .ok_or(err()));
    let creation_time = try!(map.i64_from_textmap("creation_time")
                             .ok_or(err()));
    let segment_count = try!(map.i64_from_textmap("segment_count")
                             .ok_or(err()));

    let segments: Vec<_> = (0..segment_count)
        .map(|num| {
            let name = format!("segment{}", num+1);
            let seg_dict = try!(map.textmap_from_textmap(&name).ok_or(err()));
            segment::from_textmap(seg_dict, pvs)
        })
        .filter_map(|seg| seg.ok())
        .collect();

    let status = try!(status_from_textmap(map));

    let flags: Vec<_> = try!(map.list_from_textmap("flags").ok_or(err()))
        .iter()
        .filter_map(|item| match item {
            &Entry::String(ref x) => Some(x.clone()),
            _ => {None},
        })
        .collect();

    Ok(LV {
        name: name.to_string(),
        id: id.to_string(),
        status: status,
        flags: flags,
        creation_host: creation_host.to_string(),
        creation_time: creation_time,
        segments: segments,
        device: None,
    })
}

pub fn to_textmap(lv: &LV, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap {
    let mut map = LvmTextMap::new();

    map.insert("id".to_string(), Entry::String(lv.id.clone()));

    map.insert("status".to_string(),
               Entry::List(
                   Box::new(
                       lv.status
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("flags".to_string(),
               Entry::List(
                   Box::new(
                       lv.flags
                           .iter()
                           .map(|x| Entry::String(x.clone()))
                           .collect())));

    map.insert("creation_host".to_string(),
               Entry::String(lv.creation_host.clone()));
    map.insert("creation_time".to_string(),
               Entry::Number(lv.creation_time as i64));

    map.insert("segment_count".to_string(),
               Entry::Number(lv.segments.len() as i64));

    for (i, seg) in lv.segments.iter().enumerate() {
        map.insert(format!("segment{}", i+1),
                   Entry::TextMap(
                       Box::new(seg.to_textmap(dev_to_idx))));
    }

    map
}

pub mod segment {
    use std::io::Result;
    use std::io::Error;
    use std::io::ErrorKind::Other;
    use std::collections::BTreeMap;
    use std::fmt;

    use parser::{
        LvmTextMap,
        TextMapOps,
        Entry,
    };
    use PV;
    use VG;
    use Device;

    /// Used to treat segment types polymorphically
    pub trait Segment : fmt::Debug {
        /// Convert this segment to an LvmTextMap.
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>) -> LvmTextMap;
        /// Returns the first extent of the segment.
        fn start_extent(&self) -> u64;
        /// Returns how many extents are in the segment.
        fn extent_count(&self) -> u64;
        /// Returns which PVs the segment depends on.
        fn pv_dependencies(&self) -> Vec<Device>;
        /// Returns areas that make up the segment.
        fn used_areas(&self) -> Vec<(Device, u64, u64)>;
        /// Returns the name of the DM target that handles this segment.
        fn dm_type(&self) -> &'static str;
        /// Generates the parameters to send to DM for this segment.
        fn dm_params(&self, vg: &VG) -> String;
    }

    pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                        -> Result<Box<Segment>> {
        match map.string_from_textmap("type") {
            Some("striped") => StripedSegment::from_textmap(map, pvs),
            Some("thin-pool") => ThinpoolSegment::from_textmap(map, pvs),
            _ => unimplemented!(),
        }
    }

    /// A striped Logical Volume Segment.
    #[derive(Debug, PartialEq)]
    pub struct StripedSegment {
        /// The first extent within the LV this segment comprises.
        pub start_extent: u64,
        /// How many extents this segment comprises
        pub extent_count: u64,
        /// Hoy many 512-byte sectors per stripe
        pub stripe_size: Option<u64>,
        /// Stripes contain the Device and the starting PV extent.
        pub stripes: Vec<(Device, u64)>,
    }

    impl StripedSegment {
        pub fn from_textmap(map: &LvmTextMap, pvs: &BTreeMap<String, PV>)
                            -> Result<Box<Segment>> {
            let err = || Error::new(Other, "segment textmap parsing error");

            let stripe_list = try!(map.list_from_textmap("stripes").ok_or(err()));

            let mut stripes: Vec<_> = Vec::new();
            for slc in stripe_list.chunks(2) {
                let name = match &slc[0] {
                    &Entry::String(ref x) => {
                        let pv = try!(pvs.get(x).ok_or(err()));
                        pv.device
                    },
                    _ => return Err(err())
                };
                let val = match slc[1] {
                    Entry::Number(x) => x,
                    _ => return Err(err())
                };
                stripes.push((name, val as u64));
            }

            Ok(Box::new(StripedSegment {
                start_extent: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) as u64,
                extent_count: try!(
                    map.i64_from_textmap("extent_count").ok_or(err())) as u64,
                stripes: stripes,
                stripe_size: map.i64_from_textmap("start_extent").map(|x| x as u64),
            }))
        }
    }

    impl Segment for StripedSegment {
        fn to_textmap(&self, dev_to_idx: &BTreeMap<Device, usize>)
                          -> LvmTextMap {
            let mut map = LvmTextMap::new();

            map.insert("start_extent".to_string(),
                       Entry::Number(self.start_extent as i64));
            map.insert("extent_count".to_string(),
                       Entry::Number(self.extent_count as i64));
            map.insert("type".to_string(),
                       Entry::String("striped".to_string()));
            map.insert("stripe_count".to_string(),
                       Entry::Number(self.stripes.len() as i64));
            if let Some(stripe_size) = self.stripe_size {
                map.insert("stripe_size".to_string(),
                           Entry::Number(stripe_size as i64));
            }

            map.insert("stripes".to_string(),
                       Entry::List(
                           Box::new(
                               self.stripes
                                   .iter()
                                   .map(|&(k, v)| {
                                       let name = format!(
                                           "pv{}", dev_to_idx.get(&k).unwrap());
                                       vec![
                                           Entry::String(name),
                                           Entry::Number(v as i64)
                                               ]
                                           .into_iter()
                                   })
                                   .flat_map(|x| x)
                                   .collect())));
            map
        }

        fn start_extent(&self) -> u64 {
            self.start_extent
        }

        fn extent_count(&self) -> u64 {
            self.extent_count
        }

        fn pv_dependencies(&self) -> Vec<Device> {
            self.stripes.iter()
                .map(|&(dev, _)| dev)
                .collect()
        }

        // returns (device, start_extent, length)
        fn used_areas(&self) -> Vec<(Device, u64, u64)> {
            self.stripes.iter()
                .map(|&(dev, ext)| (dev, ext, self.extent_count))
                .collect()
        }

        fn dm_type(&self) -> &'static str {
            if self.stripes.len() == 1 {
                "linear"
            } else {
                "striped"
            }
        }

        fn dm_params(&self, vg: &VG) -> String {
            if self.stripes.len() == 1 {
                let (dev, start_ext) = self.stripes[0];
                let pv = vg.pv_get(dev).unwrap();
                format!("{}:{} {}", dev.major, dev.minor,
                        (start_ext * vg.extent_size()) + pv.pe_start)
            } else {
                let stripes: Vec<_> = self.stripes.iter()
                    .map(|&(dev, start_ext)| {
                        let pv = vg.pv_get(dev).unwrap();
                        format!("{}:{} {}", dev.major, dev.minor,
                                (start_ext * vg.extent_size()) + pv.pe_start)
                            })
                    .collect();

                format!("{} {} {}",
                        self.stripes.len(),
                        self.stripe_size.unwrap(),
                        stripes.join(" "))
            }
        }
    }

    #[derive(Debug, PartialEq)]
    pub enum DiscardPolicy {
        Passdown,
        NoPassdown,
        Ignore,
    }

    /// A Thinpool Logical Volume Segment, tying together data and metadata LVs
    #[derive(Debug, PartialEq)]
    pub struct ThinpoolSegment {
        /// The first extent within the LV this segment comprises.
        pub start_extent: u64,
        /// How many extents this segment comprises
        pub extent_count: u64,
        /// The name of the metadata LV
        pub metadata_lv: String,
        /// The name of the data LV
        pub data_lv: String,
        /// The transaction ID
        pub transaction_id: u64,
        /// The chunk size.
        pub chunk_size: u64,
        /// The discard policy.
        pub discards: DiscardPolicy,
        /// Whether to zero new blocks.
        pub zero_new_blocks: bool,
    }

    impl ThinpoolSegment {
        pub fn from_textmap(map: &LvmTextMap, _pvs: &BTreeMap<String, PV>)
                            -> Result<Box<Segment>> {
            let err = || Error::new(Other, "thinpool segment textmap parsing error");

            let discards = match map.string_from_textmap("discards") {
                Some("passdown") => DiscardPolicy::Passdown,
                Some("nopassdown") => DiscardPolicy::NoPassdown,
                Some("ignore") => DiscardPolicy::Ignore,
                _ => return Err(Error::new(
                    Other, "Invalid text for \"discards\" in thinpool segment")),
            };

            Ok(Box::new(ThinpoolSegment {
                start_extent: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) as u64,
                extent_count: try!(
                    map.i64_from_textmap("extent_count").ok_or(err())) as u64,
                metadata_lv: try!(
                    map.string_from_textmap("metadata").ok_or(err())).to_string(),
                data_lv: try!(
                    map.string_from_textmap("pool").ok_or(err())).to_string(),
                transaction_id: try!(
                    map.i64_from_textmap("transaction_id").ok_or(err())) as u64,
                chunk_size: try!(
                    map.i64_from_textmap("chunk_size").ok_or(err())) as u64,
                discards: discards,
                zero_new_blocks: try!(
                    map.i64_from_textmap("start_extent").ok_or(err())) != 0,
            }))
        }
    }

    impl Segment for ThinpoolSegment {
        fn to_textmap(&self, _dev_to_idx: &BTreeMap<Device, usize>)
                      -> LvmTextMap {
            let mut map = LvmTextMap::new();

            let discards = match self.discards {
                DiscardPolicy::Passdown => "passdown".to_string(),
                DiscardPolicy::NoPassdown => "nopassdown".to_string(),
                DiscardPolicy::Ignore => "ignore".to_string(),
            };

            map.insert("start_extent".to_string(),
                       Entry::Number(self.start_extent as i64));
            map.insert("extent_count".to_string(),
                       Entry::Number(self.extent_count as i64));
            map.insert("type".to_string(),
                       Entry::String("thin-pool".to_string()));
            map.insert("metadata".to_string(),
                       Entry::String(self.metadata_lv.clone()));
            map.insert("pool".to_string(),
                       Entry::String(self.data_lv.clone()));
            map.insert("transaction_id".to_string(),
                       Entry::Number(self.transaction_id as i64));
            map.insert("chunk_size".to_string(),
                       Entry::Number(self.chunk_size as i64));
            map.insert("discards".to_string(), Entry::String(discards));
            map.insert("zero_new_blocks".to_string(),
                       Entry::Number(self.zero_new_blocks as i64));

            map
        }

        fn start_extent(&self) -> u64 {
            self.start_extent
        }

        fn extent_count(&self) -> u64 {
            self.extent_count
        }

        // None, they're all on subordinate devs
        fn pv_dependencies(&self) -> Vec<Device> {
            Vec::new()
        }

        // None, they're all on subordinate devs
        fn used_areas(&self) -> Vec<(Device, u64, u64)> {
            Vec::new()
        }

        fn dm_type(&self) -> &'static str {
            "thin-pool"
        }

        fn dm_params(&self, vg: &VG) -> String {
            let chunks = (self.extent_count * vg.extent_size()) / self.chunk_size;
            let mut ctor = format!(
                "{} {} {} {}",
                self.metadata_lv,
                self.data_lv,
                self.chunk_size,
                chunks / 5); // 80% low water mark

            if !self.zero_new_blocks {
                ctor.push_str(" skip_block_zeroing");
            }

            match self.discards {
                DiscardPolicy::Passdown => {},
                DiscardPolicy::NoPassdown => ctor.push_str(" no_discard_passdown"),
                DiscardPolicy::Ignore => ctor.push_str( " ignore_discard"),
            };

            ctor
        }
    }
}
<|endoftext|>