<commit_before>use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::path::Path;
use url::Url;

use ffi;
use ffi::opt;
use ffi::easy::Easy;
use http::Response;
use http::body::{Body,ToBody};
use {ProgressCb,ErrCode};

static DEFAULT_TIMEOUT_MS: uint = 30_000;

pub struct Handle {
    easy: Easy,
}

impl Handle {
    pub fn new() -> Handle {
        Handle { easy: Easy::new() }
            .timeout(DEFAULT_TIMEOUT_MS)
            .connect_timeout(DEFAULT_TIMEOUT_MS)
    }

    pub fn timeout(mut self, ms: uint) -> Handle {
        self.easy.setopt(opt::TIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn connect_timeout(mut self, ms: uint) -> Handle {
        self.easy.setopt(opt::CONNECTTIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn verbose(mut self) -> Handle {
        self.easy.setopt(opt::VERBOSE, 1u).unwrap();
        self
    }

    pub fn proxy<U: ToUrl>(mut self, proxy: U) -> Handle {
        proxy.with_url_str(|s| {
            self.easy.setopt(opt::PROXY, s).unwrap();
        });

        self
    }

    pub fn ssl_ca_path(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAPATH, path).unwrap();
        self
    }

    pub fn ssl_ca_info(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAINFO, path).unwrap();
        self
    }

    pub fn get<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Get).uri(uri)
    }

    pub fn head<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Head).uri(uri)
    }

    pub fn post<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Post).uri(uri).body(body)
    }

    pub fn put<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Put).uri(uri).body(body)
    }

    pub fn delete<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Delete).uri(uri)
    }
}

pub enum Method {
    Options,
    Get,
    Head,
    Post,
    Put,
    Delete,
    Trace,
    Connect
}

pub struct Request<'a, 'b> {
    err: Option<ErrCode>,
    handle: &'a mut Handle,
    method: Method,
    headers: HashMap<String, Vec<String>>,
    body: Option<Body<'b>>,
    body_type: Option<BodyType>,
    content_type: bool, // whether or not the content type was set
    expect_continue: bool, // whether to expect a 100 continue from the server
    progress: Option<ProgressCb<'b>>,
    follow: bool,
}

enum BodyType {
    Fixed(uint),
    Chunked,
}

impl<'a, 'b> Request<'a, 'b> {
    fn new<'a, 'b>(handle: &'a mut Handle, method: Method) -> Request<'a, 'b> {
        Request {
            err: None,
            handle: handle,
            method: method,
            headers: HashMap::new(),
            body: None,
            body_type: None,
            content_type: false,
            expect_continue: false,
            progress: None,
            follow: false,
        }
    }

    pub fn uri<U: ToUrl>(mut self, uri: U) -> Request<'a, 'b> {
        uri.with_url_str(|s| {
            match self.handle.easy.setopt(opt::URL, s) {
                Ok(_) => {}
                Err(e) => self.err = Some(e)
            }
        });

        self
    }

    pub fn body<B: ToBody<'b>>(mut self, body: B) -> Request<'a, 'b> {
        self.body = Some(body.to_body());
        self
    }

    pub fn content_type(mut self, ty: &str) -> Request<'a, 'b> {
        if !self.content_type {
            self.content_type = true;
            append_header(&mut self.headers, "Content-Type", ty);
        }

        self
    }

    pub fn content_length(mut self, len: uint) -> Request<'a, 'b> {
        self.body_type = Some(Fixed(len));
        self
    }

    pub fn chunked(mut self) -> Request<'a, 'b> {
        self.body_type = Some(Chunked);
        self
    }

    pub fn expect_continue(mut self) -> Request<'a, 'b> {
        self.expect_continue = true;
        self
    }

    pub fn header(mut self, name: &str, val: &str) -> Request<'a, 'b> {
        append_header(&mut self.headers, name, val);
        self
    }

    pub fn get_header<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.headers.find_equiv(&name).map(|a| a.as_slice())
    }

    pub fn headers<'c, 'd, I: Iterator<(&'c str, &'d str)>>(mut self, mut hdrs: I) -> Request<'a, 'b> {
        for (name, val) in hdrs {
            append_header(&mut self.headers, name, val);
        }

        self
    }

    pub fn progress(mut self, cb: ProgressCb<'b>) -> Request<'a, 'b> {
        self.progress = Some(cb);
        self
    }

    pub fn follow_redirects(mut self, follow: bool) -> Request<'a, 'b> {
        self.follow = follow;
        self
    }

    pub fn exec(self) -> Result<Response, ErrCode> {
        // Deconstruct the struct
        let Request {
            err,
            handle,
            method,
            mut headers,
            mut body,
            body_type,
            content_type,
            expect_continue,
            progress,
            follow,
            ..
        } = self;

        if follow {
            try!(handle.easy.setopt(opt::FOLLOWLOCATION, 1i));
        }

        match err {
            Some(e) => return Err(e),
            None => {}
        }

        // Clear custom headers set from the previous request
        try!(handle.easy.setopt(opt::HTTPHEADER, 0u));

        match method {
            Get => try!(handle.easy.setopt(opt::HTTPGET, 1i)),
            Head => try!(handle.easy.setopt(opt::NOBODY, 1i)),
            Post => try!(handle.easy.setopt(opt::POST, 1i)),
            Put => try!(handle.easy.setopt(opt::UPLOAD, 1i)),
            Delete => {
                if body.is_some() {
                    try!(handle.easy.setopt(opt::UPLOAD, 1i));
                }

                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "DELETE"));
            }
            _ => unimplemented!()
        }

        match body.as_ref() {
            None => {}
            Some(body) => {
                debug!("handling body");

                let body_type = body_type.unwrap_or(match body.get_size() {
                    Some(len) => Fixed(len),
                    None => Chunked,
                });

                match body_type {
                    Fixed(len) => {
                        match method {
                            Post => try!(handle.easy.setopt(opt::POSTFIELDSIZE, len)),
                            Put | Delete  => try!(handle.easy.setopt(opt::INFILESIZE, len)),
                            _ => {}
                        }
                        append_header(&mut headers, "Content-Length",
                                      len.to_string().as_slice());
                    }
                    Chunked => {
                        append_header(&mut headers, "Transfer-Encoding",
                                      "chunked");
                    }

                }

                if !content_type {
                    append_header(&mut headers, "Content-Type", "application/octet-stream");
                }

                if !expect_continue {
                    append_header(&mut headers, "Expect", "");
                }
            }
        }

        let mut ffi_headers = ffi::List::new();

        if !headers.is_empty() {
            let mut buf = Vec::new();

            for (k, v) in headers.iter() {
                buf.push_all(k.as_bytes());
                buf.push_all(b": ");

                for v in v.iter() {
                    buf.push_all(v.as_bytes());
                    buf.push(0);
                    ffi_headers.push_bytes(buf.as_slice());
                    buf.truncate(k.len() + 2);
                }

                buf.truncate(0);
            }

            try!(handle.easy.setopt(opt::HTTPHEADER, &ffi_headers));
        }

        handle.easy.perform(body.as_mut(), progress)
    }
}

fn append_header(map: &mut HashMap<String, Vec<String>>, key: &str, val: &str) {
    match map.entry(key.to_string()) {
        Vacant(entry) => {
            let mut values = Vec::new();
            values.push(val.to_string());
            entry.set(values)
        },
        Occupied(entry) => entry.into_mut()
    };
}

pub trait ToUrl{
    fn with_url_str(self, f: |&str|);
}

impl<'a> ToUrl for &'a str {
    fn with_url_str(self, f: |&str|) {
        f(self);
    }
}

impl<'a> ToUrl for &'a Url {
    fn with_url_str(self, f: |&str|) {
        self.to_string().with_url_str(f);
    }
}

impl ToUrl for String {
    fn with_url_str(self, f: |&str|) {
        self.as_slice().with_url_str(f);
    }
}

#[cfg(test)]
mod tests {
    use super::Handle;

    #[test]
    fn get_header() {
        let mut h = Handle::new();
        let r = h.get("/foo").header("foo", "bar");
        assert_eq!(r.get_header("foo"), Some(["bar".to_string()].as_slice()));
    }
}
<commit_msg>Make Request::new public<commit_after>use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use std::path::Path;
use url::Url;

use ffi;
use ffi::opt;
use ffi::easy::Easy;
use http::Response;
use http::body::{Body,ToBody};
use {ProgressCb,ErrCode};

static DEFAULT_TIMEOUT_MS: uint = 30_000;

pub struct Handle {
    easy: Easy,
}

impl Handle {
    pub fn new() -> Handle {
        Handle { easy: Easy::new() }
            .timeout(DEFAULT_TIMEOUT_MS)
            .connect_timeout(DEFAULT_TIMEOUT_MS)
    }

    pub fn timeout(mut self, ms: uint) -> Handle {
        self.easy.setopt(opt::TIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn connect_timeout(mut self, ms: uint) -> Handle {
        self.easy.setopt(opt::CONNECTTIMEOUT_MS, ms).unwrap();
        self
    }

    pub fn verbose(mut self) -> Handle {
        self.easy.setopt(opt::VERBOSE, 1u).unwrap();
        self
    }

    pub fn proxy<U: ToUrl>(mut self, proxy: U) -> Handle {
        proxy.with_url_str(|s| {
            self.easy.setopt(opt::PROXY, s).unwrap();
        });

        self
    }

    pub fn ssl_ca_path(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAPATH, path).unwrap();
        self
    }

    pub fn ssl_ca_info(mut self, path: &Path) -> Handle {
        self.easy.setopt(opt::CAINFO, path).unwrap();
        self
    }

    pub fn get<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Get).uri(uri)
    }

    pub fn head<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Head).uri(uri)
    }

    pub fn post<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Post).uri(uri).body(body)
    }

    pub fn put<'a, 'b, U: ToUrl, B: ToBody<'b>>(&'a mut self, uri: U, body: B) -> Request<'a, 'b> {
        Request::new(self, Put).uri(uri).body(body)
    }

    pub fn delete<'a, 'b, U: ToUrl>(&'a mut self, uri: U) -> Request<'a, 'b> {
        Request::new(self, Delete).uri(uri)
    }
}

