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

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

use dom::{TNode, TElement, UnsafeNode};
use matching::MatchMethods;
use selectors::bloom::BloomFilter;

pub struct StyleBloom {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains. These unsafe
    /// nodes are guaranteed to be elements.
    ///
    /// Note that the use we do for them is safe, since the data we access from
    /// them is completely read-only during restyling.
    elements: Vec<UnsafeNode>,

    /// A monotonic counter incremented which each reflow in order to invalidate
    /// the bloom filter if appropriate.
    generation: u32,
}

impl StyleBloom {
    pub fn new(generation: u32) -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
            generation: generation,
        }
    }

    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    pub fn generation(&self) -> u32 {
        self.generation
    }

    pub fn maybe_pop<E>(&mut self, element: E)
        where E: TElement + MatchMethods
    {
        if self.elements.last() == Some(&element.as_node().to_unsafe()) {
            self.pop::<E>().unwrap();
        }
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push<E>(&mut self, element: E)
        where E: TElement + MatchMethods,
    {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.parent_element().is_none());
            }
        }
        element.insert_into_bloom_filter(&mut *self.filter);
        self.elements.push(element.as_node().to_unsafe());
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop<E>(&mut self) -> Option<E>
        where E: TElement + MatchMethods,
    {
        let popped =
            self.elements.pop().map(|unsafe_node| {
                let parent = unsafe {
                    E::ConcreteNode::from_unsafe(&unsafe_node)
                };
                parent.as_element().unwrap()
            });
        if let Some(popped) = popped {
            popped.remove_from_bloom_filter(&mut self.filter);
        }

        popped
    }

    fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    fn rebuild<E>(&mut self, mut element: E) -> usize
        where E: TElement + MatchMethods,
    {
        self.clear();

        while let Some(parent) = element.parent_element() {
            parent.insert_into_bloom_filter(&mut *self.filter);
            self.elements.push(parent.as_node().to_unsafe());
            element = parent;
        }

        // Put them in the order we expect, from root to `element`'s parent.
        self.elements.reverse();
        return self.elements.len();
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    pub fn assert_complete<E>(&self, mut element: E)
        where E: TElement,
    {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.parent_element() {
                assert_eq!(parent.as_node().to_unsafe(),
                           self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth.
    pub fn insert_parents_recovering<E>(&mut self,
                                        element: E,
                                        element_depth: Option<usize>,
                                        generation: u32)
                                        -> usize
        where E: TElement,
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.generation != generation || self.elements.is_empty() {
            self.generation = generation;
            return self.rebuild(element);
        }

        let parent_element = match element.parent_element() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return 0;
            }
        };

        let unsafe_parent = parent_element.as_node().to_unsafe();
        if self.elements.last() == Some(&unsafe_parent) {
            // Ta da, cache hit, we're all done.
            return self.elements.len();
        }

        let element_depth = match element_depth {
            Some(depth) => depth,
            // If we don't know the depth of `element`, we'd rather don't try
            // fixing up the bloom filter, since it's quadratic.
            None => {
                return self.rebuild(element);
            }
        };

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth >= element_depth - 1 {
            self.pop::<E>().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with parent_element.
        let mut common_parent = parent_element;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = vec![];

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        while *self.elements.last().unwrap() != common_parent.as_node().to_unsafe() {
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied again?");
            self.pop::<E>().unwrap();
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.into_iter().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
        return self.elements.len();
    }
}
<commit_msg>Auto merge of #14439 - emilio:fix-bloom, r=bholley<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/. */

//! The style bloom filter is used as an optimization when matching deep
//! descendant selectors.

use dom::{TNode, TElement, UnsafeNode};
use matching::MatchMethods;
use selectors::bloom::BloomFilter;

pub struct StyleBloom {
    /// The bloom filter per se.
    filter: Box<BloomFilter>,

    /// The stack of elements that this bloom filter contains. These unsafe
    /// nodes are guaranteed to be elements.
    ///
    /// Note that the use we do for them is safe, since the data we access from
    /// them is completely read-only during restyling.
    elements: Vec<UnsafeNode>,

    /// A monotonic counter incremented which each reflow in order to invalidate
    /// the bloom filter if appropriate.
    generation: u32,
}

impl StyleBloom {
    pub fn new(generation: u32) -> Self {
        StyleBloom {
            filter: Box::new(BloomFilter::new()),
            elements: vec![],
            generation: generation,
        }
    }

    pub fn filter(&self) -> &BloomFilter {
        &*self.filter
    }

    pub fn generation(&self) -> u32 {
        self.generation
    }

    pub fn maybe_pop<E>(&mut self, element: E)
        where E: TElement + MatchMethods
    {
        if self.elements.last() == Some(&element.as_node().to_unsafe()) {
            self.pop::<E>().unwrap();
        }
    }

    /// Push an element to the bloom filter, knowing that it's a child of the
    /// last element parent.
    pub fn push<E>(&mut self, element: E)
        where E: TElement + MatchMethods,
    {
        if cfg!(debug_assertions) {
            if self.elements.is_empty() {
                assert!(element.parent_element().is_none());
            }
        }
        element.insert_into_bloom_filter(&mut *self.filter);
        self.elements.push(element.as_node().to_unsafe());
    }

    /// Pop the last element in the bloom filter and return it.
    fn pop<E>(&mut self) -> Option<E>
        where E: TElement + MatchMethods,
    {
        let popped =
            self.elements.pop().map(|unsafe_node| {
                let parent = unsafe {
                    E::ConcreteNode::from_unsafe(&unsafe_node)
                };
                parent.as_element().unwrap()
            });
        if let Some(popped) = popped {
            popped.remove_from_bloom_filter(&mut self.filter);
        }

        popped
    }

    fn clear(&mut self) {
        self.filter.clear();
        self.elements.clear();
    }

    fn rebuild<E>(&mut self, mut element: E) -> usize
        where E: TElement + MatchMethods,
    {
        self.clear();

        while let Some(parent) = element.parent_element() {
            parent.insert_into_bloom_filter(&mut *self.filter);
            self.elements.push(parent.as_node().to_unsafe());
            element = parent;
        }

        // Put them in the order we expect, from root to `element`'s parent.
        self.elements.reverse();
        return self.elements.len();
    }

    /// In debug builds, asserts that all the parents of `element` are in the
    /// bloom filter.
    pub fn assert_complete<E>(&self, mut element: E)
        where E: TElement,
    {
        if cfg!(debug_assertions) {
            let mut checked = 0;
            while let Some(parent) = element.parent_element() {
                assert_eq!(parent.as_node().to_unsafe(),
                           self.elements[self.elements.len() - 1 - checked]);
                element = parent;
                checked += 1;
            }
            assert_eq!(checked, self.elements.len());
        }
    }

    /// Insert the parents of an element in the bloom filter, trying to recover
    /// the filter if the last element inserted doesn't match.
    ///
    /// Gets the element depth in the dom, to make it efficient, or if not
    /// provided always rebuilds the filter from scratch.
    ///
    /// Returns the new bloom filter depth.
    pub fn insert_parents_recovering<E>(&mut self,
                                        element: E,
                                        element_depth: Option<usize>,
                                        generation: u32)
                                        -> usize
        where E: TElement,
    {
        // Easy case, we're in a different restyle, or we're empty.
        if self.generation != generation || self.elements.is_empty() {
            self.generation = generation;
            return self.rebuild(element);
        }

        let parent_element = match element.parent_element() {
            Some(parent) => parent,
            None => {
                // Yay, another easy case.
                self.clear();
                return 0;
            }
        };

        let unsafe_parent = parent_element.as_node().to_unsafe();
        if self.elements.last() == Some(&unsafe_parent) {
            // Ta da, cache hit, we're all done.
            return self.elements.len();
        }

        let element_depth = match element_depth {
            Some(depth) => depth,
            // If we don't know the depth of `element`, we'd rather don't try
            // fixing up the bloom filter, since it's quadratic.
            None => {
                return self.rebuild(element);
            }
        };

        // We should've early exited above.
        debug_assert!(element_depth != 0,
                      "We should have already cleared the bloom filter");
        debug_assert!(!self.elements.is_empty(),
                      "How! We should've just rebuilt!");

        // Now the fun begins: We have the depth of the dom and the depth of the
        // last element inserted in the filter, let's try to find a common
        // parent.
        //
        // The current depth, that is, the depth of the last element inserted in
        // the bloom filter, is the number of elements _minus one_, that is: if
        // there's one element, it must be the root -> depth zero.
        let mut current_depth = self.elements.len() - 1;

        // If the filter represents an element too deep in the dom, we need to
        // pop ancestors.
        while current_depth > element_depth - 1 {
            self.pop::<E>().expect("Emilio is bad at math");
            current_depth -= 1;
        }

        // Now let's try to find a common parent in the bloom filter chain,
        // starting with parent_element.
        let mut common_parent = parent_element;
        let mut common_parent_depth = element_depth - 1;

        // Let's collect the parents we are going to need to insert once we've
        // found the common one.
        let mut parents_to_insert = vec![];

        // If the bloom filter still doesn't have enough elements, the common
        // parent is up in the dom.
        while common_parent_depth > current_depth {
            // TODO(emilio): Seems like we could insert parents here, then
            // reverse the slice.
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied");
            common_parent_depth -= 1;
        }

        // Now the two depths are the same.
        debug_assert_eq!(common_parent_depth, current_depth);

        // Happy case: The parents match, we only need to push the ancestors
        // we've collected and we'll never enter in this loop.
        //
        // Not-so-happy case: Parent's don't match, so we need to keep going up
        // until we find a common ancestor.
        while *self.elements.last().unwrap() != common_parent.as_node().to_unsafe() {
            parents_to_insert.push(common_parent);
            common_parent =
                common_parent.parent_element().expect("We were lied again?");
            self.pop::<E>().unwrap();
        }

        // Now the parents match, so insert the stack of elements we have been
        // collecting so far.
        for parent in parents_to_insert.into_iter().rev() {
            self.push(parent);
        }

        debug_assert_eq!(self.elements.len(), element_depth);

        // We're done! Easy.
        return self.elements.len();
    }
}
<|endoftext|>


<commit_before>use super::{Result, Specifier, SpecifierClass, SpecifierStack};
use std::rc::Rc;

pub fn spec(s: &str) -> Result<Rc<Specifier>> {
    Specifier::from_str(s)
}

impl SpecifierStack {
    #[allow(dead_code)]
    fn from_str(mut s: &str) -> Result<SpecifierStack> {
        let mut overlays = vec![];
        let addrtype;
        let addr;

        'a: loop {
            macro_rules! my {
                ($x:expr) => {
                    for pre in $x.get_prefixes() {
                        if s.starts_with(pre) {
                            let rest = &s[pre.len()..];
                            if $x.is_overlay() {
                                overlays.push(Rc::new($x) as Rc<SpecifierClass>);
                                s = rest;
                                continue 'a;
                            } else {
                                addr = rest.to_string();
                                addrtype = Rc::new($x) as Rc<SpecifierClass>;
                                break 'a;
                            }
                        }
                    }
                    Err(format!("Unknown address or overlay type of `{}`", s))?;
                };
            }
            list_of_all_specifier_classes!(my);
        }

        Ok(SpecifierStack { addr, addrtype, overlays })
    }
}

impl Specifier {
    #[allow(dead_code)]
    fn from_stack(st: SpecifierStack) -> Result<Rc<Specifier>> {
        let mut x = st.addrtype.construct("FIXME", st.addr.as_str())?;
        for overlay in st.overlays {
            x = overlay.construct_overlay(x)?;
        }
        Ok(x)
    }

    #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
    fn from_str(s: &str) -> Result<Rc<Specifier>> {
        #[cfg(not(feature = "ssl"))]
        {
            if s.starts_with("wss://") {
                Err("SSL is not compiled in. Use ws:// or get/make another Websocat build.\nYou can also try to workaround missing SSL by using ws-c:cmd:socat trick (see some ws-c: example)")?
            }
        }

        #[cfg(not(any(target_os = "linux", target_os = "android")))]
        {
            if s.starts_with("abstract") {
                warn!("Abstract-namespaced UNIX sockets are unlikely to be supported here");
            }
        }

        macro_rules! my {
            ($x:expr) => {
                for pre in $x.get_prefixes() {
                    if s.starts_with(pre) {
                        let rest = &s[pre.len()..];
                        return $x.construct(s, rest);
                    }
                }
            };
        }
        list_of_all_specifier_classes!(my);

        if s == "inetd-ws:" {
            return spec("ws-l:inetd:");
        } else if s.starts_with("l-ws-unix:") {
            return spec(&format!("ws-l:unix-l:{}", &s[10..]));
        } else if s.starts_with("l-ws-abstract:") {
            return spec(&format!("ws-l:abstract-l::{}", &s[14..]));
        }

        if s.starts_with("open:") {
            return Err("There is no `open:` specifier. Consider `open-async:` or `readfile:` or `writefile:` or `appendfile:`")?;
        }

        #[cfg(not(unix))]
        {
            if s.starts_with("unix") || s.starts_with("abstract") {
                Err("`unix*:` or `abstract*:` are not supported in this Websocat build")?
            }
        }

        #[cfg(not(feature = "tokio-process"))]
        {
            if s.starts_with("sh-c:") {
                Err("`sh-c:` is not supported in this Websocat build")?
            } else if s.starts_with("exec:") {
                Err("`exec:` is not supported in this Websocat build")?
            }
        }

        error!(
            "Invalid specifier string `{}`. See --long-help for the list of specifiers",
            s
        );
        Err("Wrong specifier")?
    }
}
<commit_msg>The switch.<commit_after>use super::{Result, Specifier, SpecifierClass, SpecifierStack};
use std::rc::Rc;

pub fn spec(s: &str) -> Result<Rc<Specifier>> {
    Specifier::from_stack(SpecifierStack::from_str(s)?)
}

impl SpecifierStack {
    fn from_str(mut s: &str) -> Result<SpecifierStack> {
        let mut overlays = vec![];
        let addrtype;
        let addr;

        'a: loop {
            macro_rules! my {
                ($x:expr) => {
                    for pre in $x.get_prefixes() {
                        if s.starts_with(pre) {
                            let rest = &s[pre.len()..];
                            if $x.is_overlay() {
                                overlays.push(Rc::new($x) as Rc<SpecifierClass>);
                                s = rest;
                                continue 'a;
                            } else {
                                addr = rest.to_string();
                                addrtype = Rc::new($x) as Rc<SpecifierClass>;
                                break 'a;
                            }
                        }
                    }
                    Err(format!("Unknown address or overlay type of `{}`", s))?;
                };
            }
            list_of_all_specifier_classes!(my);
        }

        Ok(SpecifierStack { addr, addrtype, overlays })
    }
}

