<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/. */

use cookie::Cookie;
use cookie_storage::CookieStorage;
use fetch::methods::should_be_blocked_due_to_bad_port;
use http_loader;
use hyper::header::{Host, SetCookie};
use net_traits::{CookieSource, MessageData, WebSocketCommunicate};
use net_traits::{WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent};
use net_traits::hosts::replace_host_in_url;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use websocket::{Client, Message};
use websocket::header::{Origin, WebSocketProtocol};
use websocket::message::Type;
use websocket::receiver::Receiver;
use websocket::result::{WebSocketError, WebSocketResult};
use websocket::sender::Sender;
use websocket::stream::WebSocketStream;
use websocket::ws::receiver::Receiver as WSReceiver;
use websocket::ws::sender::Sender as Sender_Object;

// https://fetch.spec.whatwg.org/#concept-websocket-establish
fn establish_a_websocket_connection(resource_url: &ServoUrl,
                                    origin: String,
                                    protocols: Vec<String>,
                                    cookie_jar: Arc<RwLock<CookieStorage>>)
                                    -> WebSocketResult<(Option<String>,
                                                        Sender<WebSocketStream>,
                                                        Receiver<WebSocketStream>)> {
    // Steps 1-2 are not really applicable here, given we don't exactly go
    // through the same infrastructure as the Fetch spec.

    if should_be_blocked_due_to_bad_port(resource_url) {
        // Subset of steps 11-12, we inline the bad port check here from the
        // main fetch algorithm for the same reason steps 1-2 are not
        // applicable.
        return Err(WebSocketError::RequestError("Request should be blocked due to bad port."));
    }

    // Steps 3-7.
    let net_url = replace_host_in_url(resource_url.clone());
    let mut request = try!(Client::connect(net_url.as_url()));

    // Client::connect sets the Host header to the host of the URL that is
    // passed to it, so we need to reset it afterwards to the correct one.
    request.headers.set(Host {
        hostname: resource_url.host_str().unwrap().to_owned(),
        port: resource_url.port(),
    });

    // Step 8.
    if !protocols.is_empty() {
        request.headers.set(WebSocketProtocol(protocols.clone()));
    }

    // Steps 9-10.
    // TODO: support for permessage-deflate extension.

    // Subset of step 11.
    // See step 2 of https://fetch.spec.whatwg.org/#concept-fetch.
    request.headers.set(Origin(origin));

    // Transitive subset of step 11.
    // See step 17.1 of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch.
    http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar);

    // Step 11, somewhat.
    let response = try!(request.send());

    // Step 12, 14.
    try!(response.validate());

    // Step 13 and transitive subset of step 14.
    // See step 6 of http://tools.ietf.org/html/rfc6455#section-4.1.
    let protocol_in_use = response.protocol().and_then(|header| {
        // https://github.com/whatwg/fetch/issues/515
        header.first().cloned()
    });
    if let Some(ref protocol_name) = protocol_in_use {
        if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) {
            return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list"));
        };
    };

    // Transitive subset of step 11.
    // See step 15 of https://fetch.spec.whatwg.org/#http-network-fetch.
    if let Some(cookies) = response.headers.get::<SetCookie>() {
        let mut jar = cookie_jar.write().unwrap();
        for cookie in &**cookies {
            if let Some(cookie) = Cookie::new_wrapped(cookie.clone(), resource_url, CookieSource::HTTP) {
                jar.push(cookie, resource_url, CookieSource::HTTP);
            }
        }
    }

    let (sender, receiver) = response.begin().split();
    Ok((protocol_in_use, sender, receiver))
}

pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, cookie_jar: Arc<RwLock<CookieStorage>>) {
    thread::Builder::new().name(format!("WebSocket connection to {}", connect_data.resource_url)).spawn(move || {
        let channel = establish_a_websocket_connection(&connect_data.resource_url,
                                                       connect_data.origin,
                                                       connect_data.protocols,
                                                       cookie_jar);
        let (ws_sender, mut receiver) = match channel {
            Ok((protocol_in_use, sender, receiver)) => {
                let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use });
                (sender, receiver)
            },
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let _ = connect.event_sender.send(WebSocketNetworkEvent::Fail);
                return;
            }

        };

        let initiated_close = Arc::new(AtomicBool::new(false));
        let ws_sender = Arc::new(Mutex::new(ws_sender));

        let initiated_close_incoming = initiated_close.clone();
        let ws_sender_incoming = ws_sender.clone();
        let resource_event_sender = connect.event_sender;
        thread::spawn(move || {
            for message in receiver.incoming_messages() {
                let message: Message = match message {
                    Ok(m) => m,
                    Err(e) => {
                        debug!("Error receiving incoming WebSocket message: {:?}", e);
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
                        break;
                    }
                };
                let message = match message.opcode {
                    Type::Text => MessageData::Text(String::from_utf8_lossy(&message.payload).into_owned()),
                    Type::Binary => MessageData::Binary(message.payload.into_owned()),
                    Type::Ping => {
                        let pong = Message::pong(message.payload);
                        ws_sender_incoming.lock().unwrap().send_message(&pong).unwrap();
                        continue;
                    },
                    Type::Pong => continue,
                    Type::Close => {
                        if !initiated_close_incoming.fetch_or(true, Ordering::SeqCst) {
                            ws_sender_incoming.lock().unwrap().send_message(&message).unwrap();
                        }
                        let code = message.cd_status_code;
                        let reason = String::from_utf8_lossy(&message.payload).into_owned();
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Close(code, reason));
                        break;
                    },
                };
                let _ = resource_event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
            }
        });

        while let Ok(dom_action) = connect.action_receiver.recv() {
            match dom_action {
                WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::text(data)).unwrap();
                },
                WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::binary(data)).unwrap();
                },
                WebSocketDomAction::Close(code, reason) => {
                    if !initiated_close.fetch_or(true, Ordering::SeqCst) {
                        let message = match code {
                            Some(code) => Message::close_because(code, reason.unwrap_or("".to_owned())),
                            None => Message::close()
                        };
                        ws_sender.lock().unwrap().send_message(&message).unwrap();
                    }
                },
            }
        }
    }).expect("Thread spawning failed");
}
<commit_msg>Put websocket_loader::init first in its module<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/. */

use cookie::Cookie;
use cookie_storage::CookieStorage;
use fetch::methods::should_be_blocked_due_to_bad_port;
use http_loader;
use hyper::header::{Host, SetCookie};
use net_traits::{CookieSource, MessageData, WebSocketCommunicate};
use net_traits::{WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent};
use net_traits::hosts::replace_host_in_url;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::{Arc, Mutex, RwLock};
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use websocket::{Client, Message};
use websocket::header::{Origin, WebSocketProtocol};
use websocket::message::Type;
use websocket::receiver::Receiver;
use websocket::result::{WebSocketError, WebSocketResult};
use websocket::sender::Sender;
use websocket::stream::WebSocketStream;
use websocket::ws::receiver::Receiver as WSReceiver;
use websocket::ws::sender::Sender as Sender_Object;

pub fn init(connect: WebSocketCommunicate, connect_data: WebSocketConnectData, cookie_jar: Arc<RwLock<CookieStorage>>) {
    thread::Builder::new().name(format!("WebSocket connection to {}", connect_data.resource_url)).spawn(move || {
        let channel = establish_a_websocket_connection(&connect_data.resource_url,
                                                       connect_data.origin,
                                                       connect_data.protocols,
                                                       cookie_jar);
        let (ws_sender, mut receiver) = match channel {
            Ok((protocol_in_use, sender, receiver)) => {
                let _ = connect.event_sender.send(WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use });
                (sender, receiver)
            },
            Err(e) => {
                debug!("Failed to establish a WebSocket connection: {:?}", e);
                let _ = connect.event_sender.send(WebSocketNetworkEvent::Fail);
                return;
            }

        };

        let initiated_close = Arc::new(AtomicBool::new(false));
        let ws_sender = Arc::new(Mutex::new(ws_sender));

        let initiated_close_incoming = initiated_close.clone();
        let ws_sender_incoming = ws_sender.clone();
        let resource_event_sender = connect.event_sender;
        thread::spawn(move || {
            for message in receiver.incoming_messages() {
                let message: Message = match message {
                    Ok(m) => m,
                    Err(e) => {
                        debug!("Error receiving incoming WebSocket message: {:?}", e);
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Fail);
                        break;
                    }
                };
                let message = match message.opcode {
                    Type::Text => MessageData::Text(String::from_utf8_lossy(&message.payload).into_owned()),
                    Type::Binary => MessageData::Binary(message.payload.into_owned()),
                    Type::Ping => {
                        let pong = Message::pong(message.payload);
                        ws_sender_incoming.lock().unwrap().send_message(&pong).unwrap();
                        continue;
                    },
                    Type::Pong => continue,
                    Type::Close => {
                        if !initiated_close_incoming.fetch_or(true, Ordering::SeqCst) {
                            ws_sender_incoming.lock().unwrap().send_message(&message).unwrap();
                        }
                        let code = message.cd_status_code;
                        let reason = String::from_utf8_lossy(&message.payload).into_owned();
                        let _ = resource_event_sender.send(WebSocketNetworkEvent::Close(code, reason));
                        break;
                    },
                };
                let _ = resource_event_sender.send(WebSocketNetworkEvent::MessageReceived(message));
            }
        });

        while let Ok(dom_action) = connect.action_receiver.recv() {
            match dom_action {
                WebSocketDomAction::SendMessage(MessageData::Text(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::text(data)).unwrap();
                },
                WebSocketDomAction::SendMessage(MessageData::Binary(data)) => {
                    ws_sender.lock().unwrap().send_message(&Message::binary(data)).unwrap();
                },
                WebSocketDomAction::Close(code, reason) => {
                    if !initiated_close.fetch_or(true, Ordering::SeqCst) {
                        let message = match code {
                            Some(code) => Message::close_because(code, reason.unwrap_or("".to_owned())),
                            None => Message::close()
                        };
                        ws_sender.lock().unwrap().send_message(&message).unwrap();
                    }
                },
            }
        }
    }).expect("Thread spawning failed");
}