pub enum Method {
    Options,
    Get,
    Head,
    Post,
    Put,
    Delete,
    Trace,
    Connect
}

pub struct Request<'a, 'b> {
    err: Option<ErrCode>,
    handle: &'a mut Handle,
    method: Method,
    headers: HashMap<String, Vec<String>>,
    body: Option<Body<'b>>,
    body_type: Option<BodyType>,
    content_type: bool, // whether or not the content type was set
    expect_continue: bool, // whether to expect a 100 continue from the server
    progress: Option<ProgressCb<'b>>,
    follow: bool,
}

enum BodyType {
    Fixed(uint),
    Chunked,
}

impl<'a, 'b> Request<'a, 'b> {
    pub fn new<'a, 'b>(handle: &'a mut Handle, method: Method) -> Request<'a, 'b> {
        Request {
            err: None,
            handle: handle,
            method: method,
            headers: HashMap::new(),
            body: None,
            body_type: None,
            content_type: false,
            expect_continue: false,
            progress: None,
            follow: false,
        }
    }

    pub fn uri<U: ToUrl>(mut self, uri: U) -> Request<'a, 'b> {
        uri.with_url_str(|s| {
            match self.handle.easy.setopt(opt::URL, s) {
                Ok(_) => {}
                Err(e) => self.err = Some(e)
            }
        });

        self
    }

    pub fn body<B: ToBody<'b>>(mut self, body: B) -> Request<'a, 'b> {
        self.body = Some(body.to_body());
        self
    }

    pub fn content_type(mut self, ty: &str) -> Request<'a, 'b> {
        if !self.content_type {
            self.content_type = true;
            append_header(&mut self.headers, "Content-Type", ty);
        }

        self
    }

    pub fn content_length(mut self, len: uint) -> Request<'a, 'b> {
        self.body_type = Some(Fixed(len));
        self
    }

    pub fn chunked(mut self) -> Request<'a, 'b> {
        self.body_type = Some(Chunked);
        self
    }

    pub fn expect_continue(mut self) -> Request<'a, 'b> {
        self.expect_continue = true;
        self
    }

    pub fn header(mut self, name: &str, val: &str) -> Request<'a, 'b> {
        append_header(&mut self.headers, name, val);
        self
    }

    pub fn get_header<'a>(&'a self, name: &str) -> Option<&'a [String]> {
        self.headers.find_equiv(&name).map(|a| a.as_slice())
    }

    pub fn headers<'c, 'd, I: Iterator<(&'c str, &'d str)>>(mut self, mut hdrs: I) -> Request<'a, 'b> {
        for (name, val) in hdrs {
            append_header(&mut self.headers, name, val);
        }

        self
    }

    pub fn progress(mut self, cb: ProgressCb<'b>) -> Request<'a, 'b> {
        self.progress = Some(cb);
        self
    }

    pub fn follow_redirects(mut self, follow: bool) -> Request<'a, 'b> {
        self.follow = follow;
        self
    }

    pub fn exec(self) -> Result<Response, ErrCode> {
        // Deconstruct the struct
        let Request {
            err,
            handle,
            method,
            mut headers,
            mut body,
            body_type,
            content_type,
            expect_continue,
            progress,
            follow,
            ..
        } = self;

        if follow {
            try!(handle.easy.setopt(opt::FOLLOWLOCATION, 1i));
        }

        match err {
            Some(e) => return Err(e),
            None => {}
        }

        // Clear custom headers set from the previous request
        try!(handle.easy.setopt(opt::HTTPHEADER, 0u));

        match method {
            Get => try!(handle.easy.setopt(opt::HTTPGET, 1i)),
            Head => try!(handle.easy.setopt(opt::NOBODY, 1i)),
            Post => try!(handle.easy.setopt(opt::POST, 1i)),
            Put => try!(handle.easy.setopt(opt::UPLOAD, 1i)),
            Delete => {
                if body.is_some() {
                    try!(handle.easy.setopt(opt::UPLOAD, 1i));
                }

                try!(handle.easy.setopt(opt::CUSTOMREQUEST, "DELETE"));
            }
            _ => unimplemented!()
        }

        match body.as_ref() {
            None => {}
            Some(body) => {
                debug!("handling body");

                let body_type = body_type.unwrap_or(match body.get_size() {
                    Some(len) => Fixed(len),
                    None => Chunked,
                });

                match body_type {
                    Fixed(len) => {
                        match method {
                            Post => try!(handle.easy.setopt(opt::POSTFIELDSIZE, len)),
                            Put | Delete  => try!(handle.easy.setopt(opt::INFILESIZE, len)),
                            _ => {}
                        }
                        append_header(&mut headers, "Content-Length",
                                      len.to_string().as_slice());
                    }
                    Chunked => {
                        append_header(&mut headers, "Transfer-Encoding",
                                      "chunked");
                    }

                }

                if !content_type {
                    append_header(&mut headers, "Content-Type", "application/octet-stream");
                }

                if !expect_continue {
                    append_header(&mut headers, "Expect", "");
                }
            }
        }

        let mut ffi_headers = ffi::List::new();

        if !headers.is_empty() {
            let mut buf = Vec::new();

            for (k, v) in headers.iter() {
                buf.push_all(k.as_bytes());
                buf.push_all(b": ");

                for v in v.iter() {
                    buf.push_all(v.as_bytes());
                    buf.push(0);
                    ffi_headers.push_bytes(buf.as_slice());
                    buf.truncate(k.len() + 2);
                }

                buf.truncate(0);
            }

            try!(handle.easy.setopt(opt::HTTPHEADER, &ffi_headers));
        }

        handle.easy.perform(body.as_mut(), progress)
    }
}

fn append_header(map: &mut HashMap<String, Vec<String>>, key: &str, val: &str) {
    match map.entry(key.to_string()) {
        Vacant(entry) => {
            let mut values = Vec::new();
            values.push(val.to_string());
            entry.set(values)
        },
        Occupied(entry) => entry.into_mut()
    };
}

pub trait ToUrl{
    fn with_url_str(self, f: |&str|);
}

impl<'a> ToUrl for &'a str {
    fn with_url_str(self, f: |&str|) {
        f(self);
    }
}

impl<'a> ToUrl for &'a Url {
    fn with_url_str(self, f: |&str|) {
        self.to_string().with_url_str(f);
    }
}

impl ToUrl for String {
    fn with_url_str(self, f: |&str|) {
        self.as_slice().with_url_str(f);
    }
}

#[cfg(test)]
mod tests {
    use super::Handle;

    #[test]
    fn get_header() {
        let mut h = Handle::new();
        let r = h.get("/foo").header("foo", "bar");
        assert_eq!(r.get_header("foo"), Some(["bar".to_string()].as_slice()));
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use ast::*;
use value::*;

struct Environment {
    symbol_tables: Vec<HashMap<String, Value>>
}

impl Environment {
    fn new() -> Environment {
        Environment {
            symbol_tables: Vec::new()
        }
    }

    fn start_scope(&mut self) {
        self.symbol_tables.push(HashMap::new());
    }

    fn end_scope(&mut self) {
        self.symbol_tables.pop();
    }

    fn declare(&mut self, variable: &Variable, value: &Value) {
        match *variable {
            Variable::Identifier(_, ref id) => {
                self.symbol_tables.last_mut().unwrap().insert(id.clone(), *value);
            }
        };
    }

    fn set(&mut self, identifier: &String, value: Value) {
        for table in self.symbol_tables.iter_mut().rev() {
            // TODO: Entry API
            if table.contains_key(identifier) {
                table.insert(identifier.clone(), value);
                return;
            }
        }
        panic!(format!("reference error: '{}' was not declared", identifier));
    }

    fn get_value(&mut self, identifier: &String) -> Value {
        for table in self.symbol_tables.iter().rev() {
            if let Some(val) = table.get(identifier) {
                return *val
            }
        }
        panic!(format!("reference error: '{}' was not declared", identifier));
    }
}

pub fn interpret_program(ast: &Vec<Statement>) {
    let mut env = Environment::new();
    env.start_scope();
    for statement in ast.iter() {
        interpret_statement(statement, &mut env);
    }
    env.end_scope();
}

fn interpret_statement(s: &Statement, env: &mut Environment) {
    match *s {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let val = interpret_expr(expr, env);
            env.declare(variable, &val);
            println!("{:?} => {}", variable, val);
        },
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let val = interpret_expr(expr, env);
            match *lhs_expr {
                LhsExpr::Identifier(ref id) => env.set(id, val)
            };
        },
        Statement::Block(ref statements) => {
            env.start_scope();
            for statement in statements.iter() {
                interpret_statement(statement, env);
            }
            env.end_scope();
        },
        Statement::Expression(ref expr) => {
            interpret_expr(expr, env);
        },
        Statement::Empty => {},
    }
}