impl Specifier {
    fn from_stack(st: SpecifierStack) -> Result<Rc<Specifier>> {
        let mut x = st.addrtype.construct("FIXME", st.addr.as_str())?;
        for overlay in st.overlays {
            x = overlay.construct_overlay(x)?;
        }
        Ok(x)
    }

    #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
    #[allow(dead_code)]
    fn from_str(s: &str) -> Result<Rc<Specifier>> {
        #[cfg(not(feature = "ssl"))]
        {
            if s.starts_with("wss://") {
                Err("SSL is not compiled in. Use ws:// or get/make another Websocat build.\nYou can also try to workaround missing SSL by using ws-c:cmd:socat trick (see some ws-c: example)")?
            }
        }

        #[cfg(not(any(target_os = "linux", target_os = "android")))]
        {
            if s.starts_with("abstract") {
                warn!("Abstract-namespaced UNIX sockets are unlikely to be supported here");
            }
        }

        macro_rules! my {
            ($x:expr) => {
                for pre in $x.get_prefixes() {
                    if s.starts_with(pre) {
                        let rest = &s[pre.len()..];
                        return $x.construct(s, rest);
                    }
                }
            };
        }
        list_of_all_specifier_classes!(my);

        if s == "inetd-ws:" {
            return spec("ws-l:inetd:");
        } else if s.starts_with("l-ws-unix:") {
            return spec(&format!("ws-l:unix-l:{}", &s[10..]));
        } else if s.starts_with("l-ws-abstract:") {
            return spec(&format!("ws-l:abstract-l::{}", &s[14..]));
        }

        if s.starts_with("open:") {
            return Err("There is no `open:` specifier. Consider `open-async:` or `readfile:` or `writefile:` or `appendfile:`")?;
        }

        #[cfg(not(unix))]
        {
            if s.starts_with("unix") || s.starts_with("abstract") {
                Err("`unix*:` or `abstract*:` are not supported in this Websocat build")?
            }
        }

        #[cfg(not(feature = "tokio-process"))]
        {
            if s.starts_with("sh-c:") {
                Err("`sh-c:` is not supported in this Websocat build")?
            } else if s.starts_with("exec:") {
                Err("`exec:` is not supported in this Websocat build")?
            }
        }

        error!(
            "Invalid specifier string `{}`. See --long-help for the list of specifiers",
            s
        );
        Err("Wrong specifier")?
    }
}
<|endoftext|>


<commit_before>pub type in_addr_t = u32;
pub type in_port_t = u16;
pub type pthread_key_t = usize;
pub type pthread_t = usize;
pub type sa_family_t = u8;
pub type socklen_t = usize;