// https://fetch.spec.whatwg.org/#concept-websocket-establish
fn establish_a_websocket_connection(resource_url: &ServoUrl,
                                    origin: String,
                                    protocols: Vec<String>,
                                    cookie_jar: Arc<RwLock<CookieStorage>>)
                                    -> WebSocketResult<(Option<String>,
                                                        Sender<WebSocketStream>,
                                                        Receiver<WebSocketStream>)> {
    // Steps 1-2 are not really applicable here, given we don't exactly go
    // through the same infrastructure as the Fetch spec.

    if should_be_blocked_due_to_bad_port(resource_url) {
        // Subset of steps 11-12, we inline the bad port check here from the
        // main fetch algorithm for the same reason steps 1-2 are not
        // applicable.
        return Err(WebSocketError::RequestError("Request should be blocked due to bad port."));
    }

    // Steps 3-7.
    let net_url = replace_host_in_url(resource_url.clone());
    let mut request = try!(Client::connect(net_url.as_url()));

    // Client::connect sets the Host header to the host of the URL that is
    // passed to it, so we need to reset it afterwards to the correct one.
    request.headers.set(Host {
        hostname: resource_url.host_str().unwrap().to_owned(),
        port: resource_url.port(),
    });

    // Step 8.
    if !protocols.is_empty() {
        request.headers.set(WebSocketProtocol(protocols.clone()));
    }

    // Steps 9-10.
    // TODO: support for permessage-deflate extension.

    // Subset of step 11.
    // See step 2 of https://fetch.spec.whatwg.org/#concept-fetch.
    request.headers.set(Origin(origin));

    // Transitive subset of step 11.
    // See step 17.1 of https://fetch.spec.whatwg.org/#concept-http-network-or-cache-fetch.
    http_loader::set_request_cookies(&resource_url, &mut request.headers, &cookie_jar);

    // Step 11, somewhat.
    let response = try!(request.send());

    // Step 12, 14.
    try!(response.validate());

    // Step 13 and transitive subset of step 14.
    // See step 6 of http://tools.ietf.org/html/rfc6455#section-4.1.
    let protocol_in_use = response.protocol().and_then(|header| {
        // https://github.com/whatwg/fetch/issues/515
        header.first().cloned()
    });
    if let Some(ref protocol_name) = protocol_in_use {
        if !protocols.is_empty() && !protocols.iter().any(|p| (&**p).eq_ignore_ascii_case(protocol_name)) {
            return Err(WebSocketError::ProtocolError("Protocol in Use not in client-supplied protocol list"));
        };
    };

    // Transitive subset of step 11.
    // See step 15 of https://fetch.spec.whatwg.org/#http-network-fetch.
    if let Some(cookies) = response.headers.get::<SetCookie>() {
        let mut jar = cookie_jar.write().unwrap();
        for cookie in &**cookies {
            if let Some(cookie) = Cookie::new_wrapped(cookie.clone(), resource_url, CookieSource::HTTP) {
                jar.push(cookie, resource_url, CookieSource::HTTP);
            }
        }
    }

    let (sender, receiver) = response.begin().split();
    Ok((protocol_in_use, sender, receiver))
}
<|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/. */

use cg;
use quote;
use syn;

pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
    let name = &input.ident;
    let trait_path = &["values", "animated", "Animate"];
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, trait_path);

    let variants = cg::variants(&input);
    let mut match_body = quote!();
    let mut append_error_clause = variants.len() > 1;
    match_body.append_all(variants.iter().flat_map(|variant| {
        let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
        if attrs.error {
            append_error_clause = true;
            return None;
        }
        let name = cg::variant_ctor(&input, variant);
        let (this_pattern, this_info) = cg::ref_pattern(&name, variant, "this");
        let (other_pattern, other_info) = cg::ref_pattern(&name, variant, "other");
        let (result_value, result_info) = cg::value(&name, variant, "result");
        let mut computations = quote!();
        let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
        computations.append_all(iter.map(|(result, (this, other))| {
            where_clause.add_trait_bound(this.field.ty.clone());
            quote! {
                let #result = ::values::animated::Animate::animate(#this, #other, procedure)?;
            }
        }));
        Some(quote! {
            (&#this_pattern, &#other_pattern) => {
                #computations
                Ok(#result_value)
            }
        })
    }));

    if append_error_clause {
        match_body = quote! { #match_body, _ => Err(()), };
    }

    quote! {
        impl #impl_generics ::values::animated::Animate for #name #ty_generics #where_clause {
            #[allow(unused_variables, unused_imports)]
            #[inline]
            fn animate(
                &self,
                other: &Self,
                procedure: ::values::animated::Procedure,
            ) -> Result<Self, ()> {
                match (self, other) {
                    #match_body
                }
            }
        }
    }
}

#[derive(Default, FromVariant)]
#[darling(attributes(animate), default)]
pub struct AnimateAttrs {
    pub error: bool,
}
<commit_msg>Fix the name of the #[animation] attribute, oops<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/. */

use cg;
use quote;
use syn;

pub fn derive(input: syn::DeriveInput) -> quote::Tokens {
    let name = &input.ident;
    let trait_path = &["values", "animated", "Animate"];
    let (impl_generics, ty_generics, mut where_clause) =
        cg::trait_parts(&input, trait_path);

    let variants = cg::variants(&input);
    let mut match_body = quote!();
    let mut append_error_clause = variants.len() > 1;
    match_body.append_all(variants.iter().flat_map(|variant| {
        let attrs = cg::parse_variant_attrs::<AnimateAttrs>(variant);
        if attrs.error {
            append_error_clause = true;
            return None;
        }
        let name = cg::variant_ctor(&input, variant);
        let (this_pattern, this_info) = cg::ref_pattern(&name, variant, "this");
        let (other_pattern, other_info) = cg::ref_pattern(&name, variant, "other");
        let (result_value, result_info) = cg::value(&name, variant, "result");
        let mut computations = quote!();
        let iter = result_info.iter().zip(this_info.iter().zip(&other_info));
        computations.append_all(iter.map(|(result, (this, other))| {
            where_clause.add_trait_bound(this.field.ty.clone());
            quote! {
                let #result = ::values::animated::Animate::animate(#this, #other, procedure)?;
            }
        }));
        Some(quote! {
            (&#this_pattern, &#other_pattern) => {
                #computations
                Ok(#result_value)
            }
        })
    }));

    if append_error_clause {
        match_body = quote! { #match_body, _ => Err(()), };
    }

    quote! {
        impl #impl_generics ::values::animated::Animate for #name #ty_generics #where_clause {
            #[allow(unused_variables, unused_imports)]
            #[inline]
            fn animate(
                &self,
                other: &Self,
                procedure: ::values::animated::Procedure,
            ) -> Result<Self, ()> {
                match (self, other) {
                    #match_body
                }
            }
        }
    }
}

#[derive(Default, FromVariant)]
#[darling(attributes(animation), default)]
pub struct AnimateAttrs {
    pub error: bool,
}
<|endoftext|>


<commit_before>//! Provides functionality for building up a model of the configuration files used by git, as well
//! as editing them.

use std::{env, fmt, io, str};
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File;
use std::iter::Peekable;
use std::io::Read;
use std::str::Chars;

#[derive(Debug)]
pub enum Error {
    IOError(io::Error),
    InvalidFile(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::IOError(ref err) => write!(f, "IO error: {}", err),
            Error::InvalidFile(ref description) => write!(f, "invalid file: {}", description),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IOError(ref err) => err.description(),
            Error::InvalidFile(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::IOError(ref err) => Some(err),
            ref err => Some(err)
        }
    }
}

struct Parser<'a> {
    map: HashMap<String, String>,
    chars: Peekable<Chars<'a>>,
    current_section_names: Vec<String>,
}

impl <'a> Parser<'a> {
    fn new(contents: &'a String) -> Parser<'a> {
        Parser {
            map: HashMap::new(),
            chars: contents.chars().peekable(),
            current_section_names: Vec::new(),
        }
    }

    fn parse(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.peek() {
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'#') => try!(self.parse_comment()),
                Some(&'[') => try!(self.parse_section()),
                Some(_) => { self.chars.next(); },
                None => { self.chars.next(); break; },
            }
        }