fn interpret_expr(e: &Expr, env: &mut Environment) -> Value {
    match *e {
        Expr::Literal(ref x) => Value::from(x.clone()),
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let val1 = interpret_expr(expr1, env);
            let val2 = interpret_expr(expr2, env);
            match *op {
                BinaryOp::Add => val1 + val2,
                BinaryOp::Sub => val1 - val2,
                BinaryOp::Mul => val1 * val2,
                BinaryOp::Div => val1 / val2,
            }
        },
        Expr::Identifier(ref id) => env.get_value(&id),
    }
}
<commit_msg>Print the value of an expression used as a statement<commit_after>use std::collections::HashMap;

use ast::*;
use value::*;

struct Environment {
    symbol_tables: Vec<HashMap<String, Value>>
}

impl Environment {
    fn new() -> Environment {
        Environment {
            symbol_tables: Vec::new()
        }
    }

    fn start_scope(&mut self) {
        self.symbol_tables.push(HashMap::new());
    }

    fn end_scope(&mut self) {
        self.symbol_tables.pop();
    }

    fn declare(&mut self, variable: &Variable, value: &Value) {
        match *variable {
            Variable::Identifier(_, ref id) => {
                self.symbol_tables.last_mut().unwrap().insert(id.clone(), *value);
            }
        };
    }

    fn set(&mut self, identifier: &String, value: Value) {
        for table in self.symbol_tables.iter_mut().rev() {
            // TODO: Entry API
            if table.contains_key(identifier) {
                table.insert(identifier.clone(), value);
                return;
            }
        }
        panic!(format!("reference error: '{}' was not declared", identifier));
    }

    fn get_value(&mut self, identifier: &String) -> Value {
        for table in self.symbol_tables.iter().rev() {
            if let Some(val) = table.get(identifier) {
                return *val
            }
        }
        panic!(format!("reference error: '{}' was not declared", identifier));
    }
}

pub fn interpret_program(ast: &Vec<Statement>) {
    let mut env = Environment::new();
    env.start_scope();
    for statement in ast.iter() {
        interpret_statement(statement, &mut env);
    }
    env.end_scope();
}

fn interpret_statement(s: &Statement, env: &mut Environment) {
    match *s {
        Statement::VariableDeclaration(ref variable, ref expr) => {
            let val = interpret_expr(expr, env);
            env.declare(variable, &val);
            println!("{:?} => {}", variable, val);
        },
        Statement::Assignment(ref lhs_expr, ref expr) => {
            let val = interpret_expr(expr, env);
            match *lhs_expr {
                LhsExpr::Identifier(ref id) => env.set(id, val)
            };
        },
        Statement::Block(ref statements) => {
            env.start_scope();
            for statement in statements.iter() {
                interpret_statement(statement, env);
            }
            env.end_scope();
        },
        Statement::Expression(ref expr) => {
            let val = interpret_expr(expr, env);
            println!("Expression => {}", val);
        },
        Statement::Empty => {},
    }
}

fn interpret_expr(e: &Expr, env: &mut Environment) -> Value {
    match *e {
        Expr::Literal(ref x) => Value::from(x.clone()),
        Expr::BinaryExpression(ref expr1, ref op, ref expr2) => {
            let val1 = interpret_expr(expr1, env);
            let val2 = interpret_expr(expr2, env);
            match *op {
                BinaryOp::Add => val1 + val2,
                BinaryOp::Sub => val1 - val2,
                BinaryOp::Mul => val1 * val2,
                BinaryOp::Div => val1 / val2,
            }
        },
        Expr::Identifier(ref id) => env.get_value(&id),
    }
}
<|endoftext|>


<commit_before>//! This module implements the in-memory database
//!
//! A support folder with SDK debug symbols can be processed into a
//! in-memory database format which is a flat file on the file system
//! that gets mmaped into the process.
use std::io::{Write, Seek, SeekFrom};
use std::fs::File;
use std::mem;
use std::slice;
use std::path::Path;
use std::cell::RefCell;
use std::collections::HashMap;

use uuid::Uuid;
use xz2::write::XzEncoder;
use tempfile::tempfile;

use super::types::{IndexItem, StoredSlice, MemDbHeader, IndexedUuid};
use super::super::Result;
use super::super::sdk::{SdkInfo, DumpOptions, Objects};
use super::super::dsym::{Object, Variant};
use super::super::utils::{file_size_format, ProgressIndicator, copy_with_progress};

struct MemDbBuilder<W> {
    writer: RefCell<W>,
    tempfile: Option<RefCell<File>>,
    info: SdkInfo,
    symbols: Vec<String>,
    symbols_map: HashMap<String, u32>,
    object_names: Vec<String>,
    object_names_map: HashMap<String, u16>,
    object_uuid_mapping: Vec<(String, Uuid)>,
    variant_uuids: Vec<IndexedUuid>,
    variants: Vec<Vec<IndexItem>>,
    symbol_count: usize,
    progress: ProgressIndicator,
    options: DumpOptions,
}

trait WriteSeek : Write + Seek {}
impl<T: Write+Seek> WriteSeek for T {}

impl<W: Write + Seek> MemDbBuilder<W> {

    pub fn new(writer: W, info: &SdkInfo, opts: DumpOptions, object_count: usize)
        -> Result<MemDbBuilder<W>>
    {
        let rv = MemDbBuilder {
            writer: RefCell::new(writer),
            tempfile: if opts.compress {
                Some(RefCell::new(tempfile()?))
            } else {
                None
            },
            info: info.clone(),
            symbols: vec![],
            symbols_map: HashMap::new(),
            object_names: vec![],
            object_names_map: HashMap::new(),
            object_uuid_mapping: vec![],
            variant_uuids: vec![],
            variants: vec![],
            symbol_count: 0,
            progress: if opts.show_progress_bar {
                ProgressIndicator::new(object_count)
            } else {
                ProgressIndicator::disabled()
            },
            options: opts,
        };
        let header = MemDbHeader { ..Default::default() };
        rv.write(&header)?;
        rv.progress.set_message("Initializing");
        Ok(rv)
    }

    fn with_file<T, F: FnOnce(&mut WriteSeek) -> T>(&self, f: F) -> T {
        if self.options.compress {
            f(&mut *self.tempfile.as_ref().unwrap().borrow_mut() as &mut WriteSeek)
        } else {
            f(&mut *self.writer.borrow_mut() as &mut WriteSeek)
        }
    }

    fn write_bytes(&self, x: &[u8]) -> Result<usize> {
        self.with_file(|mut w| {
            w.write_all(x)?;
            Ok(x.len())
        })
    }

    fn write<T>(&self, x: &T) -> Result<usize> {
        unsafe {
            let bytes : *const u8 = mem::transmute(x);
            let size = mem::size_of_val(x);
            self.with_file(|mut w| {
                w.write_all(slice::from_raw_parts(bytes, size))?;
                Ok(size)
            })
        }
    }

    fn seek(&self, new_pos: usize) -> Result<()> {
        self.with_file(|mut w| {
            w.seek(SeekFrom::Start(new_pos as u64))?;
            Ok(())
        })
    }

    fn tell(&self) -> Result<usize> {
        self.with_file(|mut w| {
            Ok(w.seek(SeekFrom::Current(0))? as usize)
        })
    }

    fn add_symbol(&mut self, sym: &str) -> u32 {
        if let Some(&sym_id) = self.symbols_map.get(sym) {
            return sym_id as u32;
        }
        let symbol_count = self.symbols.len();
        self.symbols.push(sym.to_string());
        self.symbols_map.insert(sym.to_string(), symbol_count as u32);
        symbol_count as u32
    }

    fn add_object_name(&mut self, src: &str) -> u16 {
        if let Some(&src_id) = self.object_names_map.get(src) {
            return src_id as u16;
        }
        let object_count = self.object_names.len();
        self.object_names.push(src.to_string());
        self.object_names_map.insert(src.to_string(), object_count as u16);
        object_count as u16
    }

    pub fn write_object(&mut self, obj: &Object, filename: Option<&str>,
                        offset: usize) -> Result<()> {
        self.progress.inc(offset);
        for variant in obj.variants() {
            let src = variant.name().or(filename).unwrap();
            if let Some(fname) = Path::new(src).file_name().and_then(|x| x.to_str()) {
                self.progress.set_message(fname);
            }
            if variant.uuid().is_some() {
                self.write_object_variant(&obj, &variant, src)?;
            }
        }
        Ok(())
    }

    pub fn write_object_variant(&mut self, obj: &Object, var: &Variant,
                                src: &str) -> Result<()> {
        let mut symbols = obj.symbols(var.arch())?;
        let src_id = self.add_object_name(src);

        // build symbol index
        let mut index = vec![];
        for (idx, (addr, sym)) in symbols.iter().enumerate() {
            let sym_id = self.add_symbol(sym);
            index.push(IndexItem::new(addr - var.vmaddr(), src_id, Some(sym_id)));
            if idx % 100 == 0 {
                self.progress.tick();
            }
            self.symbol_count += 1;
        }

        // write an end marker if we know the image size
        if var.vmsize() > 0 {
            index.push(IndexItem::new(var.vmsize(), src_id, None));
            self.symbol_count += 1;
        }

        index.sort_by_key(|item| item.addr());

        // register variant and uuid
        self.variant_uuids.push(IndexedUuid::new(&var.uuid().unwrap(), self.variants.len()));
        self.object_uuid_mapping.push((
            format!("{}:{}", src, var.arch()),
            var.uuid().unwrap()
        ));
        self.variants.push(index);

        Ok(())
    }