s! {
    pub struct addrinfo {
        pub ai_flags: ::c_int,
        pub ai_family: ::c_int,
        pub ai_socktype: ::c_int,
        pub ai_protocol: ::c_int,
        pub ai_addrlen: ::socklen_t,
        pub ai_addr: *mut ::sockaddr,
        pub ai_canonname: *mut ::c_char,
        pub ai_next: *mut addrinfo,
    }

    pub struct in_addr {
        pub s_addr: in_addr_t,
    }

    pub struct in6_addr {
        pub s6_addr: [u8; 16],
    }

    pub struct pthread_attr_t {
        __detachstate: ::c_int,
        __stacksize: usize,
    }

    pub struct sockaddr {
        pub sa_family: sa_family_t,
        pub sa_data: [::c_char; 0],
    }

    pub struct sockaddr_in {
        pub sin_family: ::sa_family_t,
        pub sin_port: ::in_port_t,
        pub sin_addr: ::in_addr,
    }

    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: ::in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_storage {
        pub ss_family: ::sa_family_t,
        __ss_data: [u8; 32],
    }
}

pub const _SC_PAGESIZE: ::c_int = 54;

pub const AF_INET: ::c_int = 1;
pub const AF_INET6: ::c_int = 2;

pub const EACCES: ::c_int = 2;
pub const EADDRINUSE: ::c_int = 3;
pub const EADDRNOTAVAIL: ::c_int = 4;
pub const EAGAIN: ::c_int = 6;
pub const ECONNABORTED: ::c_int = 13;
pub const ECONNREFUSED: ::c_int = 14;
pub const ECONNRESET: ::c_int = 15;
pub const EEXIST: ::c_int = 20;
pub const EINTR: ::c_int = 27;
pub const EINVAL: ::c_int = 28;
pub const ENOENT: ::c_int = 44;
pub const ENOTCONN: ::c_int = 53;
pub const EPERM: ::c_int = 63;
pub const EPIPE: ::c_int = 64;
pub const ETIMEDOUT: ::c_int = 73;
pub const EWOULDBLOCK: ::c_int = EAGAIN;

pub const EAI_SYSTEM: ::c_int = 9;

pub const EXIT_FAILURE: ::c_int = 1;
pub const EXIT_SUCCESS: ::c_int = 0;

pub const PTHREAD_STACK_MIN: ::size_t = 1024;

pub const SOCK_DGRAM: ::c_int = 128;
pub const SOCK_STREAM: ::c_int = 130;

extern {
    pub fn arc4random_buf(buf: *const ::c_void, len: ::size_t);
    pub fn freeaddrinfo(res: *mut addrinfo);
    pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
    pub fn getaddrinfo(
        node: *const c_char,
        service: *const c_char,
        hints: *const addrinfo,
        res: *mut *mut addrinfo,
    ) -> ::c_int;
    pub fn getsockopt(
        sockfd: ::c_int,
        level: ::c_int,
        optname: ::c_int,
        optval: *mut ::c_void,
        optlen: *mut ::socklen_t,
    ) -> ::c_int;
    pub fn posix_memalign(
        memptr: *mut *mut ::c_void,
        align: ::size_t,
        size: ::size_t,
    ) -> ::c_int;
    pub fn pthread_attr_destroy(attr: *mut ::pthread_attr_t) -> ::c_int;
    pub fn pthread_attr_init(attr: *mut ::pthread_attr_t) -> ::c_int;
    pub fn pthread_attr_setstacksize(
        attr: *mut ::pthread_attr_t,
        stack_size: ::size_t,
    ) -> ::c_int;
    pub fn pthread_create(
        native: *mut ::pthread_t,
        attr: *const ::pthread_attr_t,
        f: extern fn(*mut ::c_void) -> *mut ::c_void,
        value: *mut ::c_void,
    ) -> ::c_int;
    pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
    pub fn pthread_getspecific(key: pthread_key_t) -> *mut ::c_void;
    pub fn pthread_join(
        native: ::pthread_t,
        value: *mut *mut ::c_void,
    ) -> ::c_int;
    pub fn pthread_key_create(
        key: *mut pthread_key_t,
        dtor: Option<unsafe extern fn(*mut ::c_void)>,
    ) -> ::c_int;
    pub fn pthread_key_delete(key: pthread_key_t) -> ::c_int;
    pub fn pthread_setspecific(
        key: pthread_key_t,
        value: *const ::c_void,
    ) -> ::c_int;
    pub fn send(
        socket: ::c_int,
        buf: *const ::c_void,
        len: ::size_t,
        flags: ::c_int,
    ) -> ::ssize_t;
    pub fn sysconf(name: ::c_int) -> ::c_long;
}

cfg_if! {
    if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else if #[cfg(any(target_arch = "x86"))] {
        mod x86;
        pub use self::x86::*;
    } else if #[cfg(any(target_arch = "x86_64"))] {
        mod x86_64;
        pub use self::x86_64::*;
    } else {
        // Unknown target_arch
    }
}
<commit_msg>Auto merge of #885 - EdSchouten:nprocessors, r=alexcrichton<commit_after>pub type in_addr_t = u32;
pub type in_port_t = u16;
pub type pthread_key_t = usize;
pub type pthread_t = usize;
pub type sa_family_t = u8;
pub type socklen_t = usize;

s! {
    pub struct addrinfo {
        pub ai_flags: ::c_int,
        pub ai_family: ::c_int,
        pub ai_socktype: ::c_int,
        pub ai_protocol: ::c_int,
        pub ai_addrlen: ::socklen_t,
        pub ai_addr: *mut ::sockaddr,
        pub ai_canonname: *mut ::c_char,
        pub ai_next: *mut addrinfo,
    }

    pub struct in_addr {
        pub s_addr: in_addr_t,
    }

    pub struct in6_addr {
        pub s6_addr: [u8; 16],
    }

    pub struct pthread_attr_t {
        __detachstate: ::c_int,
        __stacksize: usize,
    }

    pub struct sockaddr {
        pub sa_family: sa_family_t,
        pub sa_data: [::c_char; 0],
    }

    pub struct sockaddr_in {
        pub sin_family: ::sa_family_t,
        pub sin_port: ::in_port_t,
        pub sin_addr: ::in_addr,
    }

    pub struct sockaddr_in6 {
        pub sin6_family: sa_family_t,
        pub sin6_port: ::in_port_t,
        pub sin6_flowinfo: u32,
        pub sin6_addr: ::in6_addr,
        pub sin6_scope_id: u32,
    }

    pub struct sockaddr_storage {
        pub ss_family: ::sa_family_t,
        __ss_data: [u8; 32],
    }
}

pub const _SC_NPROCESSORS_ONLN: ::c_int = 52;
pub const _SC_PAGESIZE: ::c_int = 54;

pub const AF_INET: ::c_int = 1;
pub const AF_INET6: ::c_int = 2;

pub const EACCES: ::c_int = 2;
pub const EADDRINUSE: ::c_int = 3;
pub const EADDRNOTAVAIL: ::c_int = 4;
pub const EAGAIN: ::c_int = 6;
pub const ECONNABORTED: ::c_int = 13;
pub const ECONNREFUSED: ::c_int = 14;
pub const ECONNRESET: ::c_int = 15;
pub const EEXIST: ::c_int = 20;
pub const EINTR: ::c_int = 27;
pub const EINVAL: ::c_int = 28;
pub const ENOENT: ::c_int = 44;
pub const ENOTCONN: ::c_int = 53;
pub const EPERM: ::c_int = 63;
pub const EPIPE: ::c_int = 64;
pub const ETIMEDOUT: ::c_int = 73;
pub const EWOULDBLOCK: ::c_int = EAGAIN;

pub const EAI_SYSTEM: ::c_int = 9;

pub const EXIT_FAILURE: ::c_int = 1;
pub const EXIT_SUCCESS: ::c_int = 0;

pub const PTHREAD_STACK_MIN: ::size_t = 1024;

pub const SOCK_DGRAM: ::c_int = 128;
pub const SOCK_STREAM: ::c_int = 130;