        Ok(())
    }

    fn parse_comment(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.next() {
                Some('\n') | None => break,
                _ => continue,
            }
        }

        Ok(())
    }

    fn parse_section(&mut self) -> Result<(), Error> {
        let mut section_name = String::new();

        self.chars.next();
        loop {
            match self.chars.peek() {
                Some(&']') | None => {
                    self.chars.next();
                    self.current_section_names.push(section_name);
                    try!(self.parse_variables());
                    self.current_section_names.pop();
                    break;
                },
                Some(&chr) => {
                    section_name.push(chr);
                    self.chars.next();
                }
            }
        }

        Ok(())
    }

    fn parse_variables(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.peek() {
                Some(&'#') => try!(self.parse_comment()),
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'[') | None => break,
                _ => try!(self.parse_single_variable()),
            }
        }

        Ok(())
    }

    fn parse_single_variable(&mut self) -> Result<(), Error> {
        let mut key_name = String::new();
        loop {
            match self.chars.peek() {
                Some(&'#') => { try!(self.parse_comment()); break; },
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'\n') => { self.chars.next(); break; },
                Some(&'=') => {
                    self.chars.next();
                    try!(self.parse_key_value_pair(key_name.clone()));
                    key_name.clear();
                    break;
                }
                Some(&'[') | None => break,
                Some(&chr) => {
                    self.chars.next();
                    key_name.push(chr);
                },
            }
        }

        if !key_name.is_empty() {
            let full_key_name = self.variable_name_for_current_section(&key_name);
            self.map.insert(full_key_name, "true".to_string());
        }
        Ok(())
    }

    fn parse_key_value_pair(&mut self, key_name: String) -> Result<(), Error> {
        let mut value = String::new();

        loop {
            match self.chars.peek() {
                Some(&'#') => { try!(self.parse_comment()); break; },
                Some(&'\n') => { self.chars.next(); break; },
                Some(&'[') | None => break,
                Some(&chr) => {
                    self.chars.next();
                    value.push(chr);
                },
            }
        }

        let full_key_name = self.variable_name_for_current_section(&key_name);
        self.map.insert(full_key_name, value.trim().to_string());
        Ok(())
    }

    fn variable_name_for_current_section(&self, key_name: &String) -> String {
        let mut name = String::new();
        for section in self.current_section_names.iter() {
            name.push_str(&section);
            name.push('.');
        }

        name.push_str(key_name);
        name
    }
}

/// The fundamental data structure representing the configuration for this process. Instead of
/// having specific fields for each configuration item, this structure exposes a map-like interface
/// indexed by strings.
pub struct Config {
    map: HashMap<String, String>,
}

impl Config {
    fn new() -> Config {
        Config { map: HashMap::new() }
    }

    fn add_from_file(&mut self, filename: String) -> Result<&Config, Error> {
        println!("reading from {}", filename);
        let mut file = try!(File::open(filename).map_err(|e| Error::IOError(e)));
        let mut contents = String::new();
        try!(file.read_to_string(&mut contents).map_err(|e| Error::IOError(e)));
        self.add_from_string(contents.to_string())
    }

    fn add_from_string(&mut self, contents: String) -> Result<&Config, Error> {
        let mut parser = Parser::new(&contents);
        try!(parser.parse());

        for (k, v) in parser.map.drain() {
            self.map.insert(k.to_string(), v.to_string());
        }

        Ok(self)
    }

    pub fn all(&self) -> Vec<(String, String)> {
        let mut list: Vec<(String, String)> = self.map
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();

        list.sort();
        list
    }
}

pub fn read_all() -> Result<Config, Error> {
    let mut config = Config::new();

    let home_gitconfig = env::home_dir()
        .and_then(|mut path| {
            path.push(".gitconfig");
            path.to_str().map(|s| s.to_string())
        });
    match home_gitconfig {
        Some(path) => { try!(config.add_from_file(path)); },
        None => {}
    }

    let repo_gitconfig = env::current_dir()
        .ok()
        .and_then(|mut path| {
            path.push(".git/config");
            path.to_str().map(|s| s.to_string())
        });
    match repo_gitconfig {
        Some(path) => { try!(config.add_from_file(path)); },
        None => {}
    }

    Ok(config)
}

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

    #[test]
    fn parses_config_syntax() {
        let contents = r"
# This is a comment
[simple]
key0 = val0
key1=val1
key2 = val2 with spaces

[Complicated-123] key3 = val3
key4
key-5 # here's a comment
key6 = val6 # and another comment

# TODO: multiline values
# TODO: subsections
";

        let mut config = Config::new();
        config.add_from_string(contents.to_string()).unwrap();
        assert_eq!(config.all(), vec![
                   ("Complicated-123.key-5", "true"),
                   ("Complicated-123.key3", "val3"),
                   ("Complicated-123.key4", "true"),
                   ("Complicated-123.key6", "val6"),
                   ("simple.key0", "val0"),
                   ("simple.key1", "val1"),
                   ("simple.key2", "val2 with spaces"),
                   ]
                   .iter()
                   .map(|s| (s.0.to_string(), s.1.to_string()))
                   .collect::<Vec<_> >());
    }
}
<commit_msg>Support subsections in config<commit_after>//! Provides functionality for building up a model of the configuration files used by git, as well
//! as editing them.

use std::{env, fmt, io, str};
use std::collections::HashMap;
use std::error::Error as StdError;
use std::fs::File;
use std::iter::Peekable;
use std::io::Read;
use std::str::Chars;

#[derive(Debug)]
pub enum Error {
    IOError(io::Error),
    InvalidFile(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::IOError(ref err) => write!(f, "IO error: {}", err),
            Error::InvalidFile(ref description) => write!(f, "invalid file: {}", description),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IOError(ref err) => err.description(),
            Error::InvalidFile(ref description) => description,
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::IOError(ref err) => Some(err),
            ref err => Some(err)
        }
    }
}

struct Parser<'a> {
    map: HashMap<String, String>,
    chars: Peekable<Chars<'a>>,
    current_section_names: Vec<String>,
}

impl <'a> Parser<'a> {
    fn new(contents: &'a String) -> Parser<'a> {
        Parser {
            map: HashMap::new(),
            chars: contents.chars().peekable(),
            current_section_names: Vec::new(),
        }
    }

    fn parse(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.peek() {
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'#') => try!(self.parse_comment()),
                Some(&'[') => try!(self.parse_section()),
                Some(_) => { self.chars.next(); },
                None => { self.chars.next(); break; },
            }
        }

        Ok(())
    }

    fn parse_comment(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.next() {
                Some('\n') | None => break,
                _ => continue,
            }
        }

        Ok(())
    }

    fn parse_section(&mut self) -> Result<(), Error> {
        let mut section_name = String::new();
        let mut subsection_name = String::new();
        let mut is_reading_subsection = false;

        self.chars.next();
        loop {
            match self.chars.peek() {
                Some(&chr @ ' ') | Some(&chr @ '\t') => {
                    self.chars.next();

                    if is_reading_subsection {
                        subsection_name.push(chr);
                    }
                },
                Some(&'"') => {
                    self.chars.next();

                    // We're deliberately ignoring escaped quotes for now.
                    is_reading_subsection = !is_reading_subsection;
                },
                Some(&']') | None => {
                    self.chars.next();

                    self.current_section_names.push(section_name.trim().to_string());
                    if !subsection_name.is_empty() {
                        self.current_section_names.push(subsection_name.clone());
                    }

                    try!(self.parse_variables());

                    if !subsection_name.is_empty() {
                        self.current_section_names.pop();
                    }
                    self.current_section_names.pop();
                    break;
                },
                Some(&chr) => {
                    if is_reading_subsection {
                        subsection_name.push(chr);
                    } else {
                        section_name.push(chr);
                    }

                    self.chars.next();
                }
            }
        }

        Ok(())
    }

    fn parse_variables(&mut self) -> Result<(), Error> {
        loop {
            match self.chars.peek() {
                Some(&'#') => try!(self.parse_comment()),
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'[') | None => break,
                _ => try!(self.parse_single_variable()),
            }
        }

        Ok(())
    }

    fn parse_single_variable(&mut self) -> Result<(), Error> {
        let mut key_name = String::new();
        loop {
            match self.chars.peek() {
                Some(&'#') => { try!(self.parse_comment()); break; },
                Some(&' ') | Some(&'\t') => { self.chars.next(); },
                Some(&'\n') => { self.chars.next(); break; },
                Some(&'=') => {
                    self.chars.next();
                    try!(self.parse_key_value_pair(key_name.clone()));
                    key_name.clear();
                    break;
                }
                Some(&'[') | None => break,
                Some(&chr) => {
                    self.chars.next();
                    key_name.push(chr);
                },
            }
        }

        if !key_name.is_empty() {
            let full_key_name = self.variable_name_for_current_section(&key_name);
            self.map.insert(full_key_name, "true".to_string());
        }
        Ok(())
    }

    fn parse_key_value_pair(&mut self, key_name: String) -> Result<(), Error> {
        let mut value = String::new();

        loop {
            match self.chars.peek() {
                Some(&'#') => { try!(self.parse_comment()); break; },
                Some(&'\n') => { self.chars.next(); break; },
                Some(&'[') | None => break,
                Some(&chr) => {
                    self.chars.next();
                    value.push(chr);
                },
            }
        }

        let full_key_name = self.variable_name_for_current_section(&key_name);
        self.map.insert(full_key_name, value.trim().to_string());
        Ok(())
    }

    fn variable_name_for_current_section(&self, key_name: &String) -> String {
        let mut name = String::new();
        for section in self.current_section_names.iter() {
            name.push_str(&section);
            name.push('.');
        }

        name.push_str(key_name);
        name
    }
}

/// The fundamental data structure representing the configuration for this process. Instead of
/// having specific fields for each configuration item, this structure exposes a map-like interface
/// indexed by strings.
pub struct Config {
    map: HashMap<String, String>,
}

impl Config {
    fn new() -> Config {
        Config { map: HashMap::new() }
    }

    fn add_from_file(&mut self, filename: String) -> Result<&Config, Error> {
        println!("reading from {}", filename);
        let mut file = try!(File::open(filename).map_err(|e| Error::IOError(e)));
        let mut contents = String::new();
        try!(file.read_to_string(&mut contents).map_err(|e| Error::IOError(e)));
        self.add_from_string(contents.to_string())
    }