    fn make_string_slices(&self, strings: &[String], _try_compress: bool) -> Result<Vec<StoredSlice>> {
        let mut slices = vec![];
        for (idx, string) in strings.iter().enumerate() {
            let offset = self.tell()?;
            let len = self.write_bytes(string.as_bytes())?;
            slices.push(StoredSlice::new(offset, len, false));
            if idx % 500 == 0 {
                self.progress.tick();
            }
        }
        Ok(slices)
    }

    fn write_slices(&self, slices: &[StoredSlice], start: &mut u32, len: &mut u32) -> Result<()> {
        *start = self.tell()? as u32;
        for (idx, item) in slices.iter().enumerate() {
            self.write(item)?;
            if idx % 500 == 0 {
                self.progress.tick();
            }
        }
        *len = slices.len() as u32;
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        self.progress.finish(&format!(
            "Processed {} symbols", self.symbol_count));
        self.progress.add_bar(3);

        let mut header = MemDbHeader { ..Default::default() };
        header.version = 2;
        header.sdk_info.set_from_sdk_info(&self.info);

        self.progress.inc(1);
        self.progress.set_message("Writing metadata");

        // start by writing out the index of the variants and record the slices.
        let mut slices = vec![];
        let mut ii_idx = 0;
        for variant in self.variants.iter() {
            let offset = self.tell()?;
            for index_item in variant {
                self.write(index_item)?;
                ii_idx += 1;
                if ii_idx % 100 == 0 {
                    self.progress.tick();
                }
            }
            slices.push(StoredSlice::new(offset, (self.tell()? - offset), false));
        }
        self.write_slices(&slices[..], &mut header.variants_start,
                          &mut header.variants_count)?;

        // next write out the UUIDs.  Since these are fixed length we do not
        // need to use slices here.
        header.uuids_start = self.tell()? as u32;
        header.uuids_count = self.variant_uuids.len() as u32;
        self.variant_uuids.sort_by_key(|x| x.uuid);
        for (idx, indexed_uuid) in self.variant_uuids.iter().enumerate() {
            self.write(indexed_uuid)?;
            if idx % 200 == 0 {
                self.progress.tick();
            }
        }

        // next we write out the name + arch -> uuid index mapping.  We also sort
        // this by uuid so that the index matches up.
        header.tagged_object_names_start = self.tell()? as u32;
        self.object_uuid_mapping.sort_by_key(|&(_, b)| b);
        for (idx, &(ref tagged_object, _)) in self.object_uuid_mapping.iter().enumerate() {
            self.write_bytes(format!("{}\x00", tagged_object).as_bytes())?;
            if idx % 100 == 0 {
                self.progress.tick();
            }
        }
        header.tagged_object_names_end = self.tell()? as u32;

        // now write out all the object name sources
        let slices = self.make_string_slices(&self.object_names[..], true)?;
        self.write_slices(&slices[..], &mut header.object_names_start,
                          &mut header.object_names_count)?;

        self.progress.inc(1);
        self.progress.set_message("Writing symbols");

        // now write out all the symbols
        let slices = self.make_string_slices(&self.symbols[..], true)?;
        self.write_slices(&slices[..], &mut header.symbols_start,
                          &mut header.symbols_count)?;

        self.progress.inc(1);
        self.progress.set_message("Writing headers");

        let file_size = self.tell()?;

        // write the updated header
        self.seek(0)?;
        self.write(&header)?;

        self.progress.finish(&format!(
            "Indexed {} variants", self.variant_uuids.len()));

        // compress if necessary
        if self.options.compress {
            self.progress.add_bar(file_size);
            self.progress.set_message("Compressing");
            self.seek(0)?;
            let mut reader = self.tempfile.as_ref().unwrap().borrow_mut();
            let mut writer = self.writer.borrow_mut();
            {
                let mut zwriter = XzEncoder::new(&mut *writer, 9);
                copy_with_progress(&self.progress, &mut *reader, &mut zwriter)?;
            }
            let compressed_file_size = writer.seek(SeekFrom::Current(0))? as usize;
            let pct = (compressed_file_size * 100) / file_size;
            self.progress.finish(&format!(
                "Compressed from {} to {} ({}% of original size)",
                file_size_format(file_size),
                file_size_format(compressed_file_size),
                pct));
        }

        Ok(())
    }
}

/// Dumps objects into a writer
pub fn dump_memdb<W: Write + Seek>(writer: W, info: &SdkInfo,
                                   opts: DumpOptions, objects: Objects)
    -> Result<()>
{
    let mut builder = MemDbBuilder::new(writer, info, opts, objects.file_count())?;
    for obj_res in objects {
        let (offset, filename, obj) = obj_res?;
        builder.write_object(&obj, Some(&filename), offset)?;
    }
    builder.flush()?;
    Ok(())
}
<commit_msg>Do not write uuid duplicates into the file<commit_after>//! This module implements the in-memory database
//!
//! A support folder with SDK debug symbols can be processed into a
//! in-memory database format which is a flat file on the file system
//! that gets mmaped into the process.
use std::io::{Write, Seek, SeekFrom};
use std::fs::File;
use std::mem;
use std::slice;
use std::path::Path;
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};

use uuid::Uuid;
use xz2::write::XzEncoder;
use tempfile::tempfile;

use super::types::{IndexItem, StoredSlice, MemDbHeader, IndexedUuid};
use super::super::Result;
use super::super::sdk::{SdkInfo, DumpOptions, Objects};
use super::super::dsym::{Object, Variant};
use super::super::utils::{file_size_format, ProgressIndicator, copy_with_progress};

struct MemDbBuilder<W> {
    writer: RefCell<W>,
    tempfile: Option<RefCell<File>>,
    info: SdkInfo,
    symbols: Vec<String>,
    symbols_map: HashMap<String, u32>,
    object_names: Vec<String>,
    object_names_map: HashMap<String, u16>,
    object_uuid_mapping: Vec<(String, Uuid)>,
    variant_uuids: Vec<IndexedUuid>,
    variant_uuids_seen: HashSet<Uuid>,
    variants: Vec<Vec<IndexItem>>,
    symbol_count: usize,
    progress: ProgressIndicator,
    options: DumpOptions,
}

trait WriteSeek : Write + Seek {}
impl<T: Write+Seek> WriteSeek for T {}

impl<W: Write + Seek> MemDbBuilder<W> {

    pub fn new(writer: W, info: &SdkInfo, opts: DumpOptions, object_count: usize)
        -> Result<MemDbBuilder<W>>
    {
        let rv = MemDbBuilder {
            writer: RefCell::new(writer),
            tempfile: if opts.compress {
                Some(RefCell::new(tempfile()?))
            } else {
                None
            },
            info: info.clone(),
            symbols: vec![],
            symbols_map: HashMap::new(),
            object_names: vec![],
            object_names_map: HashMap::new(),
            object_uuid_mapping: vec![],
            variant_uuids: vec![],
            variant_uuids_seen: HashSet::new(),
            variants: vec![],
            symbol_count: 0,
            progress: if opts.show_progress_bar {
                ProgressIndicator::new(object_count)
            } else {
                ProgressIndicator::disabled()
            },
            options: opts,
        };
        let header = MemDbHeader { ..Default::default() };
        rv.write(&header)?;
        rv.progress.set_message("Initializing");
        Ok(rv)
    }

    fn with_file<T, F: FnOnce(&mut WriteSeek) -> T>(&self, f: F) -> T {
        if self.options.compress {
            f(&mut *self.tempfile.as_ref().unwrap().borrow_mut() as &mut WriteSeek)
        } else {
            f(&mut *self.writer.borrow_mut() as &mut WriteSeek)
        }
    }

    fn write_bytes(&self, x: &[u8]) -> Result<usize> {
        self.with_file(|mut w| {
            w.write_all(x)?;
            Ok(x.len())
        })
    }

    fn write<T>(&self, x: &T) -> Result<usize> {
        unsafe {
            let bytes : *const u8 = mem::transmute(x);
            let size = mem::size_of_val(x);
            self.with_file(|mut w| {
                w.write_all(slice::from_raw_parts(bytes, size))?;
                Ok(size)
            })
        }
    }

    fn seek(&self, new_pos: usize) -> Result<()> {
        self.with_file(|mut w| {
            w.seek(SeekFrom::Start(new_pos as u64))?;
            Ok(())
        })
    }

    fn tell(&self) -> Result<usize> {
        self.with_file(|mut w| {
            Ok(w.seek(SeekFrom::Current(0))? as usize)
        })
    }

    fn add_symbol(&mut self, sym: &str) -> u32 {
        if let Some(&sym_id) = self.symbols_map.get(sym) {
            return sym_id as u32;
        }
        let symbol_count = self.symbols.len();
        self.symbols.push(sym.to_string());
        self.symbols_map.insert(sym.to_string(), symbol_count as u32);
        symbol_count as u32
    }

    fn add_object_name(&mut self, src: &str) -> u16 {
        if let Some(&src_id) = self.object_names_map.get(src) {
            return src_id as u16;
        }
        let object_count = self.object_names.len();
        self.object_names.push(src.to_string());
        self.object_names_map.insert(src.to_string(), object_count as u16);
        object_count as u16
    }