extern {
    pub fn arc4random_buf(buf: *const ::c_void, len: ::size_t);
    pub fn freeaddrinfo(res: *mut addrinfo);
    pub fn gai_strerror(errcode: ::c_int) -> *const ::c_char;
    pub fn getaddrinfo(
        node: *const c_char,
        service: *const c_char,
        hints: *const addrinfo,
        res: *mut *mut addrinfo,
    ) -> ::c_int;
    pub fn getsockopt(
        sockfd: ::c_int,
        level: ::c_int,
        optname: ::c_int,
        optval: *mut ::c_void,
        optlen: *mut ::socklen_t,
    ) -> ::c_int;
    pub fn posix_memalign(
        memptr: *mut *mut ::c_void,
        align: ::size_t,
        size: ::size_t,
    ) -> ::c_int;
    pub fn pthread_attr_destroy(attr: *mut ::pthread_attr_t) -> ::c_int;
    pub fn pthread_attr_init(attr: *mut ::pthread_attr_t) -> ::c_int;
    pub fn pthread_attr_setstacksize(
        attr: *mut ::pthread_attr_t,
        stack_size: ::size_t,
    ) -> ::c_int;
    pub fn pthread_create(
        native: *mut ::pthread_t,
        attr: *const ::pthread_attr_t,
        f: extern fn(*mut ::c_void) -> *mut ::c_void,
        value: *mut ::c_void,
    ) -> ::c_int;
    pub fn pthread_detach(thread: ::pthread_t) -> ::c_int;
    pub fn pthread_getspecific(key: pthread_key_t) -> *mut ::c_void;
    pub fn pthread_join(
        native: ::pthread_t,
        value: *mut *mut ::c_void,
    ) -> ::c_int;
    pub fn pthread_key_create(
        key: *mut pthread_key_t,
        dtor: Option<unsafe extern fn(*mut ::c_void)>,
    ) -> ::c_int;
    pub fn pthread_key_delete(key: pthread_key_t) -> ::c_int;
    pub fn pthread_setspecific(
        key: pthread_key_t,
        value: *const ::c_void,
    ) -> ::c_int;
    pub fn send(
        socket: ::c_int,
        buf: *const ::c_void,
        len: ::size_t,
        flags: ::c_int,
    ) -> ::ssize_t;
    pub fn sysconf(name: ::c_int) -> ::c_long;
}

cfg_if! {
    if #[cfg(target_arch = "aarch64")] {
        mod aarch64;
        pub use self::aarch64::*;
    } else if #[cfg(any(target_arch = "x86"))] {
        mod x86;
        pub use self::x86::*;
    } else if #[cfg(any(target_arch = "x86_64"))] {
        mod x86_64;
        pub use self::x86_64::*;
    } else {
        // Unknown target_arch
    }
}
<|endoftext|>


<commit_before>use std::error;
use config;

use clap::{Arg, App, SubCommand, AppSettings};

use super::{DeployCommand, RunTaskCommand, ScheduleTaskPutCommand, ScheduleTaskDeleteCommand, ParamsExecCommand, ParamsGetCommand, ParamsPutCommand, ParamsDeleteCommand};

pub struct MainCommand {
}

impl MainCommand {

  pub fn run() -> Result<(), Box<error::Error>> {

    let matches = App::new("Racco")
                    .version("0.0.1")
                    .author("Daichi Sakai. <daisaru11@gmail.com>")
                    .about("Utilities to deploy ECS")
                    .arg(Arg::with_name("config")
                            .short("c")
                            .long("config")
                            .value_name("FILE")
                            .help("Configuration file")
                            .takes_value(true))
                    .subcommand(SubCommand::with_name("deploy")
                        .about("deploy ECS service")
                        .arg(Arg::with_name("NAME")
                            .help("name")
                            .index(1))
                    )
                    .subcommand(SubCommand::with_name("run-task")
                        .about("run ECS single task")
                        .arg(Arg::with_name("NAME")
                            .help("name")
                            .required(true)
                            .index(1))
                    )
                    .subcommand(SubCommand::with_name("schedule-task")
                        .about("deploy ECS task scheduled by Cloudwatch events")
                        .subcommand(SubCommand::with_name("put")
                            .about("put scheduled task")
                            .arg(Arg::with_name("NAME")
                                .help("name")
                                .required(true)
                                .index(1))
                        )
                        .subcommand(SubCommand::with_name("delete")
                            .about("delete scheduled task")
                            .arg(Arg::with_name("NAME")
                                .help("name")
                                .required(true)
                                .index(1))
                        )
                    )
                    .subcommand(SubCommand::with_name("params")
                        .about("manages parameters")
                        .subcommand(SubCommand::with_name("get")
                            .about("get parameters")
                            .arg(Arg::with_name("NAME")
                               .help("Parameter name")
                               .required(true)
                               .index(1))
                        )
                        .subcommand(SubCommand::with_name("put")
                            .about("put parameters")
                            .arg(Arg::with_name("NAME")
                               .help("Parameter name")
                               .required(true)
                               .index(1))
                            .arg(Arg::with_name("VALUE")
                               .help("Parameter value")
                               .required(true)
                               .index(2))
                        )
                        .subcommand(SubCommand::with_name("delete")
                            .about("delete parameters")
                            .arg(Arg::with_name("NAME")
                               .help("Parameter name")
                               .required(true)
                               .index(1))
                        )
                        .subcommand(SubCommand::with_name("exec")
                            .setting(AppSettings::AllowLeadingHyphen)
                            .about("executes a command with exported parameters as env variables")
                            .arg(Arg::with_name("PROGRAM")
                                .help("Program")
                                .required(true)
                                .index(1))
                            .arg(Arg::with_name("ARGS")
                                .help("Arguments")
                                .multiple(true)
                                .index(2)
                                )
                        )
                    )
                    .get_matches()
                    ;

    info!("start racco");

    let arg_conf = matches.value_of("config").unwrap_or("racco.yml");
    info!("config file: {}", arg_conf);

    match config::command::Config::from_file(arg_conf) {
        Err(error) => {
            error!("invalid config: {}", error)
        }
        Ok(config) => {

            // deploy
            if let Some(sub_matches) = matches.subcommand_matches("deploy") {

                info!("start deploy");

                let cmd = DeployCommand::from_args(&config, sub_matches);
                match cmd.run() {
                    Ok(_) => {
                    },
                    Err(error) => {
                        error!("deploy failed: {}", error)
                    }
                }

                info!("end deploy");
            }

            // run-task
            if let Some(sub_matches) = matches.subcommand_matches("run-task") {

                info!("start run-task");

                let cmd = RunTaskCommand::from_args(&config, sub_matches);
                match cmd.run() {
                    Ok(_) => {
                    },
                    Err(error) => {
                        error!("run-task failed: {}", error)
                    }
                }

                info!("end run-task");
            }

            // schedule-task
            if let Some(sub0_matches) = matches.subcommand_matches("schedule-task") {
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                    info!("start schedule-task put");

                    let cmd = ScheduleTaskPutCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("schedule-task put failed: {}", error)
                        }
                    }

                    info!("end schdule-task put");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                    info!("start schedule-task delete");

                    let cmd = ScheduleTaskDeleteCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("schedule-task delete failed: {}", error)
                        }
                    }

                    info!("end schedule-task delete");
                }
            }

            // params
            if let Some(sub0_matches) = matches.subcommand_matches("params") {
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("get") {
                    info!("start params get");

                    let cmd = ParamsGetCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params get failed: {}", error)
                        }
                    }

                    info!("end params get");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                    info!("start params put");

                    let cmd = ParamsPutCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params put failed: {}", error)
                        }
                    }

                    info!("end params put");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                    info!("start params delete");

                    let cmd = ParamsDeleteCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params delete failed: {}", error)
                        }
                    }

                    info!("end params delete");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("exec") {
                    info!("start params exec");

                    let cmd = ParamsExecCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params exec failed: {}", error)
                        }
                    }

                    info!("end params exec");
                }
            }

        },
    };

    info!("end racco");

    Ok(())
  }
}
<commit_msg>enrich command descriptions<commit_after>use std::error;
use config;

use clap::{Arg, App, SubCommand, AppSettings};

use super::{DeployCommand, RunTaskCommand, ScheduleTaskPutCommand, ScheduleTaskDeleteCommand, ParamsExecCommand, ParamsGetCommand, ParamsPutCommand, ParamsDeleteCommand};

pub struct MainCommand {
}

impl MainCommand {