    fn add_from_string(&mut self, contents: String) -> Result<&Config, Error> {
        let mut parser = Parser::new(&contents);
        try!(parser.parse());

        for (k, v) in parser.map.drain() {
            self.map.insert(k.to_string(), v.to_string());
        }

        Ok(self)
    }

    pub fn all(&self) -> Vec<(String, String)> {
        let mut list: Vec<(String, String)> = self.map
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();

        list.sort();
        list
    }
}

pub fn read_all() -> Result<Config, Error> {
    let mut config = Config::new();

    let home_gitconfig = env::home_dir()
        .and_then(|mut path| {
            path.push(".gitconfig");
            path.to_str().map(|s| s.to_string())
        });
    match home_gitconfig {
        Some(path) => { try!(config.add_from_file(path)); },
        None => {}
    }

    let repo_gitconfig = env::current_dir()
        .ok()
        .and_then(|mut path| {
            path.push(".git/config");
            path.to_str().map(|s| s.to_string())
        });
    match repo_gitconfig {
        Some(path) => { try!(config.add_from_file(path)); },
        None => {}
    }

    Ok(config)
}

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

    #[test]
    fn parses_config_syntax() {
        let contents = r#"
# This is a comment
[simple]
key0 = val0
key1=val1
key2 = val2 with spaces

[Complicated-123] key3 = val3
key4
key-5 # here's a comment
key6 = val6 # and another comment

[simple "subsection (with spaces)"]
key7 = val7

# TODO: multiline values
"#;

        let mut config = Config::new();
        config.add_from_string(contents.to_string()).unwrap();
        assert_eq!(config.all(), vec![
                   ("Complicated-123.key-5", "true"),
                   ("Complicated-123.key3", "val3"),
                   ("Complicated-123.key4", "true"),
                   ("Complicated-123.key6", "val6"),
                   ("simple.key0", "val0"),
                   ("simple.key1", "val1"),
                   ("simple.key2", "val2 with spaces"),
                   ("simple.subsection (with spaces).key7", "val7"),
                   ]
                   .iter()
                   .map(|s| (s.0.to_string(), s.1.to_string()))
                   .collect::<Vec<_> >());
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io::{Error as IoError, Read, Write};
use std::path::PathBuf;

use ansi_term::{Color, Style};
use toml;
use xdg::BaseDirectories;

use error::TealdeerError::{self, ConfigError};

pub const SYNTAX_CONFIG_FILE_NAME: &'static str = "config.toml";

#[serde(rename_all = "lowercase")]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Purple,
    Cyan,
    White,
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Color {
        match raw_color {
            RawColor::Black => Color::Black,
            RawColor::Red => Color::Red,
            RawColor::Green => Color::Green,
            RawColor::Yellow => Color::Yellow,
            RawColor::Blue => Color::Blue,
            RawColor::Purple => Color::Purple,
            RawColor::Cyan => Color::Cyan,
            RawColor::White => Color::White,
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    pub underline: bool,
    pub bold: bool,
}

impl Default for RawStyle {
    fn default() -> RawStyle {
        RawStyle{
            foreground: None,
            background: None,
            underline: false,
            bold: false,
        }
    }
} // impl RawStyle

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Style {
        let mut style = Style::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.on(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    pub description: RawStyle,
    pub command_name: RawStyle,
    pub example_text: RawStyle,
    pub example_code: RawStyle,
    pub example_variable: RawStyle,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawConfig {
    style: RawStyleConfig,
}

impl RawConfig {
    fn new() -> RawConfig {
        let mut raw_config = RawConfig::default();

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;

        raw_config
    }
} // impl RawConfig

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Config {
    pub style: StyleConfig,
}

impl From<RawConfig> for Config {
    fn from(raw_config: RawConfig) -> Config {
        Config{
            style: StyleConfig{
                command_name: raw_config.style.command_name.into(),
                description: raw_config.style.description.into(),
                example_text: raw_config.style.example_text.into(),
                example_code: raw_config.style.example_code.into(),
                example_variable: raw_config.style.example_variable.into(),
            }
        }
    }
}

fn map_io_err_to_config_err(e: IoError) -> TealdeerError {
    ConfigError(format!("Io Error: {}", e))
}

impl Config {
    pub fn load() -> Result<Config, TealdeerError> {
        let raw_config = match get_syntax_config_path() {
            Ok(syntax_config_file_path) => {
                let mut syntax_config_file = fs::File::open(syntax_config_file_path)
                    .map_err(map_io_err_to_config_err)?;
                let mut contents = String::new();
                let _rc = syntax_config_file.read_to_string(&mut contents)
                    .map_err(map_io_err_to_config_err)?;

                toml::from_str(&contents).map_err(|err| ConfigError(format!("Failed to parse syntax config file: {}", err)))?
            }
            Err(ConfigError(_)) => RawConfig::new(),
            Err(_) => {
                return Err(ConfigError("Unknown error while looking up syntax config path".into()));
            }
        };

        Ok(Config::from(raw_config))
    }
} // impl Config

/// Return the path to the config directory.
pub fn get_config_dir() -> Result<PathBuf, TealdeerError> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        let path = PathBuf::from(value);

        if path.exists() && path.is_dir() {
            return Ok(path)
        } else {
            return Err(ConfigError(
                "Path specified by $TEALDEER_CONFIG_DIR \
                 does not exist or is not a directory.".into()
            ));
        }
    };

    // Otherwise, fall back to $XDG_CONFIG_HOME/tealdeer.
    let xdg_dirs = match BaseDirectories::with_prefix(::NAME) {
        Ok(dirs) => dirs,
        Err(_) => return Err(ConfigError("Could not determine XDG base directory.".into())),
    };
    Ok(xdg_dirs.get_config_home())
}

/// Return the path to the syntax config file.
pub fn get_syntax_config_path() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    let syntax_config_file_path = config_dir.join(SYNTAX_CONFIG_FILE_NAME);

    if syntax_config_file_path.is_file() {
        Ok(syntax_config_file_path)
    } else {
        Err(ConfigError(format!("{} is not a file path", syntax_config_file_path.to_str().unwrap())))
    }
}

/// Create default syntax config file.
pub fn make_default_syntax_config() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    if !config_dir.is_dir() {
        if let Err(e) = fs::create_dir_all(&config_dir) {
            return Err(ConfigError(format!("Could not create config directory: {}", e)));
        }
    }

    let syntax_config_file_path = config_dir.join(SYNTAX_CONFIG_FILE_NAME);
    if syntax_config_file_path.is_file() {
        return Err(ConfigError(format!(
            "A configuration file already exists at {}, no action was taken.",
            syntax_config_file_path.to_str().unwrap()
        )));
    }

    let serialized_syntax_config = toml::to_string(&RawConfig::new())
        .map_err(|err| ConfigError(format!("Failed to serialize default syntax config: {}", err)))?;

    let mut syntax_config_file = fs::File::create(&syntax_config_file_path)
        .map_err(map_io_err_to_config_err)?;
    let _wc = syntax_config_file.write(serialized_syntax_config.as_bytes())
        .map_err(map_io_err_to_config_err)?;

    Ok(syntax_config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<commit_msg>Define defaults for config fields<commit_after>use std::env;
use std::fs;
use std::io::{Error as IoError, Read, Write};
use std::path::PathBuf;

use ansi_term::{Color, Style};
use toml;
use xdg::BaseDirectories;

use error::TealdeerError::{self, ConfigError};

pub const SYNTAX_CONFIG_FILE_NAME: &'static str = "config.toml";

fn default_underline() -> bool {
    false
}

fn default_bold() -> bool {
    false
}

#[serde(rename_all = "lowercase")]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Purple,
    Cyan,
    White,
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Color {
        match raw_color {
            RawColor::Black => Color::Black,
            RawColor::Red => Color::Red,
            RawColor::Green => Color::Green,
            RawColor::Yellow => Color::Yellow,
            RawColor::Blue => Color::Blue,
            RawColor::Purple => Color::Purple,
            RawColor::Cyan => Color::Cyan,
            RawColor::White => Color::White,
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    #[serde(default = "default_underline")]
    pub underline: bool,
    #[serde(default = "default_bold")]
    pub bold: bool,
}

impl Default for RawStyle {
    fn default() -> RawStyle {
        RawStyle{
            foreground: None,
            background: None,
            underline: false,
            bold: false,
        }
    }
} // impl RawStyle

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Style {
        let mut style = Style::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.on(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    #[serde(default)]
    pub description: RawStyle,
    #[serde(default)]
    pub command_name: RawStyle,
    #[serde(default)]
    pub example_text: RawStyle,
    #[serde(default)]
    pub example_code: RawStyle,
    #[serde(default)]
    pub example_variable: RawStyle,
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawConfig {
    style: RawStyleConfig,
}

impl RawConfig {
    fn new() -> RawConfig {
        let mut raw_config = RawConfig::default();

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;

        raw_config
    }
} // impl RawConfig

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Config {
    pub style: StyleConfig,
}

impl From<RawConfig> for Config {
    fn from(raw_config: RawConfig) -> Config {
        Config{
            style: StyleConfig{
                command_name: raw_config.style.command_name.into(),
                description: raw_config.style.description.into(),
                example_text: raw_config.style.example_text.into(),
                example_code: raw_config.style.example_code.into(),
                example_variable: raw_config.style.example_variable.into(),
            }
        }
    }
}

fn map_io_err_to_config_err(e: IoError) -> TealdeerError {
    ConfigError(format!("Io Error: {}", e))
}

impl Config {
    pub fn load() -> Result<Config, TealdeerError> {
        let raw_config = match get_syntax_config_path() {
            Ok(syntax_config_file_path) => {
                let mut syntax_config_file = fs::File::open(syntax_config_file_path)
                    .map_err(map_io_err_to_config_err)?;
                let mut contents = String::new();
                let _rc = syntax_config_file.read_to_string(&mut contents)
                    .map_err(map_io_err_to_config_err)?;

                toml::from_str(&contents).map_err(|err| ConfigError(format!("Failed to parse syntax config file: {}", err)))?
            }
            Err(ConfigError(_)) => RawConfig::new(),
            Err(_) => {
                return Err(ConfigError("Unknown error while looking up syntax config path".into()));
            }
        };

        Ok(Config::from(raw_config))
    }
} // impl Config

/// Return the path to the config directory.
pub fn get_config_dir() -> Result<PathBuf, TealdeerError> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        let path = PathBuf::from(value);

        if path.exists() && path.is_dir() {
            return Ok(path)
        } else {
            return Err(ConfigError(
                "Path specified by $TEALDEER_CONFIG_DIR \
                 does not exist or is not a directory.".into()
            ));
        }
    };

    // Otherwise, fall back to $XDG_CONFIG_HOME/tealdeer.
    let xdg_dirs = match BaseDirectories::with_prefix(::NAME) {
        Ok(dirs) => dirs,
        Err(_) => return Err(ConfigError("Could not determine XDG base directory.".into())),
    };
    Ok(xdg_dirs.get_config_home())
}

/// Return the path to the syntax config file.
pub fn get_syntax_config_path() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    let syntax_config_file_path = config_dir.join(SYNTAX_CONFIG_FILE_NAME);

    if syntax_config_file_path.is_file() {
        Ok(syntax_config_file_path)
    } else {
        Err(ConfigError(format!("{} is not a file path", syntax_config_file_path.to_str().unwrap())))
    }
}

/// Create default syntax config file.
pub fn make_default_syntax_config() -> Result<PathBuf, TealdeerError> {
    let config_dir = get_config_dir()?;
    if !config_dir.is_dir() {
        if let Err(e) = fs::create_dir_all(&config_dir) {
            return Err(ConfigError(format!("Could not create config directory: {}", e)));
        }
    }

    let syntax_config_file_path = config_dir.join(SYNTAX_CONFIG_FILE_NAME);
    if syntax_config_file_path.is_file() {
        return Err(ConfigError(format!(
            "A configuration file already exists at {}, no action was taken.",
            syntax_config_file_path.to_str().unwrap()
        )));
    }

    let serialized_syntax_config = toml::to_string(&RawConfig::new())
        .map_err(|err| ConfigError(format!("Failed to serialize default syntax config: {}", err)))?;

    let mut syntax_config_file = fs::File::create(&syntax_config_file_path)
        .map_err(map_io_err_to_config_err)?;
    let _wc = syntax_config_file.write(serialized_syntax_config.as_bytes())
        .map_err(map_io_err_to_config_err)?;

    Ok(syntax_config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_void, c_int};
use libc::{MS_REC, MS_PRIVATE, MS_NOSUID, MS_NODEV, MS_NOEXEC, MNT_DETACH};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};
use std::fs;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::io::{Read, Write};
use std::thread;
use linux::{chdir, mount, umount2, pivot_root};
use stack::Stack;

// a lot copied from man USER_NAMESPACES(7)
extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs<bool>;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    let setup = unsafe { (*args).setup };
    let run = unsafe { (*args).run };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let h = thread::spawn(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            assert_eq!(libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1), 0);
        }

        {
            println!("Child status");
            let mut fstatus = File::open("/proc/self/status").expect("open status");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                if l.starts_with("Seccomp") || l.starts_with("Cap"){
                    println!("{}", l);
                }
            }
        }

        let container_root = Path::new("./mntcont");
        if container_root.exists() {
            assert!(container_root.is_dir());
        } else {
            println!("creating {:?}", container_root.as_os_str());
            fs::create_dir(container_root).expect("mkdir mntcont");
        }

        println!("mounting container root");
        mount(None, Some(container_root), Some("tmpfs"), 0);
        mount(Some("none"), Some("/"), None, MS_REC|MS_PRIVATE);
        {
            let proc_dir = container_root.join("proc");
            println!("setting up {:?}", proc_dir.as_os_str());
            fs::create_dir(proc_dir.as_path()).expect("mkdir proc");
            mount(None, Some(proc_dir.as_path()), Some("proc"), MS_NOSUID|MS_NODEV|MS_NOEXEC);
        }

        // setup: a chance to populate root.
        let setup_result = setup(container_root);

        let old_root_name = "oldroot";
        {
            println!("Entering new root");
            let old_root = container_root.join(old_root_name);
            fs::create_dir(old_root.as_path()).expect("mkdir oldroot");
            pivot_root(container_root, old_root.as_path());
        }

        chdir("/");

        // we moved to the new root
        let container_root = Path::new("/");
        let old_root = container_root.join(old_root_name);
        let old_root = old_root.as_path();

        {
            println!("getting rid of old root");
            umount2(old_root, MNT_DETACH);
            assert!(fs::read_dir(old_root).expect("oldroot dir").next().is_none());
            fs::remove_dir(old_root).expect("rmdir oldroot");
        }

        {
            println!("mountinfo");
            let mut fstatus = File::open("/proc/self/mountinfo").expect("open mountinfo");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                println!("{}", l);
            }
        }
        setup_result
    });

    match h.join() {
        Ok(setup_result) => run(setup_result),
        Err(_) => 1,
    }
}