    pub fn write_object(&mut self, obj: &Object, filename: Option<&str>,
                        offset: usize) -> Result<()> {
        self.progress.inc(offset);
        for variant in obj.variants() {
            let src = variant.name().or(filename).unwrap();
            if let Some(fname) = Path::new(src).file_name().and_then(|x| x.to_str()) {
                self.progress.set_message(fname);
            }
            if let Some(uuid) = variant.uuid() {
                self.write_object_variant(&obj, &variant, &uuid, src)?;
            }
        }
        Ok(())
    }

    fn write_object_variant(&mut self, obj: &Object, var: &Variant,
                            uuid: &Uuid, src: &str) -> Result<bool> {
        self.object_uuid_mapping.push((
            format!("{}:{}", src, var.arch()),
            *uuid
        ));

        if self.variant_uuids_seen.contains(uuid) {
            return Ok(false);
        }
        self.variant_uuids_seen.insert(*uuid);

        let mut symbols = obj.symbols(var.arch())?;
        let src_id = self.add_object_name(src);

        // build symbol index
        let mut index = vec![];
        for (idx, (addr, sym)) in symbols.iter().enumerate() {
            let sym_id = self.add_symbol(sym);
            index.push(IndexItem::new(addr - var.vmaddr(), src_id, Some(sym_id)));
            if idx % 100 == 0 {
                self.progress.tick();
            }
            self.symbol_count += 1;
        }

        // write an end marker if we know the image size
        if var.vmsize() > 0 {
            index.push(IndexItem::new(var.vmsize(), src_id, None));
            self.symbol_count += 1;
        }

        index.sort_by_key(|item| item.addr());

        // register variant and uuid
        self.variant_uuids.push(IndexedUuid::new(uuid, self.variants.len()));
        self.variants.push(index);

        Ok(true)
    }

    fn make_string_slices(&self, strings: &[String], _try_compress: bool) -> Result<Vec<StoredSlice>> {
        let mut slices = vec![];
        for (idx, string) in strings.iter().enumerate() {
            let offset = self.tell()?;
            let len = self.write_bytes(string.as_bytes())?;
            slices.push(StoredSlice::new(offset, len, false));
            if idx % 500 == 0 {
                self.progress.tick();
            }
        }
        Ok(slices)
    }

    fn write_slices(&self, slices: &[StoredSlice], start: &mut u32, len: &mut u32) -> Result<()> {
        *start = self.tell()? as u32;
        for (idx, item) in slices.iter().enumerate() {
            self.write(item)?;
            if idx % 500 == 0 {
                self.progress.tick();
            }
        }
        *len = slices.len() as u32;
        Ok(())
    }

    pub fn flush(&mut self) -> Result<()> {
        self.progress.finish(&format!(
            "Processed {} symbols", self.symbol_count));
        self.progress.add_bar(3);

        let mut header = MemDbHeader { ..Default::default() };
        header.version = 2;
        header.sdk_info.set_from_sdk_info(&self.info);

        self.progress.inc(1);
        self.progress.set_message("Writing metadata");

        // start by writing out the index of the variants and record the slices.
        let mut slices = vec![];
        let mut ii_idx = 0;
        for variant in self.variants.iter() {
            let offset = self.tell()?;
            for index_item in variant {
                self.write(index_item)?;
                ii_idx += 1;
                if ii_idx % 100 == 0 {
                    self.progress.tick();
                }
            }
            slices.push(StoredSlice::new(offset, (self.tell()? - offset), false));
        }
        self.write_slices(&slices[..], &mut header.variants_start,
                          &mut header.variants_count)?;

        // next write out the UUIDs.  Since these are fixed length we do not
        // need to use slices here.
        header.uuids_start = self.tell()? as u32;
        header.uuids_count = self.variant_uuids.len() as u32;
        self.variant_uuids.sort_by_key(|x| x.uuid);
        for (idx, indexed_uuid) in self.variant_uuids.iter().enumerate() {
            self.write(indexed_uuid)?;
            if idx % 200 == 0 {
                self.progress.tick();
            }
        }

        // next we write out the name + arch -> uuid index mapping.  We also sort
        // this by uuid so that the index matches up.
        header.tagged_object_names_start = self.tell()? as u32;
        self.object_uuid_mapping.sort_by_key(|&(_, b)| b);
        for (idx, &(ref tagged_object, _)) in self.object_uuid_mapping.iter().enumerate() {
            self.write_bytes(format!("{}\x00", tagged_object).as_bytes())?;
            if idx % 100 == 0 {
                self.progress.tick();
            }
        }
        header.tagged_object_names_end = self.tell()? as u32;

        // now write out all the object name sources
        let slices = self.make_string_slices(&self.object_names[..], true)?;
        self.write_slices(&slices[..], &mut header.object_names_start,
                          &mut header.object_names_count)?;

        self.progress.inc(1);
        self.progress.set_message("Writing symbols");

        // now write out all the symbols
        let slices = self.make_string_slices(&self.symbols[..], true)?;
        self.write_slices(&slices[..], &mut header.symbols_start,
                          &mut header.symbols_count)?;

        self.progress.inc(1);
        self.progress.set_message("Writing headers");

        let file_size = self.tell()?;

        // write the updated header
        self.seek(0)?;
        self.write(&header)?;

        self.progress.finish(&format!(
            "Indexed {} variants", self.variant_uuids.len()));

        // compress if necessary
        if self.options.compress {
            self.progress.add_bar(file_size);
            self.progress.set_message("Compressing");
            self.seek(0)?;
            let mut reader = self.tempfile.as_ref().unwrap().borrow_mut();
            let mut writer = self.writer.borrow_mut();
            {
                let mut zwriter = XzEncoder::new(&mut *writer, 9);
                copy_with_progress(&self.progress, &mut *reader, &mut zwriter)?;
            }
            let compressed_file_size = writer.seek(SeekFrom::Current(0))? as usize;
            let pct = (compressed_file_size * 100) / file_size;
            self.progress.finish(&format!(
                "Compressed from {} to {} ({}% of original size)",
                file_size_format(file_size),
                file_size_format(compressed_file_size),
                pct));
        }

        Ok(())
    }
}

/// Dumps objects into a writer
pub fn dump_memdb<W: Write + Seek>(writer: W, info: &SdkInfo,
                                   opts: DumpOptions, objects: Objects)
    -> Result<()>
{
    let mut builder = MemDbBuilder::new(writer, info, opts, objects.file_count())?;
    for obj_res in objects {
        let (offset, filename, obj) = obj_res?;
        builder.write_object(&obj, Some(&filename), offset)?;
    }
    builder.flush()?;
    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use rustc_serialize::json::{ToJson, Json};
use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, UTC, DateTime};

use apple_music;
use spotify;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::state::State;
use model::enclosure::Enclosure;
use model::track::Track;
use model::artist::Artist;

static PROPS: [&'static str; 14]  = ["id",
                                     "provider",
                                     "identifier",
                                     "owner_id",
                                     "owner_name",
                                     "url",
                                     "title",
                                     "description",
                                     "thumbnail_url",
                                     "artwork_url",
                                     "published_at",
                                     "created_at",
                                     "updated_at",
                                     "state"];

#[derive(Debug, Clone)]
pub struct Album {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub owner_id:      Option<String>,
    pub owner_name:    Option<String>,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub published_at:  NaiveDateTime,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
    pub state:         State,
    pub tracks:        Vec<Track>,
    pub artists:       Option<Vec<Artist>>,
}

impl PartialEq for Album {
    fn eq(&self, p: &Album) -> bool {
        return self.identifier == p.identifier && self.provider == p.provider
    }
}

impl ToJson for Album {
    fn to_json(&self) -> Json {
        let published_at = DateTime::<UTC>::from_utc(self.published_at, UTC);
        let created_at   = DateTime::<UTC>::from_utc(self.created_at  , UTC);
        let updated_at   = DateTime::<UTC>::from_utc(self.updated_at  , UTC);
        let mut d = BTreeMap::new();
        d.insert("id".to_string()           , self.id.to_string().to_json());
        d.insert("provider".to_string()     , self.provider.to_json());
        d.insert("identifier".to_string()   , self.identifier.to_json());
        d.insert("owner_id".to_string()     , self.owner_id.to_json());
        d.insert("owner_name".to_string()   , self.owner_name.to_json());
        d.insert("url".to_string()          , self.url.to_json());
        d.insert("title".to_string()        , self.title.to_json());
        d.insert("description".to_string()  , self.description.to_json());
        d.insert("thumbnail_url".to_string(), self.thumbnail_url.to_json());
        d.insert("artwork_url".to_string()  , self.artwork_url.to_json());
        d.insert("published_at".to_string() , published_at.to_rfc3339().to_json());
        d.insert("created_at".to_string()   , created_at.to_rfc3339().to_json());
        d.insert("updated_at".to_string()   , updated_at.to_rfc3339().to_json());
        d.insert("state".to_string()        , self.state.to_json());
        d.insert("artists".to_string()      , self.artists.to_json());
        Json::Object(d)
    }
}

impl fmt::Display for Album {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl Model for Album {
    fn table_name() -> String { "albums".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }

    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Album> {
        let mut albums = Vec::new();
        for row in rows.iter() {
            let album = Album {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                owner_id:      row.get(3),
                owner_name:    row.get(4),
                url:           row.get(5),
                title:         row.get(6),
                description:   row.get(7),
                thumbnail_url: row.get(8),
                artwork_url:   row.get(9),
                published_at:  row.get(10),
                created_at:    row.get(11),
                updated_at:    row.get(12),
                state:         State::new(row.get(13)),
                tracks:        vec![],
                artists:       None,
            };
            albums.push(album)
        }
        albums
    }