  pub fn run() -> Result<(), Box<error::Error>> {

    let matches = App::new("Racco")
                    .version("0.1.0")
                    .author("Daichi Sakai. <daisaru11@gmail.com>")
                    .about("Deployment toolkit for AWS ECS")
                    .arg(Arg::with_name("config")
                            .short("c")
                            .long("config")
                            .value_name("FILE")
                            .help("Specifies configuration file")
                            .takes_value(true))
                    .subcommand(SubCommand::with_name("deploy")
                        .about("Deploys ECS service")
                        .arg(Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .index(1))
                    )
                    .subcommand(SubCommand::with_name("run-task")
                        .about("Runs single ESC task")
                        .arg(Arg::with_name("NAME")
                            .help("Name of the entry in config")
                            .required(true)
                            .index(1))
                    )
                    .subcommand(SubCommand::with_name("schedule-task")
                        .about("Deploys ECS task scheduled by Cloudwatch events")
                        .subcommand(SubCommand::with_name("put")
                            .about("Puts a scheduled task")
                            .arg(Arg::with_name("NAME")
                                .help("Name of the entry in config")
                                .required(true)
                                .index(1))
                        )
                        .subcommand(SubCommand::with_name("delete")
                            .about("Deletes a scheduled task")
                            .arg(Arg::with_name("NAME")
                                .help("Name of the entry in config")
                                .required(true)
                                .index(1))
                        )
                    )
                    .subcommand(SubCommand::with_name("params")
                        .about("Manages parameters")
                        .subcommand(SubCommand::with_name("get")
                            .about("Gets a parameter")
                            .arg(Arg::with_name("NAME")
                               .help("Name of the parameter")
                               .required(true)
                               .index(1))
                        )
                        .subcommand(SubCommand::with_name("put")
                            .about("Puts a parameter")
                            .arg(Arg::with_name("NAME")
                               .help("Name of the parameter")
                               .required(true)
                               .index(1))
                            .arg(Arg::with_name("VALUE")
                               .help("Value of the parameter")
                               .required(true)
                               .index(2))
                        )
                        .subcommand(SubCommand::with_name("delete")
                            .about("Deletes a parameter")
                            .arg(Arg::with_name("NAME")
                               .help("Name of the parameter")
                               .required(true)
                               .index(1))
                        )
                        .subcommand(SubCommand::with_name("exec")
                            .setting(AppSettings::AllowLeadingHyphen)
                            .about("Executes a command with the exported parameters as env variables")
                            .arg(Arg::with_name("PROGRAM")
                                .help("Program to be executed")
                                .required(true)
                                .index(1))
                            .arg(Arg::with_name("ARGS")
                                .help("Arguments passed to the program")
                                .multiple(true)
                                .index(2)
                                )
                        )
                    )
                    .get_matches()
                    ;

    info!("start racco");

    let arg_conf = matches.value_of("config").unwrap_or("racco.yml");
    info!("config file: {}", arg_conf);

    match config::command::Config::from_file(arg_conf) {
        Err(error) => {
            error!("invalid config: {}", error)
        }
        Ok(config) => {

            // deploy
            if let Some(sub_matches) = matches.subcommand_matches("deploy") {

                info!("start deploy");

                let cmd = DeployCommand::from_args(&config, sub_matches);
                match cmd.run() {
                    Ok(_) => {
                    },
                    Err(error) => {
                        error!("deploy failed: {}", error)
                    }
                }

                info!("end deploy");
            }

            // run-task
            if let Some(sub_matches) = matches.subcommand_matches("run-task") {

                info!("start run-task");

                let cmd = RunTaskCommand::from_args(&config, sub_matches);
                match cmd.run() {
                    Ok(_) => {
                    },
                    Err(error) => {
                        error!("run-task failed: {}", error)
                    }
                }

                info!("end run-task");
            }

            // schedule-task
            if let Some(sub0_matches) = matches.subcommand_matches("schedule-task") {
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                    info!("start schedule-task put");

                    let cmd = ScheduleTaskPutCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("schedule-task put failed: {}", error)
                        }
                    }

                    info!("end schdule-task put");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                    info!("start schedule-task delete");

                    let cmd = ScheduleTaskDeleteCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("schedule-task delete failed: {}", error)
                        }
                    }

                    info!("end schedule-task delete");
                }
            }

            // params
            if let Some(sub0_matches) = matches.subcommand_matches("params") {
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("get") {
                    info!("start params get");

                    let cmd = ParamsGetCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params get failed: {}", error)
                        }
                    }

                    info!("end params get");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("put") {
                    info!("start params put");

                    let cmd = ParamsPutCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params put failed: {}", error)
                        }
                    }

                    info!("end params put");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("delete") {
                    info!("start params delete");

                    let cmd = ParamsDeleteCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params delete failed: {}", error)
                        }
                    }

                    info!("end params delete");
                }
                if let Some(sub1_matches) = sub0_matches.subcommand_matches("exec") {
                    info!("start params exec");

                    let cmd = ParamsExecCommand::from_args(&config, sub1_matches);
                    match cmd.run() {
                        Ok(_) => {
                        },
                        Err(error) => {
                            error!("params exec failed: {}", error)
                        }
                    }

                    info!("end params exec");
                }
            }

        },
    };

    info!("end racco");

    Ok(())
  }
}
<|endoftext|>


<commit_before>use rustc_serialize::base64::FromBase64;
use std::fs::File;
use std::path::Path;
use std::process::{Command, Stdio};
use std::io;
use std::io::{BufRead, Read, Write};
use std::iter;
use sodiumoxide::crypto::sign;

use tempdir;

use unpack;
use resolve;
use runlist;

impl runlist::RunList {
    pub fn run<W: Write>(&self, out: &mut W) -> io::Result<()> {
        match self.sort_dependencies() {
            Ok(deps) => {
                writeln!(out, "Running: {}", deps.iter().map(|p| p.name.clone()).collect::<Vec<String>>().join(", ")).unwrap();

                for package in deps {
                    try!(package.run(out));
                }

                Ok(())
            },
            Err(e) => {
                Err(io::Error::new(io::ErrorKind::InvalidData, e.message()))
            }
        }
    }
}

fn run_command<P: AsRef<Path>, W: Write>(out: &mut W, main: &str, dir: P, env: &runlist::Env) -> io::Result<()>{
    let mut command = Command::new(main);
    command.current_dir(dir);
    //command.env_clear();
    for (k, v) in env {
        command.env(k,v);
    }
    command.stdout(Stdio::piped());
    command.stderr(Stdio::inherit()); // TODO: is this right?
    let mut child = match command.spawn() {
        Ok(v) => v,
        Err(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("error running {}: {}", main, e)));
        }
    };

    if let Some(ref mut stdout) = child.stdout {
        for line in io::BufReader::new(stdout).lines() {
            writeln!(out, "{}", try!(line)).unwrap();
        }
    }

    let status = try!(child.wait());

    if ! status.success() {
        Err(io::Error::new(io::ErrorKind::Other, format!("{} returned {}", main, status)))
    } else {
        Ok(())
    }
}

impl resolve::Executable {
    pub fn run<W: Write>(&self, out: &mut W) -> io::Result<()> {
        let text = format!("##    Running Package {}    ##", self.name);
        let line = iter::repeat("#").take(text.len()).collect::<String>();
        writeln!(out, "\n{}\n{}\n{}\n", line, text, line).unwrap();
        run_command(out, self.main.to_str().unwrap(), &self.dir, &self.env)
    }
}

pub fn main<W: Write>(out: &mut W, pubkey_string: String, archive_string: String) -> i32 {
    let mut stderr = io::stderr();

    let mut input: Box<Read> = if archive_string != "" {
        Box::new(File::open(archive_string).unwrap())
    } else {
        Box::new(io::stdin())
    };

    // Create PublicKey
    let pubkey = sign::PublicKey::from_slice(&pubkey_string.as_bytes().from_base64().unwrap()).unwrap();

    // Create a place for the archive to be unpacked
    let tempdir = tempdir::TempDir::new("turboshell").unwrap().into_path(); // TODO: remove into_path
    //let basedir = &tempdir.path();
    let basedir = &tempdir; // todo remove

    let tarball_bytes = unpack::unpack(&mut input, pubkey).unwrap();
    unpack::explode(tarball_bytes.as_slice(), basedir);

    let runlist = match runlist::RunList::from_archive(basedir) {
        Ok(v) => v,
        Err(e) => {
            writeln!(&mut stderr, "error reading archive: {}", e).unwrap();
            return 1;
        }
    };

    if let Err(e) = runlist.run(out) {
        writeln!(&mut stderr, "error running archive: {}", e).unwrap();
        return 1;
    }

    return 0;
}
<commit_msg>improve error handling in tempdir creation<commit_after>use rustc_serialize::base64::FromBase64;
use std::fs::File;
use std::path::Path;
use std::process::{Command, Stdio};
use std::io;
use std::io::{BufRead, Read, Write};
use std::iter;
use sodiumoxide::crypto::sign;

use tempdir;

use unpack;
use resolve;
use runlist;

impl runlist::RunList {
    pub fn run<W: Write>(&self, out: &mut W) -> io::Result<()> {
        match self.sort_dependencies() {
            Ok(deps) => {
                writeln!(out, "Running: {}", deps.iter().map(|p| p.name.clone()).collect::<Vec<String>>().join(", ")).unwrap();

                for package in deps {
                    try!(package.run(out));
                }

                Ok(())
            },
            Err(e) => {
                Err(io::Error::new(io::ErrorKind::InvalidData, e.message()))
            }
        }
    }
}