struct ChildArgs<T> {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
    setup: fn(&Path) -> T, //container_root -> custom_val
    run: fn(T) -> c_int,
}

//write to file with one write call
fn write(path: &String, content: &[u8]) {
    println!("Writing to {}", path);
    let mut file = OpenOptions::new().read(false).write(true).create(false).open(path).expect("open");
    // needs to happen in one write call
    assert_eq!(file.write(content).expect("write"), content.len());
}

pub fn runcontained(setup: fn(&Path) -> bool, run: fn(bool) -> c_int) {

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1], setup: setup, run: run});
        Box::into_raw(child_args) as *mut c_void
    };

    let child_stack = Stack::new(512);

    println!("cloning");
    let child_pid = unsafe {
        libc::clone(child_func, child_stack.top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let uid = unsafe { libc::getuid() };
    write(&format!("/proc/{}/uid_map", child_pid), &format!("0 {} 1", uid).into_bytes());

    //proc_setgroups_write man user_namespaces
    write(&format!("/proc/{}/setgroups", child_pid), &format!("deny").into_bytes());

    let gid = unsafe { libc::getgid() };
    write(&format!("/proc/{}/gid_map", child_pid), &format!("0 {} 1", gid).into_bytes());

    //unleash child
    assert_eq!(unsafe { libc::close(pipe_fd[1]) }, 0);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //child_stack gets unmapped
}
<commit_msg>document what we have<commit_after>extern crate libc;

use libc::{c_void, c_int};
use libc::{MS_REC, MS_PRIVATE, MS_NOSUID, MS_NODEV, MS_NOEXEC, MNT_DETACH};
use libc::{CLONE_NEWUSER, CLONE_NEWNS, CLONE_NEWPID, SIGCHLD};
use std::fs;
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::io::{Read, Write};
use std::thread;
use linux::{chdir, mount, umount2, pivot_root};
use stack::Stack;

// a lot copied from man USER_NAMESPACES(7)
extern "C" fn child_func(args: *mut c_void) -> c_int {
    println!("I'm called from child_func");

    let args = args as *mut ChildArgs<bool>;
    let r_pipe_fd = unsafe { (*args).r_pipe_fd };
    let w_pipe_fd = unsafe { (*args).w_pipe_fd };
    let setup = unsafe { (*args).setup };
    let run = unsafe { (*args).run };
    println!("r_pipe_fd: {}", r_pipe_fd);

    // run everything in a new thread so exceptions bubble up to rust
    let h = thread::spawn(move || {
        // wait for parent
        println!("waiting for parent to set up mapping, ...");
        assert_eq!(unsafe { libc::close(w_pipe_fd) } , 0);
        unsafe {
            let mut buf = 0u8;
            assert_eq!(libc::read(r_pipe_fd, (&mut buf) as *mut u8 as *mut c_void, 1), 0);
        }

        {
            println!("Child status");
            let mut fstatus = File::open("/proc/self/status").expect("open status");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                if l.starts_with("Seccomp") || l.starts_with("Cap"){
                    println!("{}", l);
                }
            }
        }

        let container_root = Path::new("./mntcont");
        if container_root.exists() {
            assert!(container_root.is_dir());
        } else {
            println!("creating {:?}", container_root.as_os_str());
            fs::create_dir(container_root).expect("mkdir mntcont");
        }

        println!("mounting container root");
        mount(None, Some(container_root), Some("tmpfs"), 0);
        mount(Some("none"), Some("/"), None, MS_REC|MS_PRIVATE);
        {
            let proc_dir = container_root.join("proc");
            println!("setting up {:?}", proc_dir.as_os_str());
            fs::create_dir(proc_dir.as_path()).expect("mkdir proc");
            mount(None, Some(proc_dir.as_path()), Some("proc"), MS_NOSUID|MS_NODEV|MS_NOEXEC);
        }

        // setup: a chance to populate root.
        let setup_result = setup(container_root);

        let old_root_name = "oldroot";
        {
            println!("Entering new root");
            let old_root = container_root.join(old_root_name);
            fs::create_dir(old_root.as_path()).expect("mkdir oldroot");
            pivot_root(container_root, old_root.as_path());
        }

        chdir("/");

        // we moved to the new root
        let container_root = Path::new("/");
        let old_root = container_root.join(old_root_name);
        let old_root = old_root.as_path();

        {
            println!("getting rid of old root");
            umount2(old_root, MNT_DETACH);
            assert!(fs::read_dir(old_root).expect("oldroot dir").next().is_none());
            fs::remove_dir(old_root).expect("rmdir oldroot");
        }

        {
            println!("mountinfo");
            let mut fstatus = File::open("/proc/self/mountinfo").expect("open mountinfo");
            let mut contents = String::new();
            fstatus.read_to_string(&mut contents).expect("read status");
            for l in contents.lines(){
                println!("{}", l);
            }
        }

        // Bare setup is done.
        // Missing:
        //  * Resource limitations with cgroups
        //  * Capability setup (process has full capabilities in its user namespace!)
        //  * Container is naked:
        //    * Full kernel attack surface, no syscall limitations with seccomp
        //    * No critical things prohibited with AppArmor or SELinux (e.g. container has full
        //      /proc access)
        setup_result
    });

    match h.join() {
        Ok(setup_result) => run(setup_result),
        Err(_) => 1,
    }
}