    fn create(&self) -> Result<Album, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO albums (provider, identifier, url, title)
                                      VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut album = self.clone();
        for row in rows.iter() {
            album.id = row.get(0);
        }
        Ok(album)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE albums SET
                                      provider      = $2,
                                      identifier    = $3,
                                      owner_id      = $4,
                                      owner_name    = $5,
                                      url           = $6,
                                      title         = $7,
                                      description   = $8,
                                      thumbnail_url = $9,
                                      artwork_url   = $10,
                                      published_at  = $11,
                                      created_at    = $12,
                                      updated_at    = $13,
                                      state         = $14
                                      WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.owner_id,
                                  &self.owner_name,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.published_at,
                                  &self.created_at,
                                  &self.updated_at,
                                  &self.state.to_string(),
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl Enclosure for Album {
    fn new(provider: Provider, identifier: String) -> Album {
        Album {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            owner_id:      None,
            owner_name:    None,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            thumbnail_url: None,
            artwork_url:   None,
            published_at:  UTC::now().naive_utc(),
            created_at:    UTC::now().naive_utc(),
            updated_at:    UTC::now().naive_utc(),
            state:         State::Alive,
            tracks:        vec![],
            artists:       None,
        }
    }
    fn set_url(&mut self, url: String) -> &mut Album {
        self.url = url;
        self
    }
    fn set_owner_id(&mut self, owner_id: Option<String>) -> &mut Album {
        self.owner_id = owner_id;
        self
    }
    fn fetch_props(&mut self) -> &mut Album {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_album(&self.identifier, &country) {
                    Ok(album) => self.update_with_am_album(&album),
                    Err(_)    => self.disable(),
                }
            }
            Provider::Spotify => match spotify::fetch_album(&self.identifier) {
                Ok(album) => self.update_with_sp_album(&album),
                Err(_)    => self.disable(),
            },
            _ => self,
        }
    }
    fn find_by_entry_id(entry_id: Uuid) -> Vec<Album> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM albums p LEFT JOIN album_entries pe
                        ON p.id = pe.album_id
                        WHERE pe.entry_id = $1
                        ORDER BY p.published_at DESC",
                     Album::props_str("p."))).unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Album::rows_to_items(rows)
    }
}