fn run_command<P: AsRef<Path>, W: Write>(out: &mut W, main: &str, dir: P, env: &runlist::Env) -> io::Result<()>{
    let mut command = Command::new(main);
    command.current_dir(dir);
    //command.env_clear();
    for (k, v) in env {
        command.env(k,v);
    }
    command.stdout(Stdio::piped());
    command.stderr(Stdio::inherit()); // TODO: is this right?
    let mut child = match command.spawn() {
        Ok(v) => v,
        Err(e) => {
            return Err(io::Error::new(io::ErrorKind::Other, format!("error running {}: {}", main, e)));
        }
    };

    if let Some(ref mut stdout) = child.stdout {
        for line in io::BufReader::new(stdout).lines() {
            writeln!(out, "{}", try!(line)).unwrap();
        }
    }

    let status = try!(child.wait());

    if ! status.success() {
        Err(io::Error::new(io::ErrorKind::Other, format!("{} returned {}", main, status)))
    } else {
        Ok(())
    }
}

impl resolve::Executable {
    pub fn run<W: Write>(&self, out: &mut W) -> io::Result<()> {
        let text = format!("##    Running Package {}    ##", self.name);
        let line = iter::repeat("#").take(text.len()).collect::<String>();
        writeln!(out, "\n{}\n{}\n{}\n", line, text, line).unwrap();
        run_command(out, self.main.to_str().unwrap(), &self.dir, &self.env)
    }
}

pub fn main<W: Write>(out: &mut W, pubkey_string: String, archive_string: String) -> i32 {
    let mut stderr = io::stderr();

    let mut input: Box<Read> = if archive_string != "" {
        Box::new(File::open(archive_string).unwrap())
    } else {
        Box::new(io::stdin())
    };

    // Create PublicKey
    let pubkey = sign::PublicKey::from_slice(&pubkey_string.as_bytes().from_base64().unwrap()).unwrap();

    // Create a place for the archive to be unpacked
    let tempdir = match tempdir::TempDir::new("turboshell") {
        Ok(value) => value,
        Err(e) => {
            writeln!(&mut stderr, "error creating temp dir: {}", e).unwrap();
            return 1;
        }
    };

    let basedir = match tempdir.path().canonicalize() {
        Ok(value) => value,
        Err(e) => {
            writeln!(&mut stderr, "Can't canonicalize temp dir: {}", e).unwrap();
            return 1;
        }
    };

    let tarball_bytes = unpack::unpack(&mut input, pubkey).unwrap();
    unpack::explode(tarball_bytes.as_slice(), &basedir);

    let runlist = match runlist::RunList::from_archive(&basedir) {
        Ok(v) => v,
        Err(e) => {
            writeln!(&mut stderr, "error reading archive: {}", e).unwrap();
            return 1;
        }
    };

    if let Err(e) = runlist.run(out) {
        writeln!(&mut stderr, "error running archive: {}", e).unwrap();
        return 1;
    }

    return 0;
}
<|endoftext|>


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

#[derive(PartialEq)]
enum CharTypes {
    IsNumeric,
    IsAlphabetic,
    IsSpace,
    IsOther,
}

#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
    IsVariable,
    IsNumeric,
    IsFunction,
    IsOperation,
}

#[derive(Debug, Clone)]
pub struct Token {
    pub param: String,
    pub id: TokenType,
}

macro_rules! seq {
    ($($x:expr),+) => {
        [$($x,)+].iter().map(|&x| x).collect()
    }
}

impl Token {
    pub fn new(buffer: String, id: TokenType) -> Token {
        Token {
            param: buffer,
            id: id,
        }
    }
}

trait SpecTypes {
    fn get_type(&self) -> CharTypes;
}

impl SpecTypes for char {
    // get character type
    fn get_type(&self) -> CharTypes {
        if self.is_numeric() || *self == '.' {
            CharTypes::IsNumeric
        } else if self.is_alphabetic() {
            CharTypes::IsAlphabetic
        } else if *self == ' ' {
            CharTypes::IsSpace
        } else {
            CharTypes::IsOther
        }
    }
}

fn identify(item: &str) -> TokenType {
    // available functions
    let funcs: HashSet<&'static str> = seq!["print", "exit"];
    // get first character from string
    let chr = item.chars().nth(0).unwrap();
    // check character type
    if chr.is_numeric() {
        TokenType::IsNumeric
    } else if funcs.contains(item) {
        TokenType::IsFunction
    } else if chr.is_alphabetic() {
        TokenType::IsVariable
    } else {
        TokenType::IsOperation
    }
}

pub fn tokenize<'a>(buffer: &'a str) -> Vec<Token> {
    // unsafe copy function
    fn strcpy<'a>(buffer: &'a str, start: usize, stop: usize) -> &'a str {
        unsafe { buffer.slice_unchecked(start, stop) }
    }
    let mut stack: Vec<Token> = Vec::new();
    let mut last_pos: usize = 0;
    let mut curr_pos: usize = 0;
    let mut last_type = CharTypes::IsOther;
    for character in buffer.chars() {
        let curr_type = character.get_type();
        if curr_type != last_type {
            // ignore empty data
            if curr_pos - last_pos > 0 && last_type != CharTypes::IsSpace {
                let tmp = strcpy(buffer, last_pos, curr_pos);
                let token_type = identify(tmp);
                stack.push(Token::new(tmp.to_owned(), token_type));
            }
            last_pos = curr_pos;
            last_type = curr_type;
        }
        curr_pos += 1;
    }
    // add last token to stack
    if last_type != CharTypes::IsSpace {
        let tmp = strcpy(buffer, last_pos, curr_pos);
        let token_type = identify(tmp);
        stack.push(Token::new(tmp.to_owned(), token_type));
    }
    stack
}
<commit_msg>- add stack function<commit_after>use std::collections::HashSet;

#[derive(PartialEq)]
enum CharTypes {
    IsNumeric,
    IsAlphabetic,
    IsSpace,
    IsOther,
}

#[derive(Debug, PartialEq, Clone)]
pub enum TokenType {
    IsVariable,
    IsNumeric,
    IsFunction,
    IsOperation,
}

#[derive(Debug, Clone)]
pub struct Token {
    pub param: String,
    pub id: TokenType,
}

macro_rules! seq {
    ($($x:expr),+) => {
        [$($x,)+].iter().map(|&x| x).collect()
    }
}

impl Token {
    pub fn new(buffer: String, id: TokenType) -> Token {
        Token {
            param: buffer,
            id: id,
        }
    }
}

trait SpecTypes {
    fn get_type(&self) -> CharTypes;
}

impl SpecTypes for char {
    // get character type
    fn get_type(&self) -> CharTypes {
        if self.is_numeric() || *self == '.' {
            CharTypes::IsNumeric
        } else if self.is_alphabetic() {
            CharTypes::IsAlphabetic
        } else if *self == ' ' {
            CharTypes::IsSpace
        } else {
            CharTypes::IsOther
        }
    }
}

fn identify(item: &str) -> TokenType {
    // available functions
    let funcs: HashSet<&'static str> = seq!["print", "stack", "exit"];
    // get first character from string
    let chr = item.chars().nth(0).unwrap();
    // check character type
    if chr.is_numeric() {
        TokenType::IsNumeric
    } else if funcs.contains(item) {
        TokenType::IsFunction
    } else if chr.is_alphabetic() {
        TokenType::IsVariable
    } else {
        TokenType::IsOperation
    }
}

pub fn tokenize<'a>(buffer: &'a str) -> Vec<Token> {
    // unsafe copy function
    fn strcpy<'a>(buffer: &'a str, start: usize, stop: usize) -> &'a str {
        unsafe { buffer.slice_unchecked(start, stop) }
    }
    let mut stack: Vec<Token> = Vec::new();
    let mut last_pos: usize = 0;
    let mut curr_pos: usize = 0;
    let mut last_type = CharTypes::IsOther;
    for character in buffer.chars() {
        let curr_type = character.get_type();
        if curr_type != last_type {
            // ignore empty data
            if curr_pos - last_pos > 0 && last_type != CharTypes::IsSpace {
                let tmp = strcpy(buffer, last_pos, curr_pos);
                let token_type = identify(tmp);
                stack.push(Token::new(tmp.to_owned(), token_type));
            }
            last_pos = curr_pos;
            last_type = curr_type;
        }
        curr_pos += 1;
    }
    // add last token to stack
    if last_type != CharTypes::IsSpace {
        let tmp = strcpy(buffer, last_pos, curr_pos);
        let token_type = identify(tmp);
        stack.push(Token::new(tmp.to_owned(), token_type));
    }
    stack
}
<|endoftext|>