struct ChildArgs<T> {
    r_pipe_fd: c_int,
    w_pipe_fd: c_int,
    setup: fn(&Path) -> T, //container_root -> custom_val
    run: fn(T) -> c_int,
}

//write to file with one write call
fn write(path: &String, content: &[u8]) {
    println!("Writing to {}", path);
    let mut file = OpenOptions::new().read(false).write(true).create(false).open(path).expect("open");
    // needs to happen in one write call
    assert_eq!(file.write(content).expect("write"), content.len());
}

pub fn runcontained(setup: fn(&Path) -> bool, run: fn(bool) -> c_int) {

    let mut pipe_fd = vec![0;2];  /* Pipe used to synchronize parent and child */
    assert_eq!(unsafe { libc::pipe(pipe_fd.as_mut_ptr()) }, 0);
    println!("r pipe: {} w pipe: {}", pipe_fd[0], pipe_fd[1]);
    let ptr_child_args = {
        let child_args = Box::new(ChildArgs { r_pipe_fd: pipe_fd[0], w_pipe_fd: pipe_fd[1], setup: setup, run: run});
        Box::into_raw(child_args) as *mut c_void
    };

    let child_stack = Stack::new(512);

    println!("cloning");
    let child_pid = unsafe {
        libc::clone(child_func, child_stack.top, CLONE_NEWUSER|CLONE_NEWNS|CLONE_NEWPID|SIGCHLD, ptr_child_args)
    };
    assert!(child_pid != -1);

    let uid = unsafe { libc::getuid() };
    write(&format!("/proc/{}/uid_map", child_pid), &format!("0 {} 1", uid).into_bytes());

    //proc_setgroups_write man user_namespaces
    write(&format!("/proc/{}/setgroups", child_pid), &format!("deny").into_bytes());

    let gid = unsafe { libc::getgid() };
    write(&format!("/proc/{}/gid_map", child_pid), &format!("0 {} 1", gid).into_bytes());

    //unleash child
    assert_eq!(unsafe { libc::close(pipe_fd[1]) }, 0);

    let mut child_status: c_int = 0;
    assert!(unsafe { libc::waitpid(child_pid, &mut child_status, 0) } != -1);
    println!("Child terminated: {}", child_status);

    unsafe {
        drop(Box::from_raw(ptr_child_args));
    }

    //child_stack gets unmapped
}
<|endoftext|>


<commit_before>//! Endpoints for push notifications.

use ruma_common::push::PusherData;
use ruma_serde::StringEnum;
use serde::{Deserialize, Serialize};

pub mod delete_pushrule;
pub mod get_notifications;
pub mod get_pushers;
pub mod get_pushrule;
pub mod get_pushrule_actions;
pub mod get_pushrule_enabled;
pub mod get_pushrules_all;
pub mod get_pushrules_global_scope;
pub mod set_pusher;
pub mod set_pushrule;
pub mod set_pushrule_actions;
pub mod set_pushrule_enabled;

/// The kinds of push rules that are available
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
pub enum RuleKind {
    /// User-configured rules that override all other kinds
    Override,

    /// Lowest priority user-defined rules
    Underride,

    /// Sender-specific rules
    Sender,

    /// Room-specific rules
    Room,

    /// Content-specific rules
    Content,

    #[doc(hidden)]
    _Custom(String),
}

/// Defines a pusher
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Pusher {
    /// This is a unique identifier for this pusher. Max length, 512 bytes.
    pub pushkey: String,

    /// The kind of the pusher. If set to None in a call to set_pusher, this
    /// will delete the pusher
    pub kind: Option<PusherKind>,

    /// This is a reverse-DNS style identifier for the application. Max length, 64 chars.
    pub app_id: String,

    /// A string that will allow the user to identify what application owns this pusher.
    pub app_display_name: String,

    /// A string that will allow the user to identify what device owns this pusher.
    pub device_display_name: String,

    /// This string determines which set of device specific rules this pusher executes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile_tag: Option<String>,

    /// The preferred language for receiving notifications (e.g. 'en' or 'en-US')
    pub lang: String,

    /// Information for the pusher implementation itself.
    pub data: PusherData,
}

/// Which kind a pusher is
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
pub enum PusherKind {
    /// A pusher that sends HTTP pokes.
    Http,

    /// A pusher that emails the user with unread notifications.
    Email,

    #[doc(hidden)]
    _Custom(String),
}
<commit_msg>Add more dots to docs<commit_after>//! Endpoints for push notifications.

use ruma_common::push::PusherData;
use ruma_serde::StringEnum;
use serde::{Deserialize, Serialize};

pub mod delete_pushrule;
pub mod get_notifications;
pub mod get_pushers;
pub mod get_pushrule;
pub mod get_pushrule_actions;
pub mod get_pushrule_enabled;
pub mod get_pushrules_all;
pub mod get_pushrules_global_scope;
pub mod set_pusher;
pub mod set_pushrule;
pub mod set_pushrule_actions;
pub mod set_pushrule_enabled;

/// The kinds of push rules that are available.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
pub enum RuleKind {
    /// User-configured rules that override all other kinds.
    Override,

    /// Lowest priority user-defined rules.
    Underride,

    /// Sender-specific rules.
    Sender,

    /// Room-specific rules.
    Room,

    /// Content-specific rules.
    Content,

    #[doc(hidden)]
    _Custom(String),
}

/// Defines a pusher.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Pusher {
    /// This is a unique identifier for this pusher. Max length, 512 bytes.
    pub pushkey: String,

    /// The kind of the pusher. If set to None in a call to set_pusher, this
    /// will delete the pusher
    pub kind: Option<PusherKind>,

    /// This is a reverse-DNS style identifier for the application. Max length, 64 chars.
    pub app_id: String,

    /// A string that will allow the user to identify what application owns this pusher.
    pub app_display_name: String,

    /// A string that will allow the user to identify what device owns this pusher.
    pub device_display_name: String,

    /// This string determines which set of device specific rules this pusher executes.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile_tag: Option<String>,

    /// The preferred language for receiving notifications (e.g. 'en' or 'en-US')
    pub lang: String,

    /// Information for the pusher implementation itself.
    pub data: PusherData,
}

/// Which kind a pusher is.
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
pub enum PusherKind {
    /// A pusher that sends HTTP pokes.
    Http,

    /// A pusher that emails the user with unread notifications.
    Email,

    #[doc(hidden)]
    _Custom(String),
}
<|endoftext|>


<commit_before>use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("Failed to read line");

    println!("You guessed: {}", guess);
}
<commit_msg>implementing rust guessing game<commit_after>extern crate rand;

use std::io;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin().read_line(&mut guess)
        .ok()
        .expect("failed to read line");

    println!("You guessed: {}", guess);
}
<|endoftext|>