impl Album {
    fn add_track(&mut self, track: &Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_tracks (track_id, album_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        Ok(())
    }
    fn add_artist(&mut self, artist: Artist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_artists (track_id, artist_id) VALUES ($1, $2)"));
        try!(stmt.query(&[&self.id, &artist.id]));
        match self.artists {
            Some(ref mut artists) => artists.push(artist),
            None                  => self.artists = Some(vec![artist]),

        }
        Ok(())
    }

    pub fn from_sp_album(album: &spotify::Album) -> Album {
        Album::new(Provider::Spotify, (*album).id.to_string())
            .update_with_sp_album(album)
            .clone()
    }

    pub fn from_am_album(album: &apple_music::Album) -> Album {
        Album::new(Provider::AppleMusic, (*album).id.to_string())
            .update_with_am_album(album)
            .clone()
    }

    fn add_tracks(&mut self, tracks: Vec<Track>) {
        self.tracks = tracks.iter().map(|t| {
            let mut t = t.clone();
            if let Ok(new_track) = Track::find_or_create(t.provider,
                                                         t.identifier.to_string()) {
                t.id      = new_track.id;
                let _     = self.add_track(&t);
            };
            t
        }).collect::<Vec<_>>();
    }

    pub fn update_with_sp_album(&mut self, album: &spotify::Album) -> &mut Album {
        self.provider       = Provider::Spotify;
        self.identifier     = album.id.to_string();
        if album.artists.len() > 0 {
            self.owner_id   = Some(album.artists[0].id.clone());
            self.owner_name = Some(album.artists[0].name.clone());
        }
        self.url            = album.uri.clone();
        self.title          = album.name.clone();
        self.description    = None;
        self.state          = State::Alive;
        self.published_at   = UTC::now().naive_utc();
        if album.images.len() > 0 {
            self.artwork_url   = Some(album.images[0].url.clone());
            self.thumbnail_url = Some(album.images[0].url.clone());
        }
        if album.images.len() > 1 {
            self.thumbnail_url = Some(album.images[1].url.clone());
        }
        let artists = album.artists
            .iter()
            .map(|artist| {
                Artist::find_or_create(self.provider, artist.id.to_string())
                    .map(|mut a| a.update_with_sp_artist(&artist).clone())
            })
            .filter(|r| r.is_ok())
            .map(|r| r.unwrap())
            .collect::<Vec<Artist>>();
        for mut a in artists {
            let _ = a.save();
            let _ = self.add_artist(a);
        }
        let tracks = album.tracks.clone()
            .map(|t| t.items).unwrap_or(vec![]).iter()
            .map(|ref t| Track::from_sp_track(t))
            .map(|ref mut t| t.update_with_sp_album(&album).clone())
            .collect::<Vec<_>>();
        self.add_tracks(tracks);
        self
    }

    pub fn update_with_am_album(&mut self, album: &apple_music::Album) -> &mut Album {
        self.provider      = Provider::AppleMusic;
        self.identifier    = album.id.to_string();
        self.owner_id      = Some(album.album_artist.to_string());
        self.owner_name    = Some(album.album_artist.to_string());
        self.url           = album.music_url.to_string();
        self.title         = album.title.to_string();
        self.description   = None;
        self.thumbnail_url = Some(album.artwork_url.to_string());
        self.artwork_url   = Some(album.artwork_url.to_string());
        self.state         = State::Alive;
        if let Ok(mut artist) = Artist::find_or_create(self.provider,
                                                       album.album_artist.to_string()) {
            artist.name  = album.album_artist.to_string();
            let _ = artist.save();
            let _ = self.add_artist(artist);
        }
        self
    }

    pub fn disable(&mut self) -> &mut Album {
        self.state = State::Dead;
        self
    }

    pub fn delete(&self) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("DELETE FROM albums WHERE id=$1"));
        let result = stmt.query(&[&self.id]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

#[cfg(test)]
mod test {
    use model::enclosure::Enclosure;
    use model::Model;
    use super::Album;
    use Provider;
    #[test]
    fn test_new() {
        let album = Album::new(Provider::YouTube,
                               "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string());
        assert_eq!(album.provider, Provider::YouTube);
        assert_eq!(&album.identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX")
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string();
        let result     = Album::find_or_create(Provider::YouTube, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_delete() {
        let identifier = "test_delete".to_string();
        let album      = Album::find_or_create(Provider::YouTube, identifier).unwrap();
        let result     = album.delete();
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        album.title   = "title".to_string();
        let result    = album.save();
        assert!(result.is_ok());
        let album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        assert_eq!(&album.title, "title");
    }
}
<commit_msg>Refactor Album struct with serde<commit_after>use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, UTC};

use apple_music;
use spotify;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::state::State;
use model::enclosure::Enclosure;
use model::track::Track;
use model::artist::Artist;

static PROPS: [&'static str; 14]  = ["id",
                                     "provider",
                                     "identifier",
                                     "owner_id",
                                     "owner_name",
                                     "url",
                                     "title",
                                     "description",
                                     "thumbnail_url",
                                     "artwork_url",
                                     "published_at",
                                     "created_at",
                                     "updated_at",
                                     "state"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Album {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub owner_id:      Option<String>,
    pub owner_name:    Option<String>,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub published_at:  NaiveDateTime,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
    pub state:         State,
    pub tracks:        Vec<Track>,
    pub artists:       Option<Vec<Artist>>,
}

impl PartialEq for Album {
    fn eq(&self, p: &Album) -> bool {
        return self.identifier == p.identifier && self.provider == p.provider
    }
}

impl fmt::Display for Album {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Album {
    fn table_name() -> String { "albums".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }

    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Album> {
        let mut albums = Vec::new();
        for row in rows.iter() {
            let album = Album {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                owner_id:      row.get(3),
                owner_name:    row.get(4),
                url:           row.get(5),
                title:         row.get(6),
                description:   row.get(7),
                thumbnail_url: row.get(8),
                artwork_url:   row.get(9),
                published_at:  row.get(10),
                created_at:    row.get(11),
                updated_at:    row.get(12),
                state:         State::new(row.get(13)),
                tracks:        vec![],
                artists:       None,
            };
            albums.push(album)
        }
        albums
    }

    fn create(&self) -> Result<Album, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO albums (provider, identifier, url, title)
                                      VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut album = self.clone();
        for row in rows.iter() {
            album.id = row.get(0);
        }
        Ok(album)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = UTC::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE albums SET
                                      provider      = $2,
                                      identifier    = $3,
                                      owner_id      = $4,
                                      owner_name    = $5,
                                      url           = $6,
                                      title         = $7,
                                      description   = $8,
                                      thumbnail_url = $9,
                                      artwork_url   = $10,
                                      published_at  = $11,
                                      created_at    = $12,
                                      updated_at    = $13,
                                      state         = $14
                                      WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.owner_id,
                                  &self.owner_name,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.published_at,
                                  &self.created_at,
                                  &self.updated_at,
                                  &self.state.to_string(),
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Album {
    fn new(provider: Provider, identifier: String) -> Album {
        Album {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            owner_id:      None,
            owner_name:    None,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            thumbnail_url: None,
            artwork_url:   None,
            published_at:  UTC::now().naive_utc(),
            created_at:    UTC::now().naive_utc(),
            updated_at:    UTC::now().naive_utc(),
            state:         State::Alive,
            tracks:        vec![],
            artists:       None,
        }
    }
    fn set_url(&mut self, url: String) -> &mut Album {
        self.url = url;
        self
    }
    fn set_owner_id(&mut self, owner_id: Option<String>) -> &mut Album {
        self.owner_id = owner_id;
        self
    }
    fn fetch_props(&mut self) -> &mut Album {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_album(&self.identifier, &country) {
                    Ok(album) => self.update_with_am_album(&album),
                    Err(_)    => self.disable(),
                }
            }
            Provider::Spotify => match spotify::fetch_album(&self.identifier) {
                Ok(album) => self.update_with_sp_album(&album),
                Err(_)    => self.disable(),
            },
            _ => self,
        }
    }
    fn find_by_entry_id(entry_id: Uuid) -> Vec<Album> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM albums p LEFT JOIN album_entries pe
                        ON p.id = pe.album_id
                        WHERE pe.entry_id = $1
                        ORDER BY p.published_at DESC",
                     Album::props_str("p."))).unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Album::rows_to_items(rows)
    }
}

impl Album {
    fn add_track(&mut self, track: &Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_tracks (track_id, album_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        Ok(())
    }
    fn add_artist(&mut self, artist: Artist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_artists (track_id, artist_id) VALUES ($1, $2)"));
        try!(stmt.query(&[&self.id, &artist.id]));
        match self.artists {
            Some(ref mut artists) => artists.push(artist),
            None                  => self.artists = Some(vec![artist]),

        }
        Ok(())
    }

    pub fn from_sp_album(album: &spotify::Album) -> Album {
        Album::new(Provider::Spotify, (*album).id.to_string())
            .update_with_sp_album(album)
            .clone()
    }

    pub fn from_am_album(album: &apple_music::Album) -> Album {
        Album::new(Provider::AppleMusic, (*album).id.to_string())
            .update_with_am_album(album)
            .clone()
    }

    fn add_tracks(&mut self, tracks: Vec<Track>) {
        self.tracks = tracks.iter().map(|t| {
            let mut t = t.clone();
            if let Ok(new_track) = Track::find_or_create(t.provider,
                                                         t.identifier.to_string()) {
                t.id      = new_track.id;
                let _     = self.add_track(&t);
            };
            t
        }).collect::<Vec<_>>();
    }

    pub fn update_with_sp_album(&mut self, album: &spotify::Album) -> &mut Album {
        self.provider       = Provider::Spotify;
        self.identifier     = album.id.to_string();
        if album.artists.len() > 0 {
            self.owner_id   = Some(album.artists[0].id.clone());
            self.owner_name = Some(album.artists[0].name.clone());
        }
        self.url            = album.uri.clone();
        self.title          = album.name.clone();
        self.description    = None;
        self.state          = State::Alive;
        self.published_at   = UTC::now().naive_utc();
        if album.images.len() > 0 {
            self.artwork_url   = Some(album.images[0].url.clone());
            self.thumbnail_url = Some(album.images[0].url.clone());
        }
        if album.images.len() > 1 {
            self.thumbnail_url = Some(album.images[1].url.clone());
        }
        let artists = album.artists
            .iter()
            .map(|artist| {
                Artist::find_or_create(self.provider, artist.id.to_string())
                    .map(|mut a| a.update_with_sp_artist(&artist).clone())
            })
            .filter(|r| r.is_ok())
            .map(|r| r.unwrap())
            .collect::<Vec<Artist>>();
        for mut a in artists {
            let _ = a.save();
            let _ = self.add_artist(a);
        }
        let tracks = album.tracks.clone()
            .map(|t| t.items).unwrap_or(vec![]).iter()
            .map(|ref t| Track::from_sp_track(t))
            .map(|ref mut t| t.update_with_sp_album(&album).clone())
            .collect::<Vec<_>>();
        self.add_tracks(tracks);
        self
    }

    pub fn update_with_am_album(&mut self, album: &apple_music::Album) -> &mut Album {
        self.provider      = Provider::AppleMusic;
        self.identifier    = album.id.to_string();
        self.owner_id      = Some(album.album_artist.to_string());
        self.owner_name    = Some(album.album_artist.to_string());
        self.url           = album.music_url.to_string();
        self.title         = album.title.to_string();
        self.description   = None;
        self.thumbnail_url = Some(album.artwork_url.to_string());
        self.artwork_url   = Some(album.artwork_url.to_string());
        self.state         = State::Alive;
        if let Ok(mut artist) = Artist::find_or_create(self.provider,
                                                       album.album_artist.to_string()) {
            artist.name  = album.album_artist.to_string();
            let _ = artist.save();
            let _ = self.add_artist(artist);
        }
        self
    }

    pub fn disable(&mut self) -> &mut Album {
        self.state = State::Dead;
        self
    }

    pub fn delete(&self) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("DELETE FROM albums WHERE id=$1"));
        let result = stmt.query(&[&self.id]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

#[cfg(test)]
mod test {
    use model::enclosure::Enclosure;
    use model::Model;
    use super::Album;
    use Provider;
    #[test]
    fn test_new() {
        let album = Album::new(Provider::YouTube,
                               "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string());
        assert_eq!(album.provider, Provider::YouTube);
        assert_eq!(&album.identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX")
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string();
        let result     = Album::find_or_create(Provider::YouTube, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_delete() {
        let identifier = "test_delete".to_string();
        let album      = Album::find_or_create(Provider::YouTube, identifier).unwrap();
        let result     = album.delete();
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        album.title   = "title".to_string();
        let result    = album.save();
        assert!(result.is_ok());
        let album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        assert_eq!(&album.title, "title");
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::{env, path};

use super::Module;
use crate::{Color, Powerline, Style};

pub struct Cwd<S: CwdScheme> {
    max_length: usize,
    wanted_seg_num: usize,
    resolve_symlinks: bool,
    scheme: PhantomData<S>,
}

pub trait CwdScheme {
    const CWD_FG: Color;
    const PATH_FG: Color;
    const PATH_BG: Color;
    const HOME_FG: Color;
    const HOME_BG: Color;
    const SEPARATOR_FG: Color;
    const CWD_HOME_SYMBOL: &'static str = "~";
}

impl<S: CwdScheme> Cwd<S> {
    pub fn new(max_length: usize, wanted_seg_num: usize, resolve_symlinks: bool) -> Cwd<S> {
        Cwd { max_length, wanted_seg_num, resolve_symlinks, scheme: PhantomData }
    }
}

macro_rules! append_cwd_segments {
    ($powerline:ident, $iter:expr) => {
        for val in $iter {
            $powerline.add_segment(val, Style::special(S::PATH_FG, S::PATH_BG, '\u{E0B1}', S::SEPARATOR_FG));
        }
    };
}

impl<S: CwdScheme> Module for Cwd<S> {
    fn append_segments(&mut self, powerline: &mut Powerline) {
        let current_dir = if self.resolve_symlinks {
            env::current_dir().unwrap()
        } else {
            path::PathBuf::from(env::var("PWD").unwrap())
        };

        let mut cwd = current_dir.to_str().unwrap();

        if cwd == "/" {
            return powerline.add_segment('/', Style::simple(S::PATH_FG, S::PATH_BG));
        }

        if let Some(home_path) = env::home_dir() {
            let home_str = home_path.to_str().unwrap();

            if cwd.starts_with(home_str) {
                powerline.add_segment(S::CWD_HOME_SYMBOL, Style::simple(S::HOME_FG, S::HOME_BG));
                cwd = &cwd[home_str.len()..]
            }
        }

        let depth = cwd.matches('/').count();
        if (cwd.len() > self.max_length as usize) && (depth > self.wanted_seg_num) {
            let left = self.wanted_seg_num / 2;
            let right = self.wanted_seg_num - left;

            let start = cwd.split('/').skip(1).take(left);
            let end = cwd.split('/').skip(depth - right + 1);

            append_cwd_segments!(powerline, start);
            powerline.add_segment('\u{2026}', Style::special(S::PATH_FG, S::PATH_BG, '\u{E0B1}', S::SEPARATOR_FG));
            append_cwd_segments!(powerline, end);
        } else {
            append_cwd_segments!(powerline, cwd.split('/').skip(1));
        };

        if let Some(style) = powerline.last_style_mut() {
            style.sep = '\u{E0B0}';
            style.sep_fg = style.bg.transpose();
        }
    }
}
<commit_msg>use /home/cirho env value for home directory<commit_after>use std::marker::PhantomData;
use std::{env, path};

use super::Module;
use crate::{Color, Powerline, Style};

pub struct Cwd<S: CwdScheme> {
    max_length: usize,
    wanted_seg_num: usize,
    resolve_symlinks: bool,
    scheme: PhantomData<S>,
}

pub trait CwdScheme {
    const CWD_FG: Color;
    const PATH_FG: Color;
    const PATH_BG: Color;
    const HOME_FG: Color;
    const HOME_BG: Color;
    const SEPARATOR_FG: Color;
    const CWD_HOME_SYMBOL: &'static str = "~";
}

impl<S: CwdScheme> Cwd<S> {
    pub fn new(max_length: usize, wanted_seg_num: usize, resolve_symlinks: bool) -> Cwd<S> {
        Cwd { max_length, wanted_seg_num, resolve_symlinks, scheme: PhantomData }
    }
}

macro_rules! append_cwd_segments {
    ($powerline:ident, $iter:expr) => {
        for val in $iter {
            $powerline.add_segment(val, Style::special(S::PATH_FG, S::PATH_BG, '\u{E0B1}', S::SEPARATOR_FG));
        }
    };
}

impl<S: CwdScheme> Module for Cwd<S> {
    fn append_segments(&mut self, powerline: &mut Powerline) {
        let current_dir = if self.resolve_symlinks {
            env::current_dir().unwrap()
        } else {
            path::PathBuf::from(env::var("PWD").unwrap())
        };

        let mut cwd = current_dir.to_str().unwrap();

        if cwd == "/" {
            return powerline.add_segment('/', Style::simple(S::PATH_FG, S::PATH_BG));
        }

        if let Ok(home_str) = env::var("HOME") {
            if cwd.starts_with(&home_str) {
                powerline.add_segment(S::CWD_HOME_SYMBOL, Style::simple(S::HOME_FG, S::HOME_BG));
                cwd = &cwd[home_str.len()..]
            }
        }

        let depth = cwd.matches('/').count();

        if (cwd.len() > self.max_length as usize) && (depth > self.wanted_seg_num) {
            let left = self.wanted_seg_num / 2;
            let right = self.wanted_seg_num - left;

            let start = cwd.split('/').skip(1).take(left);
            let end = cwd.split('/').skip(depth - right + 1);

            append_cwd_segments!(powerline, start);
            powerline.add_segment('\u{2026}', Style::special(S::PATH_FG, S::PATH_BG, '\u{E0B1}', S::SEPARATOR_FG));
            append_cwd_segments!(powerline, end);
        } else {
            append_cwd_segments!(powerline, cwd.split('/').skip(1));
        };

        if let Some(style) = powerline.last_style_mut() {
            style.sep = '\u{E0B0}';
            style.sep_fg = style.bg.transpose();
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2013 - 2021 Tim Düsterhus
// Copyright (C) 2021 Maximilian Mader
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use crate::version::Version;
use once_cell::sync::Lazy;
use regex::{Regex, RegexBuilder};
use std::{convert::TryFrom, fmt::Display};
use unic_langid::LanguageIdentifier;

static LICENSE_REGEX: Lazy<Regex> = Lazy::new(|| {
    RegexBuilder::new(r#"^(.*?)(?:\s<(https?://.*)>)?$"#)
        .case_insensitive(true)
        .build()
        .unwrap()
});

pub trait HasLanguage {
    fn language(&self) -> &Option<LanguageIdentifier>;
}

#[derive(Debug, Default)]
pub struct PackageName {
    pub name: String,
    pub language: Option<LanguageIdentifier>,
}

impl HasLanguage for PackageName {
    fn language(&self) -> &Option<LanguageIdentifier> {
        &self.language
    }
}

#[derive(Debug, Default)]
pub struct PackageDescription {
    pub description: String,
    pub language: Option<LanguageIdentifier>,
}

impl HasLanguage for PackageDescription {
    fn language(&self) -> &Option<LanguageIdentifier> {
        &self.language
    }
}

#[derive(Debug, Default)]
pub struct License {
    pub value: String,
    pub url: Option<url::Url>,
}

impl TryFrom<&str> for License {
    type Error = Box<dyn std::error::Error>;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        let caps = LICENSE_REGEX.captures(s).ok_or("Failed to match license")?;

        let mut value = caps
            .get(1)
            .ok_or("No license text found")?
            .as_str()
            .to_owned();

        let url = caps.get(2).and_then(|url| url.as_str().parse().ok());

        if value.is_empty() && url.is_none() {
            return Err("No license information found".into());
        } else if value.is_empty() {
            value = url.as_ref().map(ToString::to_string).unwrap();
        }

        Ok(License { value, url })
    }
}

#[derive(Debug)]
pub struct Compatibility(u16);

impl TryFrom<&str> for Compatibility {
    type Error = Box<dyn std::error::Error>;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        // See <https://github.com/WoltLab/WCF/blob/5.4.2/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php#L497>
        static REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"^(?:201[7-9]|20[2-9][0-9])$").unwrap());

        if REGEX.is_match(s) {
            return Ok(Self(s.parse().unwrap()));
        }

        Err(format!("Invalid API version: {}", s).into())
    }
}

impl Display for Compatibility {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

#[derive(Debug, Default)]
pub struct PackageInformation {
    pub name: Vec<PackageName>,
    pub description: Vec<PackageDescription>,
    pub url: Option<url::Url>,
    pub is_application: bool,
    pub version: Version,
    pub date: String,
    pub license: Option<License>,
}

#[derive(Debug, Default)]
pub struct AuthorInformation {
    pub author: String,
    pub author_url: Option<String>,
}

#[derive(Debug, Default)]
pub struct RequiredPackage {
    pub identifier: String,
    pub min_version: String,
}

#[derive(Debug, Default)]
pub struct OptionalPackage {
    pub identifier: String,
}

#[derive(Debug, Default)]
pub struct ExcludedPackage {
    pub identifier: String,
    pub version: Option<String>,
}

#[derive(Debug, Default)]
pub struct UpdateInstruction {
    pub from_version: String,
}

#[derive(Debug, Default)]
pub struct PackageXML {
    pub name: String,

    pub package_information: PackageInformation,
    pub author_information: AuthorInformation,
    pub required_packages: Vec<RequiredPackage>,
    pub optional_packages: Vec<OptionalPackage>,
    pub excluded_packages: Vec<ExcludedPackage>,
    pub instructions: Vec<UpdateInstruction>,

    /// Since API version 3.1
    pub compatibility: Vec<Compatibility>,
}
<commit_msg>Detect more URLs in license tags<commit_after>// Copyright (C) 2013 - 2021 Tim Düsterhus
// Copyright (C) 2021 Maximilian Mader
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// SPDX-License-Identifier: AGPL-3.0-or-later

use crate::version::Version;
use once_cell::sync::Lazy;
use regex::{Regex, RegexBuilder};
use std::{convert::TryFrom, fmt::Display};
use unic_langid::LanguageIdentifier;
use url::Url;

static LICENSE_REGEX: Lazy<Regex> = Lazy::new(|| {
    RegexBuilder::new(r#"^(.*?)(?:\s*(?:<(https?://.*)>)|(https?://.*))?$"#)
        .case_insensitive(true)
        .build()
        .unwrap()
});

pub trait HasLanguage {
    fn language(&self) -> &Option<LanguageIdentifier>;
}

#[derive(Debug, Default)]
pub struct PackageName {
    pub name: String,
    pub language: Option<LanguageIdentifier>,
}

impl HasLanguage for PackageName {
    fn language(&self) -> &Option<LanguageIdentifier> {
        &self.language
    }
}

#[derive(Debug, Default)]
pub struct PackageDescription {
    pub description: String,
    pub language: Option<LanguageIdentifier>,
}

impl HasLanguage for PackageDescription {
    fn language(&self) -> &Option<LanguageIdentifier> {
        &self.language
    }
}

#[derive(Debug, Default)]
pub struct License {
    pub value: String,
    pub url: Option<url::Url>,
}

impl TryFrom<&str> for License {
    type Error = Box<dyn std::error::Error>;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        let caps = LICENSE_REGEX.captures(s).ok_or("Failed to match license")?;

        let url = caps
            .get(2)
            .or_else(|| caps.get(3))
            .and_then(|url| url.as_str().parse::<Url>().ok());

        let mut value = caps
            .get(1)
            .map(|m| m.as_str().to_owned())
            .or_else(|| url.as_ref().map(|url| url.to_string()))
            .ok_or("No license text found")?;

        if value.is_empty() && url.is_none() {
            return Err("No license information found".into());
        } else if value.is_empty() {
            value = url.as_ref().map(ToString::to_string).unwrap();
        }

        Ok(License { value, url })
    }
}

#[derive(Debug)]
pub struct Compatibility(u16);

impl TryFrom<&str> for Compatibility {
    type Error = Box<dyn std::error::Error>;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        // See <https://github.com/WoltLab/WCF/blob/5.4.2/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php#L497>
        static REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"^(?:201[7-9]|20[2-9][0-9])$").unwrap());

        if REGEX.is_match(s) {
            return Ok(Self(s.parse().unwrap()));
        }

        Err(format!("Invalid API version: {}", s).into())
    }
}

impl Display for Compatibility {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

#[derive(Debug, Default)]
pub struct PackageInformation {
    pub name: Vec<PackageName>,
    pub description: Vec<PackageDescription>,
    pub url: Option<url::Url>,
    pub is_application: bool,
    pub version: Version,
    pub date: String,
    pub license: Option<License>,
}

#[derive(Debug, Default)]
pub struct AuthorInformation {
    pub author: String,
    pub author_url: Option<String>,
}

#[derive(Debug, Default)]
pub struct RequiredPackage {
    pub identifier: String,
    pub min_version: String,
}

#[derive(Debug, Default)]
pub struct OptionalPackage {
    pub identifier: String,
}

#[derive(Debug, Default)]
pub struct ExcludedPackage {
    pub identifier: String,
    pub version: Option<String>,
}

#[derive(Debug, Default)]
pub struct UpdateInstruction {
    pub from_version: String,
}

#[derive(Debug, Default)]
pub struct PackageXML {
    pub name: String,

    pub package_information: PackageInformation,
    pub author_information: AuthorInformation,
    pub required_packages: Vec<RequiredPackage>,
    pub optional_packages: Vec<OptionalPackage>,
    pub excluded_packages: Vec<ExcludedPackage>,
    pub instructions: Vec<UpdateInstruction>,

    /// Since API version 3.1
    pub compatibility: Vec<Compatibility>,
}
<|endoftext|>