<commit_before>use error::{SassError, ErrorKind, Result};
use event::Event;
use sass::comment::SassComment;
use sass::rule::SassRule;
use sass::selector::SassSelector;
use sass::variable::SassVariable;
use top_level_event::TopLevelEvent;
use tokenizer_utils::*;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct Tokenizer<'a> {
    toker: Toker<'a>,
    state: State,
    sass_rule_stack: Vec<SassRule<'a>>,
    current_sass_rule_selectors_done: bool,
}

#[derive(PartialEq, Debug, Copy, Clone)]
enum State {
    OutsideRules,
    InVariable,
    InComment,
    InRule,
    InSelectors,
    InProperties,
    EndRule,
    Eof,
}

impl<'a> Tokenizer<'a> {
    pub fn new(inner_str: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            toker: Toker {
                inner_str: &inner_str,
                bytes: &inner_str.as_bytes(),
                offset: 0,
            },
            state: State::OutsideRules,
            sass_rule_stack: Vec::new(),
            current_sass_rule_selectors_done: false,
        }
    }

    fn limit(&self) -> usize {
        self.toker.limit()
    }

    fn start_something(&mut self) -> Result<Option<TopLevelEvent<'a>>> {
        let mut current_sass_rule = SassRule::new();
        self.current_sass_rule_selectors_done = false;

        self.pick_something();
        while self.state != State::OutsideRules {
            if self.state == State::Eof { return Ok(None) }

            // See if this works now that State is Copy
            // self.state = match self.state

            if self.state == State::InSelectors {
                let sel = self.next_selector();
                if sel.is_some() {
                    current_sass_rule.selectors.push(sel.unwrap());
                }
            } else if self.state == State::InProperties {
                let prop = self.next_property();
                if prop.is_some() {
                    current_sass_rule.children.push(prop.unwrap());
                }
            } else if self.state == State::EndRule {
                try!(self.toker.eat("}"));

                match self.sass_rule_stack.pop() {
                    Some(mut rule) => {
                        rule.children.push(Event::ChildRule(current_sass_rule));
                        current_sass_rule = rule;
                        self.pick_something();
                    },
                    None => self.state = State::OutsideRules,
                }
            } else if self.state == State::InRule {
                self.sass_rule_stack.push(current_sass_rule);
                current_sass_rule = SassRule::new();
                self.current_sass_rule_selectors_done = false;
                self.pick_something();
            } else if self.state == State::InVariable {
                return match self.next_variable() {
                    Ok(v) => Ok(Some(v)),
                    Err(e) => Err(e),
                }
            } else if self.state == State::InComment {
                let comment = self.next_comment();
                if comment.is_some() {
                    if self.sass_rule_stack.len() == 0 &&
                       current_sass_rule.selectors.len() == 0 &&
                       current_sass_rule.children.len() == 0 {
                           return Ok(Some(
                               TopLevelEvent::Comment(SassComment { comment: comment.unwrap() })
                           ))
                    } else {
                        current_sass_rule.children.push(comment.unwrap());
                        self.pick_something();
                    }
                } else {
                    return Ok(None)
                }
            } else {
                return Err(SassError {
                    kind: ErrorKind::TokenizerError,
                    message: format!(
                        "Current tokenization state: {:?}. Current sass rule = {:?}",
                        self.state,
                        current_sass_rule
                    ),
                })
            }
        }

        Ok(Some(TopLevelEvent::Rule(current_sass_rule)))
    }

    fn pick_something(&mut self) {
        self.toker.skip_leading_whitespace();

        if self.toker.at_eof() {
            self.state = State::Eof;
            return
        }

        let c = self.toker.bytes[self.toker.offset];

        if c == b'}' {
            self.state = State::EndRule;
            return
        }

        if c == b'$' {
            self.state = State::InVariable;
            return
        }

        if c == b'/' && (self.toker.offset + 1) < self.limit() {
            let d = self.toker.bytes[self.toker.offset + 1];
            if d == b'*' {
                self.state = State::InComment;
                return
            }
        }

        if self.current_sass_rule_selectors_done {
            self.state = State::InProperties;
            return
        }

        self.state = State::InSelectors;
    }

    fn next_comment(&mut self) -> Option<Event<'a>> {
        let comment_body_beginning = self.toker.offset;
        let mut i = comment_body_beginning + 2;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, isnt_asterisk);

            if self.toker.bytes[i+1] == b'/' {
                self.toker.offset = i + 2;
                return Some(Event::Comment(Borrowed(&self.toker.inner_str[comment_body_beginning..i + 2])))
            } else {
                i += 1;
            }
        }
        None
    }

    fn next_variable(&mut self) -> Result<TopLevelEvent<'a>> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.toker.offset;
        let mut i = name_beginning;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_name_char);
            let name_end = i;

            i += 1;
            self.toker.offset = i;
            self.toker.skip_leading_whitespace();

            let value_beginning = self.toker.offset;
            i = value_beginning;

            while i < self.limit() {
                i += self.toker.scan_while_or_end(i, isnt_semicolon);
                let value_end = i;
                self.toker.offset = i + 1;

                self.toker.skip_leading_whitespace();

                return Ok(TopLevelEvent::Variable(SassVariable{
                    name: Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                    value: Borrowed(&self.toker.inner_str[value_beginning..value_end]),
                }))
            }
        }
        self.toker.offset = self.limit();
        Err(SassError {
            kind: ErrorKind::ExpectedVariable,
            message: String::from(
                "Expected variable declaration and value; reached EOF instead."
            ),
        })
    }

    fn next_property(&mut self) -> Option<Event<'a>> {
        self.toker.skip_leading_whitespace();

        if self.toker.at_eof() {
            self.state = State::Eof;
            return None
        }

        let name_beginning = self.toker.offset;
        let mut i = name_beginning;

        let c = self.toker.bytes[i];
        if c == b'}' {
            self.state = State::EndRule;
            return None
        }

        let d = self.toker.bytes[i + 1];
        if c == b'/' && d == b'*' {
            self.state = State::InComment;
            return None
        }

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_name_char);

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.toker.bytes[i];
            if c == b'{' {
                self.state = State::InRule;
                return None
            }

            let name_end = i;

            i += 1;
            self.toker.offset = i;
            self.toker.skip_leading_whitespace();

            let value_beginning = self.toker.offset;
            i = value_beginning;

            while i < self.limit() {
                i += self.toker.scan_while_or_end(i, isnt_semicolon);
                let value_end = i;
                self.toker.offset = i + 1;

                self.toker.skip_leading_whitespace();

                if self.toker.bytes[name_beginning] == b'$' {
                    return Some(Event::Variable(SassVariable {
                        name: Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                        value: Borrowed(&self.toker.inner_str[value_beginning..value_end])
                    }))
                } else {
                    return Some(Event::UnevaluatedProperty(
                        Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                        Borrowed(&self.toker.inner_str[value_beginning..value_end]),
                    ))
                }
            }
        }
        self.toker.offset = self.limit();
        None
    }

    fn next_selector(&mut self) -> Option<SassSelector<'a>> {
        self.toker.skip_leading_whitespace();

        let beginning = self.toker.offset;
        let mut i = beginning;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_selector_char);
            let c = self.toker.bytes[i];

            if c == b':' {
                self.state = State::InProperties;
                return None
            }

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.toker.inner_str[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        if self.current_sass_rule_selectors_done {
                            self.state = State::InRule;
                            return None
                        } else {
                            self.current_sass_rule_selectors_done = true;
                            self.state = State::InProperties;
                        }
                    }
                    self.toker.offset = i + 1;
                    return Some(SassSelector::new(&self.toker.inner_str[beginning..end]))
                } else {
                    // only whitespace between commas
                    self.toker.offset += 1;
                    return self.next_selector()
                }
            }

            self.toker.offset = i;
            if i > beginning {
                return Some(SassSelector::new(&self.toker.inner_str[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.toker.offset = i;
            Some(SassSelector::new(&self.toker.inner_str[beginning..i]))
        } else {
            self.state = State::Eof;
            None
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<TopLevelEvent<'a>>;

    fn next(&mut self) -> Option<Result<TopLevelEvent<'a>>> {
        if !self.toker.at_eof() {
            return match self.start_something() {
                Ok(Some(t)) => Some(Ok(t)),
                Ok(None) => None,
                Err(e) => Some(Err(e)),
            }
        }
        None
    }
}
<commit_msg>Use eat method in more places<commit_after>use error::{SassError, ErrorKind, Result};
use event::Event;
use sass::comment::SassComment;
use sass::rule::SassRule;
use sass::selector::SassSelector;
use sass::variable::SassVariable;
use top_level_event::TopLevelEvent;
use tokenizer_utils::*;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct Tokenizer<'a> {
    toker: Toker<'a>,
    state: State,
    sass_rule_stack: Vec<SassRule<'a>>,
    current_sass_rule_selectors_done: bool,
}

#[derive(PartialEq, Debug, Copy, Clone)]
enum State {
    OutsideRules,
    InVariable,
    InComment,
    InRule,
    InSelectors,
    InProperties,
    EndRule,
    Eof,
}

impl<'a> Tokenizer<'a> {
    pub fn new(inner_str: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            toker: Toker {
                inner_str: &inner_str,
                bytes: &inner_str.as_bytes(),
                offset: 0,
            },
            state: State::OutsideRules,
            sass_rule_stack: Vec::new(),
            current_sass_rule_selectors_done: false,
        }
    }

    fn limit(&self) -> usize {
        self.toker.limit()
    }

    fn start_something(&mut self) -> Result<Option<TopLevelEvent<'a>>> {
        let mut current_sass_rule = SassRule::new();
        self.current_sass_rule_selectors_done = false;

        self.pick_something();
        while self.state != State::OutsideRules {
            if self.state == State::Eof { return Ok(None) }

            // See if this works now that State is Copy
            // self.state = match self.state

            if self.state == State::InSelectors {
                let sel = self.next_selector();
                if sel.is_some() {
                    current_sass_rule.selectors.push(sel.unwrap());
                }
            } else if self.state == State::InProperties {
                let prop = self.next_property();
                if prop.is_some() {
                    current_sass_rule.children.push(prop.unwrap());
                }
            } else if self.state == State::EndRule {
                try!(self.toker.eat("}"));

                match self.sass_rule_stack.pop() {
                    Some(mut rule) => {
                        rule.children.push(Event::ChildRule(current_sass_rule));
                        current_sass_rule = rule;
                        self.pick_something();
                    },
                    None => self.state = State::OutsideRules,
                }
            } else if self.state == State::InRule {
                self.sass_rule_stack.push(current_sass_rule);
                current_sass_rule = SassRule::new();
                self.current_sass_rule_selectors_done = false;
                self.pick_something();
            } else if self.state == State::InVariable {
                return match self.next_variable() {
                    Ok(v) => Ok(Some(v)),
                    Err(e) => Err(e),
                }
            } else if self.state == State::InComment {
                let comment = self.next_comment();
                if comment.is_some() {
                    if self.sass_rule_stack.len() == 0 &&
                       current_sass_rule.selectors.len() == 0 &&
                       current_sass_rule.children.len() == 0 {
                           return Ok(Some(
                               TopLevelEvent::Comment(SassComment { comment: comment.unwrap() })
                           ))
                    } else {
                        current_sass_rule.children.push(comment.unwrap());
                        self.pick_something();
                    }
                } else {
                    return Ok(None)
                }
            } else {
                return Err(SassError {
                    kind: ErrorKind::TokenizerError,
                    message: format!(
                        "Current tokenization state: {:?}. Current sass rule = {:?}",
                        self.state,
                        current_sass_rule
                    ),
                })
            }
        }

        Ok(Some(TopLevelEvent::Rule(current_sass_rule)))
    }

    fn pick_something(&mut self) {
        self.toker.skip_leading_whitespace();

        if self.toker.at_eof() {
            self.state = State::Eof;
            return
        }

        let c = self.toker.bytes[self.toker.offset];

        if c == b'}' {
            self.state = State::EndRule;
            return
        }

        if c == b'$' {
            self.state = State::InVariable;
            return
        }

        if c == b'/' && (self.toker.offset + 1) < self.limit() {
            let d = self.toker.bytes[self.toker.offset + 1];
            if d == b'*' {
                self.state = State::InComment;
                return
            }
        }

        if self.current_sass_rule_selectors_done {
            self.state = State::InProperties;
            return
        }

        self.state = State::InSelectors;
    }

    fn next_comment(&mut self) -> Option<Event<'a>> {
        let comment_body_beginning = self.toker.offset;
        let mut i = comment_body_beginning + 2;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, isnt_asterisk);
            self.toker.offset = i;

            if self.toker.eat("*/").is_ok() {
                return Some(
                    Event::Comment(Borrowed(
                        &self.toker.inner_str[comment_body_beginning..self.toker.offset]
                    ))
                )
            } else {
                i += 1;
            }
        }
        None
    }

    fn next_variable(&mut self) -> Result<TopLevelEvent<'a>> {
        // TODO: can parts of this be deduplicated with properties?
        let name_beginning = self.toker.offset;
        let mut i = name_beginning;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_name_char);
            let name_end = i;

            self.toker.offset = i;
            try!(self.toker.eat(":"));
            self.toker.skip_leading_whitespace();

            let value_beginning = self.toker.offset;
            i = value_beginning;

            while i < self.limit() {
                i += self.toker.scan_while_or_end(i, isnt_semicolon);
                let value_end = i;
                self.toker.offset = i;

                try!(self.toker.eat(";"));
                self.toker.skip_leading_whitespace();

                return Ok(TopLevelEvent::Variable(SassVariable{
                    name: Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                    value: Borrowed(&self.toker.inner_str[value_beginning..value_end]),
                }))
            }
        }
        self.toker.offset = self.limit();
        Err(SassError {
            kind: ErrorKind::ExpectedVariable,
            message: String::from(
                "Expected variable declaration and value; reached EOF instead."
            ),
        })
    }

    fn next_property(&mut self) -> Option<Event<'a>> {
        self.toker.skip_leading_whitespace();

        if self.toker.at_eof() {
            self.state = State::Eof;
            return None
        }

        let name_beginning = self.toker.offset;
        let mut i = name_beginning;

        let c = self.toker.bytes[i];
        if c == b'}' {
            self.state = State::EndRule;
            return None
        }

        let d = self.toker.bytes[i + 1];
        if c == b'/' && d == b'*' {
            self.state = State::InComment;
            return None
        }

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_name_char);

            // Inefficient since we already skipped the whitespace and we'll have to
            // do it again but oh well
            let c = self.toker.bytes[i];
            if c == b'{' {
                self.state = State::InRule;
                return None
            }

            let name_end = i;

            i += 1;
            self.toker.offset = i;
            self.toker.skip_leading_whitespace();

            let value_beginning = self.toker.offset;
            i = value_beginning;

            while i < self.limit() {
                i += self.toker.scan_while_or_end(i, isnt_semicolon);
                let value_end = i;
                self.toker.offset = i + 1;

                self.toker.skip_leading_whitespace();

                if self.toker.bytes[name_beginning] == b'$' {
                    return Some(Event::Variable(SassVariable {
                        name: Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                        value: Borrowed(&self.toker.inner_str[value_beginning..value_end])
                    }))
                } else {
                    return Some(Event::UnevaluatedProperty(
                        Borrowed(&self.toker.inner_str[name_beginning..name_end]),
                        Borrowed(&self.toker.inner_str[value_beginning..value_end]),
                    ))
                }
            }
        }
        self.toker.offset = self.limit();
        None
    }

    fn next_selector(&mut self) -> Option<SassSelector<'a>> {
        self.toker.skip_leading_whitespace();

        let beginning = self.toker.offset;
        let mut i = beginning;

        while i < self.limit() {
            i += self.toker.scan_while_or_end(i, valid_selector_char);
            let c = self.toker.bytes[i];

            if c == b':' {
                self.state = State::InProperties;
                return None
            }

            if c == b',' || c == b'{' {
                let n = scan_trailing_whitespace(&self.toker.inner_str[beginning..i]);
                let end = i - n;
                if end > beginning {
                    if c == b'{' {
                        if self.current_sass_rule_selectors_done {
                            self.state = State::InRule;
                            return None
                        } else {
                            self.current_sass_rule_selectors_done = true;
                            self.state = State::InProperties;
                        }
                    }
                    self.toker.offset = i + 1;
                    return Some(SassSelector::new(&self.toker.inner_str[beginning..end]))
                } else {
                    // only whitespace between commas
                    self.toker.offset += 1;
                    return self.next_selector()
                }
            }

            self.toker.offset = i;
            if i > beginning {
                return Some(SassSelector::new(&self.toker.inner_str[beginning..i]))
            }
            i += 1;
        }

        if i > beginning {
            self.toker.offset = i;
            Some(SassSelector::new(&self.toker.inner_str[beginning..i]))
        } else {
            self.state = State::Eof;
            None
        }
    }
}

impl<'a> Iterator for Tokenizer<'a> {
    type Item = Result<TopLevelEvent<'a>>;

    fn next(&mut self) -> Option<Result<TopLevelEvent<'a>>> {
        if !self.toker.at_eof() {
            return match self.start_something() {
                Ok(Some(t)) => Some(Ok(t)),
                Ok(None) => None,
                Err(e) => Some(Err(e)),
            }
        }
        None
    }
}
<|endoftext|>