<commit_before>use crate::logger::{self, Level};
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> bool {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.mode(0o600).truncate(true);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(_) => return false,
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            let buf = pid.as_bytes();
            let success = file.write_all(&buf).is_ok();
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.lock_file.take().is_some() {
                    remove_lock(&self.lock_path);
                }
                self.lock_file = Some(file);
                self.lock_path = new_lock_path.to_owned();
            }
            success
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                errno::errno()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<commit_msg>Truncate file after lockf<commit_after>use crate::logger::{self, Level};
use std::fs::{self, File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
use std::process;

fn remove_lock(lock_path: &Path) {
    fs::remove_file(lock_path).ok();
    log!(
        Level::Debug,
        "FsLock: removed lockfile {}",
        lock_path.display()
    );
}

#[derive(Default)]
pub struct FsLock {
    lock_path: PathBuf,
    lock_file: Option<File>,
}

impl Drop for FsLock {
    fn drop(&mut self) {
        if self.lock_file.is_some() {
            remove_lock(&self.lock_path);
        }
    }
}

impl FsLock {
    pub fn try_lock(&mut self, new_lock_path: &Path, pid: &mut libc::pid_t) -> bool {
        if self.lock_file.is_some() && self.lock_path == new_lock_path {
            return true;
        }

        // pid == 0 indicates that something went majorly wrong during locking
        *pid = 0;

        log!(
            Level::Debug,
            "FsLock: trying to lock `{}'",
            new_lock_path.display()
        );

        // first we open (and possibly create) the lock file
        let mut options = OpenOptions::new();
        options.create(true).read(true).write(true).mode(0o600);
        let mut file = match options.open(&new_lock_path) {
            Ok(file) => file,
            Err(_) => return false,
        };

        // then we lock it (returns immediately if locking is not possible)
        if unsafe { libc::lockf(file.as_raw_fd(), libc::F_TLOCK, 0) } == 0 {
            log!(
                Level::Debug,
                "FsLock: locked `{}', writing PID...",
                new_lock_path.display()
            );
            let pid = process::id().to_string();
            let success = match unsafe { libc::ftruncate(file.as_raw_fd(), 0) } {
                0 => file.write_all(&pid.as_bytes()).is_ok(),
                _ => false,
            };
            log!(
                Level::Debug,
                "FsLock: PID written successfully: {}",
                success as usize
            );
            if success {
                if self.lock_file.take().is_some() {
                    remove_lock(&self.lock_path);
                }
                self.lock_file = Some(file);
                self.lock_path = new_lock_path.to_owned();
            }
            success
        } else {
            log!(
                Level::Error,
                "FsLock: something went wrong during locking: {}",
                errno::errno()
            );

            // locking was not successful -> read PID of locking process from the file
            let mut buf = String::new();
            if file.read_to_string(&mut buf).is_ok() {
                *pid = buf.parse().unwrap_or(0);
            }
            log!(
                Level::Debug,
                "FsLock: locking failed, already locked by {}",
                pid
            );
            false
        }
    }
}
<|endoftext|>


<commit_before>#![recursion_limit="256"]

extern crate proc_macro;
extern crate relm_derive_common;
extern crate syn;

use proc_macro::TokenStream;

use relm_derive_common::{impl_msg, impl_simple_msg};
use syn::{Ident, Item, parse};
use syn::spanned::Spanned;

#[proc_macro_derive(SimpleMsg)]
pub fn simple_msg(input: TokenStream) -> TokenStream {
    let ast: Item = parse(input).unwrap();
    let gen = impl_simple_msg(&ast, Ident::new("relm", ast.span()));
    gen.into()
}

#[proc_macro_derive(Msg)]
pub fn msg(input: TokenStream) -> TokenStream {
    let ast: Item = parse(input).unwrap();
    let gen = impl_msg(&ast, Ident::new("relm", ast.span()));
    gen.into()
}
<commit_msg>Fix typo<commit_after>#![recursion_limit="256"]

extern crate proc_macro;
extern crate relm_derive_common;
extern crate syn;

use proc_macro::TokenStream;

use relm_derive_common::{impl_msg, impl_simple_msg};
use syn::{Ident, Item, parse};
use syn::spanned::Spanned;

#[proc_macro_derive(SimpleMsg)]
pub fn simple_msg(input: TokenStream) -> TokenStream {
    let ast: Item = parse(input).unwrap();
    let gen = impl_simple_msg(&ast, Ident::new("relm_state", ast.span()));
    gen.into()
}

#[proc_macro_derive(Msg)]
pub fn msg(input: TokenStream) -> TokenStream {
    let ast: Item = parse(input).unwrap();
    let gen = impl_msg(&ast, Ident::new("relm_state", ast.span()));
    gen.into()
}
<|endoftext|>


<commit_before>//! All VST plugin editor related functionality.

use std::os::raw::c_void;

/// Implemented by plugin editors.
#[allow(unused_variables)]
pub trait Editor {
    /// Get the size of the editor window.
    fn size(&self) -> (i32, i32);

    /// Get the coordinates of the editor window.
    fn position(&self) -> (i32, i32);

    /// Editor idle call. Called by host.
    fn idle(&mut self) {}

    /// Called when the editor window is closed.
    fn close(&mut self) {}

    /// Called when the editor window is opened.
    ///
    /// `parent` is a window pointer that the new window should attach itself to.
    /// **It is dependent upon the platform you are targeting.**
    ///
    /// A few examples:
    ///
    ///  - On Windows, it should be interpreted as a `HWND`
    ///  - On Mac OS X (64 bit), it should be interpreted as a `NSView*`
    ///  - On X11 platforms, it should be interpreted as a `u32` (the ID number of the parent window)
    ///
    /// Return `true` if the window opened successfully, `false` otherwise.
    fn open(&mut self, parent: *mut c_void) -> bool;

    /// Return whether the window is currently open.
    fn is_open(&mut self) -> bool;

    /// Set the knob mode for this editor (if supported by host).
    ///
    /// Return `true` if the knob mode was set.
    fn set_knob_mode(&mut self, mode: KnobMode) -> bool {
        false
    }

    /// Receive key up event. Return `true` if the key was used.
    fn key_up(&mut self, keycode: KeyCode) -> bool {
        false
    }

    /// Receive key down event. Return `true` if the key was used.
    fn key_down(&mut self, keycode: KeyCode) -> bool {
        false
    }
}

/// Rectangle used to specify dimensions of editor window.
#[doc(hidden)]
#[derive(Copy, Clone, Debug)]
pub struct Rect {
    /// Y value in pixels of top side.
    pub top: i16,
    /// X value in pixels of left side.
    pub left: i16,
    /// Y value in pixels of bottom side.
    pub bottom: i16,
    /// X value in pixels of right side.
    pub right: i16,
}

/// A platform independent key code. Includes modifier keys.
#[derive(Copy, Clone, Debug)]
pub struct KeyCode {
    /// ASCII character for key pressed (if applicable).
    pub character: char,
    /// Key pressed. See `enums::Key`.
    pub key: Key,
    /// Modifier key bitflags. See `enums::flags::modifier_key`.
    pub modifier: u8,
}

/// Allows host to set how a parameter knob works.
#[repr(usize)]
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
pub enum KnobMode {
    Circular,
    CircularRelative,
    Linear,
}
impl_clike!(KnobMode);

/// Platform independent key codes.
#[allow(missing_docs)]
#[repr(usize)]
#[derive(Debug, Copy, Clone)]
pub enum Key {
    Back = 1,
    Tab,
    Clear,
    Return,
    Pause,
    Escape,
    Space,
    Next,
    End,
    Home,
    Left,
    Up,
    Right,
    Down,
    PageUp,
    PageDown,
    Select,
    Print,
    Enter,
    Snapshot,
    Insert,
    Delete,
    Help,
    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    Multiply,
    Add,
    Separator,
    Subtract,
    Decimal,
    Divide,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    Numlock,
    Scroll,
    Shift,
    Control,
    Alt,
    Equals,
}
impl_clike!(Key);
<commit_msg>Key character fix (#152)<commit_after>//! All VST plugin editor related functionality.

use std::os::raw::c_void;

/// Implemented by plugin editors.
#[allow(unused_variables)]
pub trait Editor {
    /// Get the size of the editor window.
    fn size(&self) -> (i32, i32);

    /// Get the coordinates of the editor window.
    fn position(&self) -> (i32, i32);

    /// Editor idle call. Called by host.
    fn idle(&mut self) {}

    /// Called when the editor window is closed.
    fn close(&mut self) {}

    /// Called when the editor window is opened.
    ///
    /// `parent` is a window pointer that the new window should attach itself to.
    /// **It is dependent upon the platform you are targeting.**
    ///
    /// A few examples:
    ///
    ///  - On Windows, it should be interpreted as a `HWND`
    ///  - On Mac OS X (64 bit), it should be interpreted as a `NSView*`
    ///  - On X11 platforms, it should be interpreted as a `u32` (the ID number of the parent window)
    ///
    /// Return `true` if the window opened successfully, `false` otherwise.
    fn open(&mut self, parent: *mut c_void) -> bool;

    /// Return whether the window is currently open.
    fn is_open(&mut self) -> bool;

    /// Set the knob mode for this editor (if supported by host).
    ///
    /// Return `true` if the knob mode was set.
    fn set_knob_mode(&mut self, mode: KnobMode) -> bool {
        false
    }

    /// Receive key up event. Return `true` if the key was used.
    fn key_up(&mut self, keycode: KeyCode) -> bool {
        false
    }

    /// Receive key down event. Return `true` if the key was used.
    fn key_down(&mut self, keycode: KeyCode) -> bool {
        false
    }
}

/// Rectangle used to specify dimensions of editor window.
#[doc(hidden)]
#[derive(Copy, Clone, Debug)]
pub struct Rect {
    /// Y value in pixels of top side.
    pub top: i16,
    /// X value in pixels of left side.
    pub left: i16,
    /// Y value in pixels of bottom side.
    pub bottom: i16,
    /// X value in pixels of right side.
    pub right: i16,
}

/// A platform independent key code. Includes modifier keys.
#[derive(Copy, Clone, Debug)]
pub struct KeyCode {
    /// ASCII character for key pressed (if applicable).
    pub character: char,
    /// Key pressed. See `enums::Key`.
    pub key: Key,
    /// Modifier key bitflags. See `enums::flags::modifier_key`.
    pub modifier: u8,
}

/// Allows host to set how a parameter knob works.
#[repr(usize)]
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
pub enum KnobMode {
    Circular,
    CircularRelative,
    Linear,
}
impl_clike!(KnobMode);

/// Platform independent key codes.
#[allow(missing_docs)]
#[repr(usize)]
#[derive(Debug, Copy, Clone)]
pub enum Key {
    None = 0,
    Back,
    Tab,
    Clear,
    Return,
    Pause,
    Escape,
    Space,
    Next,
    End,
    Home,
    Left,
    Up,
    Right,
    Down,
    PageUp,
    PageDown,
    Select,
    Print,
    Enter,
    Snapshot,
    Insert,
    Delete,
    Help,
    Numpad0,
    Numpad1,
    Numpad2,
    Numpad3,
    Numpad4,
    Numpad5,
    Numpad6,
    Numpad7,
    Numpad8,
    Numpad9,
    Multiply,
    Add,
    Separator,
    Subtract,
    Decimal,
    Divide,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    Numlock,
    Scroll,
    Shift,
    Control,
    Alt,
    Equals,
}
impl_clike!(Key);
<|endoftext|>


<commit_before>use std::ops::ControlFlow::{self, Break, Continue};

use chalk_ir::{
    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
    DebruijnIndex,
};
use hir_def::{
    adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
    EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
};

use crate::{
    db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
};

pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
    let mut uninhabited_from = UninhabitedFrom { target_mod, db };
    let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
    inhabitedness == BREAK_VISIBLY_UNINHABITED
}

pub(crate) fn is_enum_variant_uninhabited_from(
    variant: EnumVariantId,
    subst: &Substitution,
    target_mod: ModuleId,
    db: &dyn HirDatabase,
) -> bool {
    let enum_data = db.enum_data(variant.parent);
    let vars_attrs = db.variants_attrs(variant.parent);
    let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();

    let mut uninhabited_from = UninhabitedFrom { target_mod, db };
    let inhabitedness = uninhabited_from.visit_variant(
        variant.into(),
        &enum_data.variants[variant.local_id].variant_data,
        subst,
        &vars_attrs[variant.local_id],
        is_local,
    );
    inhabitedness == BREAK_VISIBLY_UNINHABITED
}

struct UninhabitedFrom<'a> {
    target_mod: ModuleId,
    db: &'a dyn HirDatabase,
}

const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(VisiblyUninhabited);
#[derive(PartialEq, Eq)]
struct VisiblyUninhabited;

impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
    type BreakTy = VisiblyUninhabited;

    fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> {
        self
    }

    fn visit_ty(
        &mut self,
        ty: &Ty,
        outer_binder: DebruijnIndex,
    ) -> ControlFlow<VisiblyUninhabited> {
        match ty.kind(Interner) {
            TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
            TyKind::Never => BREAK_VISIBLY_UNINHABITED,
            TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
            TyKind::Array(item_ty, len) => match try_usize_const(len) {
                Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
                Some(1..) => item_ty.super_visit_with(self, outer_binder),
            },

            TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
        }
    }

    fn interner(&self) -> Interner {
        Interner
    }
}

impl UninhabitedFrom<'_> {
    fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
        let attrs = self.db.attrs(adt.into());
        let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
        let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
        if adt_non_exhaustive && !is_local {
            return CONTINUE_OPAQUELY_INHABITED;
        }

        // An ADT is uninhabited iff all its variants uninhabited.
        match adt {
            // rustc: For now, `union`s are never considered uninhabited.
            AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
            AdtId::StructId(s) => {
                let struct_data = self.db.struct_data(s);
                self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
            }
            AdtId::EnumId(e) => {
                let vars_attrs = self.db.variants_attrs(e);
                let enum_data = self.db.enum_data(e);

                for (local_id, enum_var) in enum_data.variants.iter() {
                    let variant_inhabitedness = self.visit_variant(
                        EnumVariantId { parent: e, local_id }.into(),
                        &enum_var.variant_data,
                        subst,
                        &vars_attrs[local_id],
                        is_local,
                    );
                    match variant_inhabitedness {
                        Break(VisiblyUninhabited) => continue,
                        Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
                    }
                }
                BREAK_VISIBLY_UNINHABITED
            }
        }
    }

    fn visit_variant(
        &mut self,
        variant: VariantId,
        variant_data: &VariantData,
        subst: &Substitution,
        attrs: &Attrs,
        is_local: bool,
    ) -> ControlFlow<VisiblyUninhabited> {
        let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
        if non_exhaustive_field_list && !is_local {
            return CONTINUE_OPAQUELY_INHABITED;
        }

        let is_enum = matches!(variant, VariantId::EnumVariantId(..));
        let field_tys = self.db.field_types(variant);
        let field_vis = self.db.field_visibilities(variant);

        for (fid, _) in variant_data.fields().iter() {
            self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
        }
        CONTINUE_OPAQUELY_INHABITED
    }

    fn visit_field(
        &mut self,
        vis: Visibility,
        ty: &Binders<Ty>,
        subst: &Substitution,
        is_enum: bool,
    ) -> ControlFlow<VisiblyUninhabited> {
        if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
            let ty = ty.clone().substitute(Interner, subst);
            ty.visit_with(self, DebruijnIndex::INNERMOST)
        } else {
            CONTINUE_OPAQUELY_INHABITED
        }
    }
}

fn try_usize_const(c: &Const) -> Option<u128> {
    let data = &c.data(Interner);
    if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
        return None;
    }
    match data.value {
        ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
        _ => None,
    }
}
<commit_msg>Add docs<commit_after>//! Type inhabitedness logic.
use std::ops::ControlFlow::{self, Break, Continue};

use chalk_ir::{
    visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
    DebruijnIndex,
};
use hir_def::{
    adt::VariantData, attr::Attrs, type_ref::ConstScalar, visibility::Visibility, AdtId,
    EnumVariantId, HasModule, Lookup, ModuleId, VariantId,
};

use crate::{
    db::HirDatabase, Binders, ConcreteConst, Const, ConstValue, Interner, Substitution, Ty, TyKind,
};

/// Checks whether a type is visibly uninhabited from a particular module.
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
    let mut uninhabited_from = UninhabitedFrom { target_mod, db };
    let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
    inhabitedness == BREAK_VISIBLY_UNINHABITED
}

/// Checks whether a variant is visibly uninhabited from a particular module.
pub(crate) fn is_enum_variant_uninhabited_from(
    variant: EnumVariantId,
    subst: &Substitution,
    target_mod: ModuleId,
    db: &dyn HirDatabase,
) -> bool {
    let enum_data = db.enum_data(variant.parent);
    let vars_attrs = db.variants_attrs(variant.parent);
    let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();

    let mut uninhabited_from = UninhabitedFrom { target_mod, db };
    let inhabitedness = uninhabited_from.visit_variant(
        variant.into(),
        &enum_data.variants[variant.local_id].variant_data,
        subst,
        &vars_attrs[variant.local_id],
        is_local,
    );
    inhabitedness == BREAK_VISIBLY_UNINHABITED
}

struct UninhabitedFrom<'a> {
    target_mod: ModuleId,
    db: &'a dyn HirDatabase,
}

const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(VisiblyUninhabited);
#[derive(PartialEq, Eq)]
struct VisiblyUninhabited;

impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
    type BreakTy = VisiblyUninhabited;

    fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> {
        self
    }

    fn visit_ty(
        &mut self,
        ty: &Ty,
        outer_binder: DebruijnIndex,
    ) -> ControlFlow<VisiblyUninhabited> {
        match ty.kind(Interner) {
            TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
            TyKind::Never => BREAK_VISIBLY_UNINHABITED,
            TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
            TyKind::Array(item_ty, len) => match try_usize_const(len) {
                Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
                Some(1..) => item_ty.super_visit_with(self, outer_binder),
            },

            TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
        }
    }

    fn interner(&self) -> Interner {
        Interner
    }
}

impl UninhabitedFrom<'_> {
    fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
        let attrs = self.db.attrs(adt.into());
        let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
        let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
        if adt_non_exhaustive && !is_local {
            return CONTINUE_OPAQUELY_INHABITED;
        }

        // An ADT is uninhabited iff all its variants uninhabited.
        match adt {
            // rustc: For now, `union`s are never considered uninhabited.
            AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
            AdtId::StructId(s) => {
                let struct_data = self.db.struct_data(s);
                self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
            }
            AdtId::EnumId(e) => {
                let vars_attrs = self.db.variants_attrs(e);
                let enum_data = self.db.enum_data(e);

                for (local_id, enum_var) in enum_data.variants.iter() {
                    let variant_inhabitedness = self.visit_variant(
                        EnumVariantId { parent: e, local_id }.into(),
                        &enum_var.variant_data,
                        subst,
                        &vars_attrs[local_id],
                        is_local,
                    );
                    match variant_inhabitedness {
                        Break(VisiblyUninhabited) => continue,
                        Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
                    }
                }
                BREAK_VISIBLY_UNINHABITED
            }
        }
    }

    fn visit_variant(
        &mut self,
        variant: VariantId,
        variant_data: &VariantData,
        subst: &Substitution,
        attrs: &Attrs,
        is_local: bool,
    ) -> ControlFlow<VisiblyUninhabited> {
        let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
        if non_exhaustive_field_list && !is_local {
            return CONTINUE_OPAQUELY_INHABITED;
        }

        let is_enum = matches!(variant, VariantId::EnumVariantId(..));
        let field_tys = self.db.field_types(variant);
        let field_vis = self.db.field_visibilities(variant);

        for (fid, _) in variant_data.fields().iter() {
            self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
        }
        CONTINUE_OPAQUELY_INHABITED
    }

    fn visit_field(
        &mut self,
        vis: Visibility,
        ty: &Binders<Ty>,
        subst: &Substitution,
        is_enum: bool,
    ) -> ControlFlow<VisiblyUninhabited> {
        if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
            let ty = ty.clone().substitute(Interner, subst);
            ty.visit_with(self, DebruijnIndex::INNERMOST)
        } else {
            CONTINUE_OPAQUELY_INHABITED
        }
    }
}

fn try_usize_const(c: &Const) -> Option<u128> {
    let data = &c.data(Interner);
    if data.ty.kind(Interner) != &TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize)) {
        return None;
    }
    match data.value {
        ConstValue::Concrete(ConcreteConst { interned: ConstScalar::UInt(value) }) => Some(value),
        _ => None,
    }
}
<|endoftext|>