<commit_before>// Copyright 2013-2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Parser and serializer for the [`application/x-www-form-urlencoded` syntax](
//! http://url.spec.whatwg.org/#application/x-www-form-urlencoded),
//! as used by HTML forms.
//!
//! Converts between a string (such as an URL’s query string)
//! and a sequence of (name, value) pairs.

use encoding::EncodingOverride;
use percent_encoding::{percent_encode_byte, percent_decode};
use std::borrow::{Borrow, Cow};
use std::str;

/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
/// into a iterator of (name, value) pairs.
///
/// Use `parse(input.as_bytes())` to parse a `&str` string.
///
/// The names and values are percent-decoded. For instance, `%23first=%25try%25` will be
/// converted to `[("#first", "%try%")]`.
#[inline]
pub fn parse(input: &[u8]) -> Parse {
    Parse {
        input: input,
        encoding: EncodingOverride::utf8(),
    }
}

/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
/// into a iterator of (name, value) pairs.
///
/// Use `parse(input.as_bytes())` to parse a `&str` string.
///
/// This function is only available if the `query_encoding`
/// [feature](http://doc.crates.io/manifest.html#the-features-section]) is enabled.
///
/// Arguments:
///
/// * `encoding_override`: The character encoding each name and values is decoded as
///    after percent-decoding. Defaults to UTF-8.
///    `EncodingRef` is defined in [rust-encoding](https://github.com/lifthrasiir/rust-encoding).
/// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
#[cfg(feature = "query_encoding")]
pub fn parse_with_encoding<'a>(input: &'a [u8],
                               encoding_override: Option<::encoding::EncodingRef>,
                               use_charset: bool)
                               -> Result<Parse<'a>, ()> {
    use std::ascii::AsciiExt;

    let mut encoding = EncodingOverride::from_opt_encoding(encoding_override);
    if !(encoding.is_utf8() || input.is_ascii()) {
        return Err(())
    }
    if use_charset {
        for sequence in input.split(|&b| b == b'&') {
            // No '+' in "_charset_" to replace with ' '.
            if sequence.starts_with(b"_charset_=") {
                let value = unsafe { &sequence.get_unchecked(b"_charset_=".len()..) };
                // Skip replacing '+' with ' ' in value since no encoding label contains either:
                // https://encoding.spec.whatwg.org/#names-and-labels
                if let Some(e) = EncodingOverride::lookup(value) {
                    encoding = e;
                    break
                }
            }
        }
    }
    Ok(Parse {
        input: input,
        encoding: encoding,
    })
}

/// The return type of `parse()`.
#[derive(Copy, Clone, Debug)]
pub struct Parse<'a> {
    input: &'a [u8],
    encoding: EncodingOverride,
}

impl<'a> Iterator for Parse<'a> {
    type Item = (Cow<'a, str>, Cow<'a, str>);

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if self.input.is_empty() {
                return None
            }
            let mut split2 = self.input.splitn(2, |&b| b == b'&');
            let sequence = split2.next().unwrap();
            self.input = split2.next().unwrap_or(&[][..]);
            if sequence.is_empty() {
                continue
            }
            let mut split2 = sequence.splitn(2, |&b| b == b'=');
            let name = split2.next().unwrap();
            let value = split2.next().unwrap_or(&[][..]);
            return Some((
                decode(name, self.encoding),
                decode(value, self.encoding),
            ))
        }
    }
}

fn decode(input: &[u8], encoding: EncodingOverride) -> Cow<str> {
    let replaced = replace_plus(input);
    encoding.decode(match percent_decode(&replaced).if_any() {
        Some(vec) => Cow::Owned(vec),
        None => replaced,
    })
}

/// Replace b'+' with b' '
fn replace_plus(input: &[u8]) -> Cow<[u8]> {
    match input.iter().position(|&b| b == b'+') {
        None => Cow::Borrowed(input),
        Some(first_position) => {
            let mut replaced = input.to_owned();
            unsafe {
                *replaced.get_unchecked_mut(first_position) = b' ';
                for byte in replaced.get_unchecked_mut(first_position + 1..) {
                    if *byte == b'+' {
                        *byte = b' ';
                    }
                }
            }
            Cow::Owned(replaced)
        }
    }
}

impl<'a> Parse<'a> {
    /// Return a new iterator that yields pairs of `String` instead of pairs of `Cow<str>`.
    pub fn into_owned(self) -> ParseIntoOwned<'a> {
        ParseIntoOwned { inner: self }
    }
}

/// Like `Parse`, but yields pairs of `String` instead of pairs of `Cow<str>`.
#[derive(Debug)]
pub struct ParseIntoOwned<'a> {
    inner: Parse<'a>
}

impl<'a> Iterator for ParseIntoOwned<'a> {
    type Item = (String, String);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(k, v)| (k.into_owned(), v.into_owned()))
    }
}

/// The [`application/x-www-form-urlencoded` byte serializer](
/// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer).
///
/// Return an iterator of `&str` slices.
pub fn byte_serialize(input: &[u8]) -> ByteSerialize {
    ByteSerialize {
        bytes: input,
    }
}

/// Return value of `byte_serialize()`.
#[derive(Debug)]
pub struct ByteSerialize<'a> {
    bytes: &'a [u8],
}

fn byte_serialized_unchanged(byte: u8) -> bool {
    matches!(byte, b'*' | b'-' | b'.' | b'0' ... b'9' | b'A' ... b'Z' | b'_' | b'a' ... b'z')
}

impl<'a> Iterator for ByteSerialize<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if let Some((&first, tail)) = self.bytes.split_first() {
            if !byte_serialized_unchanged(first) {
                self.bytes = tail;
                return Some(if first == b' ' { "+" } else { percent_encode_byte(first) })
            }
            let position = tail.iter().position(|&b| !byte_serialized_unchanged(b));
            let (unchanged_slice, remaining) = match position {
                // 1 for first_byte + i unchanged in tail
                Some(i) => self.bytes.split_at(1 + i),
                None => (self.bytes, &[][..]),
            };
            self.bytes = remaining;
            Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.bytes.is_empty() {
            (0, Some(0))
        } else {
            (1, Some(self.bytes.len()))
        }
    }
}

/// The [`application/x-www-form-urlencoded` serializer](
/// https://url.spec.whatwg.org/#concept-urlencoded-serializer).
#[derive(Debug)]
pub struct Serializer<T: Target> {
    target: Option<T>,
    start_position: usize,
    encoding: EncodingOverride,
}

pub trait Target {
    fn as_mut_string(&mut self) -> &mut String;
    fn finish(self) -> Self::Finished;
    type Finished;
}

impl Target for String {
    fn as_mut_string(&mut self) -> &mut String { self }
    fn finish(self) -> Self { self }
    type Finished = Self;
}

impl<'a> Target for &'a mut String {
    fn as_mut_string(&mut self) -> &mut String { &mut **self }
    fn finish(self) -> Self { self }
    type Finished = Self;
}

// `as_mut_string` string here exposes the internal serialization of an `Url`,
// which should not be exposed to users.
// We achieve that by not giving users direct access to `UrlQuery`:
// * Its fields are private
//   (and so can not be constructed with struct literal syntax outside of this crate),
// * It has no constructor
// * It is only visible (on the type level) to users in the return type of
//   `Url::query_pairs_mut` which is `Serializer<UrlQuery>`
// * `Serializer` keeps its target in a private field
// * Unlike in other `Target` impls, `UrlQuery::finished` does not return `Self`.
impl<'a> Target for ::UrlQuery<'a> {
    fn as_mut_string(&mut self) -> &mut String { &mut self.url.serialization }
    fn finish(self) -> &'a mut ::Url { self.url }
    type Finished = &'a mut ::Url;
}

impl<T: Target> Serializer<T> {
    /// Create a new `application/x-www-form-urlencoded` serializer for the given target.
    ///
    /// If the target is non-empty,
    /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
    pub fn new(target: T) -> Self {
        Self::for_suffix(target, 0)
    }

    /// Create a new `application/x-www-form-urlencoded` serializer
    /// for a suffix of the given target.
    ///
    /// If that suffix is non-empty,
    /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
    pub fn for_suffix(mut target: T, start_position: usize) -> Self {
        &target.as_mut_string()[start_position..];  // Panic if out of bounds
        Serializer {
            target: Some(target),
            start_position: start_position,
            encoding: EncodingOverride::utf8(),
        }
    }

    /// Remove any existing name/value pair.
    ///
    /// Panics if called after `.finish()`.
    pub fn clear(&mut self) -> &mut Self {
        string(&mut self.target).truncate(self.start_position);
        self
    }

    /// Set the character encoding to be used for names and values before percent-encoding.
    #[cfg(feature = "query_encoding")]
    pub fn encoding_override(&mut self, new: Option<::encoding::EncodingRef>) -> &mut Self {
        self.encoding = EncodingOverride::from_opt_encoding(new).to_output_encoding();
        self
    }

    /// Serialize and append a name/value pair.
    ///
    /// Panics if called after `.finish()`.
    pub fn append_pair(&mut self, name: &str, value: &str) -> &mut Self {
        append_pair(string(&mut self.target), self.start_position, self.encoding, name, value);
        self
    }

    /// Serialize and append a number of name/value pairs.
    ///
    /// This simply calls `append_pair` repeatedly.
    /// This can be more convenient, so the user doesn’t need to introduce a block
    /// to limit the scope of `Serializer`’s borrow of its string.
    ///
    /// Panics if called after `.finish()`.
    pub fn extend_pairs<I, K, V>(&mut self, iter: I) -> &mut Self
    where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef<str>, V: AsRef<str> {
        {
            let string = string(&mut self.target);
            for pair in iter {
                let &(ref k, ref v) = pair.borrow();
                append_pair(string, self.start_position, self.encoding, k.as_ref(), v.as_ref());
            }
        }
        self
    }

    /// Add a name/value pair whose name is `_charset_`
    /// and whose value is the character encoding’s name.
    /// (See the `encoding_override()` method.)
    ///
    /// Panics if called after `.finish()`.
    #[cfg(feature = "query_encoding")]
    pub fn append_charset(&mut self) -> &mut Self {
        {
            let string = string(&mut self.target);
            append_separator_if_needed(string, self.start_position);
            string.push_str("_charset_=");
            string.push_str(self.encoding.name());
        }
        self
    }

    /// If this serializer was constructed with a string, take and return that string.
    ///
    /// ```rust
    /// use url::form_urlencoded;
    /// let encoded: String = form_urlencoded::Serializer::new(String::new())
    ///     .append_pair("foo", "bar & baz")
    ///     .append_pair("saison", "Été+hiver")
    ///     .finish();
    /// assert_eq!(encoded, "foo=bar+%26+baz&saison=%C3%89t%C3%A9%2Bhiver");
    /// ```
    ///
    /// Panics if called more than once.
    pub fn finish(&mut self) -> T::Finished {
        self.target.take().expect("url::form_urlencoded::Serializer double finish").finish()
    }
}

fn append_separator_if_needed(string: &mut String, start_position: usize) {
    if string.len() > start_position {
        string.push('&')
    }
}

fn string<T: Target>(target: &mut Option<T>) -> &mut String {
    target.as_mut().expect("url::form_urlencoded::Serializer finished").as_mut_string()
}

fn append_pair(string: &mut String, start_position: usize, encoding: EncodingOverride,
               name: &str, value: &str) {
    append_separator_if_needed(string, start_position);
    string.extend(byte_serialize(&encoding.encode(name.into())));
    string.push('=');
    string.extend(byte_serialize(&encoding.encode(value.into())));
}
<commit_msg>Revert "Remove unnecessary bounds checking in a couple places."<commit_after>// Copyright 2013-2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Parser and serializer for the [`application/x-www-form-urlencoded` syntax](
//! http://url.spec.whatwg.org/#application/x-www-form-urlencoded),
//! as used by HTML forms.
//!
//! Converts between a string (such as an URL’s query string)
//! and a sequence of (name, value) pairs.

use encoding::EncodingOverride;
use percent_encoding::{percent_encode_byte, percent_decode};
use std::borrow::{Borrow, Cow};
use std::str;

/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
/// into a iterator of (name, value) pairs.
///
/// Use `parse(input.as_bytes())` to parse a `&str` string.
///
/// The names and values are percent-decoded. For instance, `%23first=%25try%25` will be
/// converted to `[("#first", "%try%")]`.
#[inline]
pub fn parse(input: &[u8]) -> Parse {
    Parse {
        input: input,
        encoding: EncodingOverride::utf8(),
    }
}

/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
/// into a iterator of (name, value) pairs.
///
/// Use `parse(input.as_bytes())` to parse a `&str` string.
///
/// This function is only available if the `query_encoding`
/// [feature](http://doc.crates.io/manifest.html#the-features-section]) is enabled.
///
/// Arguments:
///
/// * `encoding_override`: The character encoding each name and values is decoded as
///    after percent-decoding. Defaults to UTF-8.
///    `EncodingRef` is defined in [rust-encoding](https://github.com/lifthrasiir/rust-encoding).
/// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
#[cfg(feature = "query_encoding")]
pub fn parse_with_encoding<'a>(input: &'a [u8],
                               encoding_override: Option<::encoding::EncodingRef>,
                               use_charset: bool)
                               -> Result<Parse<'a>, ()> {
    use std::ascii::AsciiExt;

    let mut encoding = EncodingOverride::from_opt_encoding(encoding_override);
    if !(encoding.is_utf8() || input.is_ascii()) {
        return Err(())
    }
    if use_charset {
        for sequence in input.split(|&b| b == b'&') {
            // No '+' in "_charset_" to replace with ' '.
            if sequence.starts_with(b"_charset_=") {
                let value = &sequence[b"_charset_=".len()..];
                // Skip replacing '+' with ' ' in value since no encoding label contains either:
                // https://encoding.spec.whatwg.org/#names-and-labels
                if let Some(e) = EncodingOverride::lookup(value) {
                    encoding = e;
                    break
                }
            }
        }
    }
    Ok(Parse {
        input: input,
        encoding: encoding,
    })
}

/// The return type of `parse()`.
#[derive(Copy, Clone, Debug)]
pub struct Parse<'a> {
    input: &'a [u8],
    encoding: EncodingOverride,
}

impl<'a> Iterator for Parse<'a> {
    type Item = (Cow<'a, str>, Cow<'a, str>);

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            if self.input.is_empty() {
                return None
            }
            let mut split2 = self.input.splitn(2, |&b| b == b'&');
            let sequence = split2.next().unwrap();
            self.input = split2.next().unwrap_or(&[][..]);
            if sequence.is_empty() {
                continue
            }
            let mut split2 = sequence.splitn(2, |&b| b == b'=');
            let name = split2.next().unwrap();
            let value = split2.next().unwrap_or(&[][..]);
            return Some((
                decode(name, self.encoding),
                decode(value, self.encoding),
            ))
        }
    }
}

fn decode(input: &[u8], encoding: EncodingOverride) -> Cow<str> {
    let replaced = replace_plus(input);
    encoding.decode(match percent_decode(&replaced).if_any() {
        Some(vec) => Cow::Owned(vec),
        None => replaced,
    })
}

/// Replace b'+' with b' '
fn replace_plus(input: &[u8]) -> Cow<[u8]> {
    match input.iter().position(|&b| b == b'+') {
        None => Cow::Borrowed(input),
        Some(first_position) => {
            let mut replaced = input.to_owned();
            replaced[first_position] = b' ';
            for byte in &mut replaced[first_position + 1..] {
                if *byte == b'+' {
                    *byte = b' ';
                }
            }
            Cow::Owned(replaced)
        }
    }
}

impl<'a> Parse<'a> {
    /// Return a new iterator that yields pairs of `String` instead of pairs of `Cow<str>`.
    pub fn into_owned(self) -> ParseIntoOwned<'a> {
        ParseIntoOwned { inner: self }
    }
}

/// Like `Parse`, but yields pairs of `String` instead of pairs of `Cow<str>`.
#[derive(Debug)]
pub struct ParseIntoOwned<'a> {
    inner: Parse<'a>
}

impl<'a> Iterator for ParseIntoOwned<'a> {
    type Item = (String, String);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(k, v)| (k.into_owned(), v.into_owned()))
    }
}

/// The [`application/x-www-form-urlencoded` byte serializer](
/// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer).
///
/// Return an iterator of `&str` slices.
pub fn byte_serialize(input: &[u8]) -> ByteSerialize {
    ByteSerialize {
        bytes: input,
    }
}

/// Return value of `byte_serialize()`.
#[derive(Debug)]
pub struct ByteSerialize<'a> {
    bytes: &'a [u8],
}

fn byte_serialized_unchanged(byte: u8) -> bool {
    matches!(byte, b'*' | b'-' | b'.' | b'0' ... b'9' | b'A' ... b'Z' | b'_' | b'a' ... b'z')
}

impl<'a> Iterator for ByteSerialize<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if let Some((&first, tail)) = self.bytes.split_first() {
            if !byte_serialized_unchanged(first) {
                self.bytes = tail;
                return Some(if first == b' ' { "+" } else { percent_encode_byte(first) })
            }
            let position = tail.iter().position(|&b| !byte_serialized_unchanged(b));
            let (unchanged_slice, remaining) = match position {
                // 1 for first_byte + i unchanged in tail
                Some(i) => self.bytes.split_at(1 + i),
                None => (self.bytes, &[][..]),
            };
            self.bytes = remaining;
            Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
        } else {
            None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        if self.bytes.is_empty() {
            (0, Some(0))
        } else {
            (1, Some(self.bytes.len()))
        }
    }
}

/// The [`application/x-www-form-urlencoded` serializer](
/// https://url.spec.whatwg.org/#concept-urlencoded-serializer).
#[derive(Debug)]
pub struct Serializer<T: Target> {
    target: Option<T>,
    start_position: usize,
    encoding: EncodingOverride,
}

pub trait Target {
    fn as_mut_string(&mut self) -> &mut String;
    fn finish(self) -> Self::Finished;
    type Finished;
}

impl Target for String {
    fn as_mut_string(&mut self) -> &mut String { self }
    fn finish(self) -> Self { self }
    type Finished = Self;
}

impl<'a> Target for &'a mut String {
    fn as_mut_string(&mut self) -> &mut String { &mut **self }
    fn finish(self) -> Self { self }
    type Finished = Self;
}

// `as_mut_string` string here exposes the internal serialization of an `Url`,
// which should not be exposed to users.
// We achieve that by not giving users direct access to `UrlQuery`:
// * Its fields are private
//   (and so can not be constructed with struct literal syntax outside of this crate),
// * It has no constructor
// * It is only visible (on the type level) to users in the return type of
//   `Url::query_pairs_mut` which is `Serializer<UrlQuery>`
// * `Serializer` keeps its target in a private field
// * Unlike in other `Target` impls, `UrlQuery::finished` does not return `Self`.
impl<'a> Target for ::UrlQuery<'a> {
    fn as_mut_string(&mut self) -> &mut String { &mut self.url.serialization }
    fn finish(self) -> &'a mut ::Url { self.url }
    type Finished = &'a mut ::Url;
}

impl<T: Target> Serializer<T> {
    /// Create a new `application/x-www-form-urlencoded` serializer for the given target.
    ///
    /// If the target is non-empty,
    /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
    pub fn new(target: T) -> Self {
        Self::for_suffix(target, 0)
    }

    /// Create a new `application/x-www-form-urlencoded` serializer
    /// for a suffix of the given target.
    ///
    /// If that suffix is non-empty,
    /// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
    pub fn for_suffix(mut target: T, start_position: usize) -> Self {
        &target.as_mut_string()[start_position..];  // Panic if out of bounds
        Serializer {
            target: Some(target),
            start_position: start_position,
            encoding: EncodingOverride::utf8(),
        }
    }

    /// Remove any existing name/value pair.
    ///
    /// Panics if called after `.finish()`.
    pub fn clear(&mut self) -> &mut Self {
        string(&mut self.target).truncate(self.start_position);
        self
    }

    /// Set the character encoding to be used for names and values before percent-encoding.
    #[cfg(feature = "query_encoding")]
    pub fn encoding_override(&mut self, new: Option<::encoding::EncodingRef>) -> &mut Self {
        self.encoding = EncodingOverride::from_opt_encoding(new).to_output_encoding();
        self
    }

    /// Serialize and append a name/value pair.
    ///
    /// Panics if called after `.finish()`.
    pub fn append_pair(&mut self, name: &str, value: &str) -> &mut Self {
        append_pair(string(&mut self.target), self.start_position, self.encoding, name, value);
        self
    }

    /// Serialize and append a number of name/value pairs.
    ///
    /// This simply calls `append_pair` repeatedly.
    /// This can be more convenient, so the user doesn’t need to introduce a block
    /// to limit the scope of `Serializer`’s borrow of its string.
    ///
    /// Panics if called after `.finish()`.
    pub fn extend_pairs<I, K, V>(&mut self, iter: I) -> &mut Self
    where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef<str>, V: AsRef<str> {
        {
            let string = string(&mut self.target);
            for pair in iter {
                let &(ref k, ref v) = pair.borrow();
                append_pair(string, self.start_position, self.encoding, k.as_ref(), v.as_ref());
            }
        }
        self
    }

    /// Add a name/value pair whose name is `_charset_`
    /// and whose value is the character encoding’s name.
    /// (See the `encoding_override()` method.)
    ///
    /// Panics if called after `.finish()`.
    #[cfg(feature = "query_encoding")]
    pub fn append_charset(&mut self) -> &mut Self {
        {
            let string = string(&mut self.target);
            append_separator_if_needed(string, self.start_position);
            string.push_str("_charset_=");
            string.push_str(self.encoding.name());
        }
        self
    }

    /// If this serializer was constructed with a string, take and return that string.
    ///
    /// ```rust
    /// use url::form_urlencoded;
    /// let encoded: String = form_urlencoded::Serializer::new(String::new())
    ///     .append_pair("foo", "bar & baz")
    ///     .append_pair("saison", "Été+hiver")
    ///     .finish();
    /// assert_eq!(encoded, "foo=bar+%26+baz&saison=%C3%89t%C3%A9%2Bhiver");
    /// ```
    ///
    /// Panics if called more than once.
    pub fn finish(&mut self) -> T::Finished {
        self.target.take().expect("url::form_urlencoded::Serializer double finish").finish()
    }
}

fn append_separator_if_needed(string: &mut String, start_position: usize) {
    if string.len() > start_position {
        string.push('&')
    }
}

fn string<T: Target>(target: &mut Option<T>) -> &mut String {
    target.as_mut().expect("url::form_urlencoded::Serializer finished").as_mut_string()
}

fn append_pair(string: &mut String, start_position: usize, encoding: EncodingOverride,
               name: &str, value: &str) {
    append_separator_if_needed(string, start_position);
    string.extend(byte_serialize(&encoding.encode(name.into())));
    string.push('=');
    string.extend(byte_serialize(&encoding.encode(value.into())));
}
<|endoftext|>


<commit_before>//! [Signed Church-encoded numerals](https://en.wikipedia.org/wiki/Church_encoding#Signed_numbers)

use term::{Term, abs, app};
use term::Term::*;
use data::num::church;
use data::pair::{fst, snd, pair, swap};
use combinators::{I, Z};

/// Applied to a Church-encoded numeral it produces a pair representing a signed Church-encoded
/// integer.
///
/// TO_SIGNED ≡ λx.PAIR x ZERO ≡ λ PAIR 1 ZERO
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::to_signed;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(to_signed(), 1.into_church()), NOR, 0), 1.into_signed());
/// ```
pub fn to_signed() -> Term {
    abs(app!(pair(), Var(1), church::zero()))
}

/// Applied to a pair representing a signed Church-encoded integer it flips the sign.
///
/// NEG ≡ SWAP
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::neg;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(neg(), 1.into_signed()), NOR, 0), (-1).into_signed());
/// ```
pub fn neg() -> Term {
    swap()
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer, ensure that at
/// least one element of the pair is equal to 0.
///
/// SIMPLIFY ≡ Z (λz.λx.IS_ZERO (FST x) (λy.x) (λy.IS_ZERO (SND x) x (z (PAIR (PRED (FST x))
/// (PRED (SND x))))) I) ≡
/// Z (λ λ IS_ZERO (FST 1) (λ 2) (λ IS_ZERO (SND 2) 2 (3 (PAIR (PRED (FST 2)) (PRED (SND 2))))) I)
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::simplify;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(simplify(), (3, 0).into_church()), NOR, 0), (3, 0).into_church());
/// assert_eq!(beta(app(simplify(), (0, 3).into_church()), NOR, 0), (0, 3).into_church());
/// assert_eq!(beta(app(simplify(), (4, 1).into_church()), NOR, 0), (3, 0).into_church());
/// ```
pub fn simplify() -> Term {
    app(
        Z(),
        abs!(2, app!(
            church::is_zero(),
            app(fst(), Var(1)),
            abs(Var(2)),
            abs(app!(
                church::is_zero(),
                app(snd(), Var(2)),
                Var(2),
                app(
                    Var(3),
                    app!(
                        pair(),
                        app(church::pred(), app(fst(), Var(2))),
                        app(church::pred(), app(snd(), Var(2)))
                    )
                )
            )),
            I()
        ))
    )
}

/// Applied to a pair representing a signed Church-encoded integer it returns its absolute
/// value as a Church numeral.
///
/// MODULUS ≡ λx.(λy.IS_ZERO (FST y) (SND y) (FST y)) (NORMALIZE x) ≡
/// λ (λ IS_ZERO (FST 1) (SND 1) (FST 1)) (NORMALIZE 1)
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, modulus};
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(modulus(), 1.into_signed()), NOR, 0), 1.into_church());
/// assert_eq!(beta(app(modulus(), (-1).into_signed()), NOR, 0), 1.into_church());
/// ```
pub fn modulus() -> Term {
    abs(app(
        abs(app!(
            church::is_zero(),
            app(fst(), Var(1)),
            app(snd(), Var(1)),
            app(fst(), Var(1))
        )),
        app(simplify(), Var(1))
    ))
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer it returns a
/// pair representing their sum.
///
/// ADD ≡ λa.λb.NORMALIZE (PAIR (ADD (FST a) (FST b)) (ADD (SND a) (SND b))) ≡
/// λ λ NORMALIZE (PAIR (ADD (FST 2) (FST 1)) (ADD (SND 2) (SND 1)))
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, add};
/// use lambda_calculus::*;
///
/// assert_eq!(
///     beta(app!(add(), (-1).into_signed(), 3.into_signed()), NOR, 0),
///     beta(2.into_signed(), NOR, 0)
/// );
/// ```
pub fn add() -> Term {
    abs!(2, app(
        simplify(),
        app!(
            pair(),
            app!(
                church::add(),
                app(fst(), Var(2)),
                app(fst(), Var(1))
            ),
            app!(
                church::add(),
                app(snd(), Var(2)),
                app(snd(), Var(1))
            )
        )
    ))
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer it returns a
/// pair representing their difference.
///
/// SUB ≡ λa.λb.NORMALIZE (PAIR (ADD (FST a) (SND b)) (ADD (SND a) (FST b))) ≡
/// λ λ NORMALIZE (PAIR (ADD (FST 2) (SND 1)) (ADD (SND 2) (FST 1)))
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, sub};
/// use lambda_calculus::*;
///
/// assert_eq!(
///     beta(app!(sub(), 2.into_signed(), 3.into_signed()), NOR, 0),
///     beta((-1).into_signed(), NOR, 0)
/// );
/// ```
pub fn sub() -> Term {
    abs!(2, app(
        simplify(),
        app!(
            pair(),
            app!(
                church::add(),
                app(fst(), Var(2)),
                app(snd(), Var(1))
            ),
            app!(
                church::add(),
                app(snd(), Var(2)),
                app(fst(), Var(1))
            )
        )
    ))
}
<commit_msg>fix docs for signed functions<commit_after>//! [Signed Church-encoded numerals](https://en.wikipedia.org/wiki/Church_encoding#Signed_numbers)

use term::{Term, abs, app};
use term::Term::*;
use data::num::church;
use data::pair::{fst, snd, pair, swap};
use combinators::{I, Z};

/// Applied to a Church-encoded numeral it produces a pair representing a signed Church-encoded
/// integer.
///
/// TO_SIGNED ≡ λx.PAIR x ZERO ≡ λ PAIR 1 ZERO
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::to_signed;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(to_signed(), 1.into_church()), NOR, 0), 1.into_signed());
/// ```
pub fn to_signed() -> Term {
    abs(app!(pair(), Var(1), church::zero()))
}

/// Applied to a pair representing a signed Church-encoded integer it flips the sign.
///
/// NEG ≡ SWAP
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::neg;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(neg(), 1.into_signed()), NOR, 0), (-1).into_signed());
/// ```
pub fn neg() -> Term {
    swap()
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer, ensure that at
/// least one element of the pair is equal to 0.
///
/// SIMPLIFY ≡ Z (λz.λx.IS_ZERO (FST x) (λy.x) (λy.IS_ZERO (SND x) x (z (PAIR (PRED (FST x))
/// (PRED (SND x))))) I) ≡
/// Z (λ λ IS_ZERO (FST 1) (λ 2) (λ IS_ZERO (SND 2) 2 (3 (PAIR (PRED (FST 2)) (PRED (SND 2))))) I)
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::simplify;
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(simplify(), (3, 0).into_church()), NOR, 0), (3, 0).into_church());
/// assert_eq!(beta(app(simplify(), (0, 3).into_church()), NOR, 0), (0, 3).into_church());
/// assert_eq!(beta(app(simplify(), (4, 1).into_church()), NOR, 0), (3, 0).into_church());
/// ```
pub fn simplify() -> Term {
    app(
        Z(),
        abs!(2, app!(
            church::is_zero(),
            app(fst(), Var(1)),
            abs(Var(2)),
            abs(app!(
                church::is_zero(),
                app(snd(), Var(2)),
                Var(2),
                app(
                    Var(3),
                    app!(
                        pair(),
                        app(church::pred(), app(fst(), Var(2))),
                        app(church::pred(), app(snd(), Var(2)))
                    )
                )
            )),
            I()
        ))
    )
}

/// Applied to a pair representing a signed Church-encoded integer it returns its absolute
/// value as a Church numeral.
///
/// MODULUS ≡ λx.(λy.IS_ZERO (FST y) (SND y) (FST y)) (SIMPLIFY x) ≡
/// λ (λ IS_ZERO (FST 1) (SND 1) (FST 1)) (SIMPLIFY 1)
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, modulus};
/// use lambda_calculus::*;
///
/// assert_eq!(beta(app(modulus(), 1.into_signed()), NOR, 0), 1.into_church());
/// assert_eq!(beta(app(modulus(), (-1).into_signed()), NOR, 0), 1.into_church());
/// ```
pub fn modulus() -> Term {
    abs(app(
        abs(app!(
            church::is_zero(),
            app(fst(), Var(1)),
            app(snd(), Var(1)),
            app(fst(), Var(1))
        )),
        app(simplify(), Var(1))
    ))
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer it returns a
/// pair representing their sum.
///
/// ADD ≡ λa.λb.SIMPLIFY (PAIR (ADD (FST a) (FST b)) (ADD (SND a) (SND b))) ≡
/// λ λ SIMPLIFY (PAIR (ADD (FST 2) (FST 1)) (ADD (SND 2) (SND 1)))
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, add};
/// use lambda_calculus::*;
///
/// assert_eq!(
///     beta(app!(add(), (-1).into_signed(), 3.into_signed()), NOR, 0),
///     beta(2.into_signed(), NOR, 0)
/// );
/// ```
pub fn add() -> Term {
    abs!(2, app(
        simplify(),
        app!(
            pair(),
            app!(
                church::add(),
                app(fst(), Var(2)),
                app(fst(), Var(1))
            ),
            app!(
                church::add(),
                app(snd(), Var(2)),
                app(snd(), Var(1))
            )
        )
    ))
}

/// Applied to a pair of two Church-encoded numerals representing a signed integer it returns a
/// pair representing their difference.
///
/// SUB ≡ λa.λb.SIMPLIFY (PAIR (ADD (FST a) (SND b)) (ADD (SND a) (FST b))) ≡
/// λ λ SIMPLIFY (PAIR (ADD (FST 2) (SND 1)) (ADD (SND 2) (FST 1)))
///
/// # Example
/// ```
/// use lambda_calculus::data::num::signed::{to_signed, neg, sub};
/// use lambda_calculus::*;
///
/// assert_eq!(
///     beta(app!(sub(), 2.into_signed(), 3.into_signed()), NOR, 0),
///     beta((-1).into_signed(), NOR, 0)
/// );
/// ```
pub fn sub() -> Term {
    abs!(2, app(
        simplify(),
        app!(
            pair(),
            app!(
                church::add(),
                app(fst(), Var(2)),
                app(snd(), Var(1))
            ),
            app!(
                church::add(),
                app(snd(), Var(2)),
                app(fst(), Var(1))
            )
        )
    ))
}
<|endoftext|>


<commit_before><commit_msg>Parse GSV messages<commit_after><|endoftext|>


<commit_before>// -*- coding: utf-8 -*-
// ------------------------------------------------------------------------------------------------
// Copyright © 2020, tree-sitter-python authors.
// See the LICENSE file in this repo for license details.
// ------------------------------------------------------------------------------------------------

//! This crate provides a Python grammar for the [tree-sitter][] parsing library.
//!
//! Typically, you will use the [language][language func] function to add this grammar to a
//! tree-sitter [Parser][], and then use the parser to parse some code:
//!
//! ```
//! use tree_sitter::Parser;
//!
//! let code = r#"
//!     def double(x):
//!         return x * 2
//! "#;
//! let mut parser = Parser::new();
//! parser.set_language(tree_sitter_python::language()).expect("Error loading Python grammar");
//! let parsed = parser.parse(code, None);
//! # let parsed = parsed.unwrap();
//! # let root = parsed.root_node();
//! # assert!(!root.has_error());
//! ```
//!
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
//! [language func]: fn.language.html
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
//! [tree-sitter]: https://tree-sitter.github.io/

use tree_sitter::Language;

extern "C" {
    fn tree_sitter_python() -> Language;
}

/// Returns the tree-sitter [Language][] for this grammar.
///
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
pub fn language() -> Language {
    unsafe { tree_sitter_python() }
}

/// The source of the Python tree-sitter grammar description.
pub const GRAMMAR: &'static str = include_str!("../../grammar.js");

/// The syntax highlighting queries for this language.
pub const HIGHLIGHT_QUERIES: &'static str = include_str!("../../queries/highlights.scm");

/// The content of the [`node-types.json`][] file for this grammar.
///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");

/// The symbol tagging queries for this language.
pub const TAGGING_QUERIES: &'static str = include_str!("../../queries/tags.scm");

#[cfg(test)]
mod tests {
    #[test]
    fn can_load_grammar() {
        let mut parser = tree_sitter::Parser::new();
        parser
            .set_language(super::language())
            .expect("Error loading Python grammar");
    }
}
<commit_msg>Change QUERIES → QUERY<commit_after>// -*- coding: utf-8 -*-
// ------------------------------------------------------------------------------------------------
// Copyright © 2020, tree-sitter-python authors.
// See the LICENSE file in this repo for license details.
// ------------------------------------------------------------------------------------------------

//! This crate provides a Python grammar for the [tree-sitter][] parsing library.
//!
//! Typically, you will use the [language][language func] function to add this grammar to a
//! tree-sitter [Parser][], and then use the parser to parse some code:
//!
//! ```
//! use tree_sitter::Parser;
//!
//! let code = r#"
//!     def double(x):
//!         return x * 2
//! "#;
//! let mut parser = Parser::new();
//! parser.set_language(tree_sitter_python::language()).expect("Error loading Python grammar");
//! let parsed = parser.parse(code, None);
//! # let parsed = parsed.unwrap();
//! # let root = parsed.root_node();
//! # assert!(!root.has_error());
//! ```
//!
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
//! [language func]: fn.language.html
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
//! [tree-sitter]: https://tree-sitter.github.io/

use tree_sitter::Language;

extern "C" {
    fn tree_sitter_python() -> Language;
}

/// Returns the tree-sitter [Language][] for this grammar.
///
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
pub fn language() -> Language {
    unsafe { tree_sitter_python() }
}

/// The source of the Python tree-sitter grammar description.
pub const GRAMMAR: &'static str = include_str!("../../grammar.js");

/// The syntax highlighting query for this language.
pub const HIGHLIGHT_QUERY: &'static str = include_str!("../../query/highlights.scm");

/// The content of the [`node-types.json`][] file for this grammar.
///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");

/// The symbol tagging query for this language.
pub const TAGGING_QUERY: &'static str = include_str!("../../query/tags.scm");

#[cfg(test)]
mod tests {
    #[test]
    fn can_load_grammar() {
        let mut parser = tree_sitter::Parser::new();
        parser
            .set_language(super::language())
            .expect("Error loading Python grammar");
    }
}
<|endoftext|>


<commit_before>use std::cmp;

use ctxt::SemContext;

use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::CARD_SIZE;
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{Card, CrossingEntry, CrossingMap};
use gc::swiper::in_kilo;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;

use mem;
use object::Obj;
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
    promoted_size: usize,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
            promoted_size: 0,
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();
        let young_init_size = self.young.used_region().size();
        self.free = self.young.to_space().start;

        self.young.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.young.protect_to_space();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let young_new_size = self.young.used_region().size();
            let garbage = young_init_size - young_new_size - self.promoted_size;
            let garbage_ratio = (garbage as f64 / young_init_size as f64) * 100f64;

            println!("GC: Minor GC ({:.2} ms, {:.1}K->{:.1}K, young {:.1}K->{:.1}K, \
                      {:.1}K promoted, {:.1}K/{:.0}% garbage)",
                in_ms(dur), in_kilo(init_size), in_kilo(new_size),
                in_kilo(young_init_size), in_kilo(young_new_size),
                in_kilo(self.promoted_size),
                in_kilo(garbage), garbage_ratio);
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_copied_objects(&mut self) {
        let mut scan = self.young.to_space().start;

        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty(|card| {
            let crossing_entry = self.crossing_map.get(card);
            let card_start = self.old.address_from_card(card);
            let card_end = card_start.offset(CARD_SIZE);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ptr = card_start;

                    for _ in 0..refs {
                        let ind_ptr = IndirectObj::from_address(ptr);
                        let dir_ptr = ind_ptr.get();

                        if self.young.from_space().contains(Address::from_ptr(dir_ptr)) {
                            ind_ptr.set(self.copy(dir_ptr));
                        }

                        ptr = ptr.offset(mem::ptr_width() as usize);
                    }

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }

                CrossingEntry::FirstObjectOffset(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }
            }
        });
    }

    fn copy_card(&mut self, card: Card, mut ptr: Address, card_end: Address) {
        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);
        let mut ref_to_young_gen = false;

        loop {
            while ptr < end {
                let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

                object.visit_reference_fields_within(end, |field| {
                    let field_ptr = field.get();

                    if self.young.from_space().contains(Address::from_ptr(field_ptr)) {
                        let copied_obj = self.copy(field_ptr);
                        field.set(copied_obj);

                        // determine if copied object is still in young generation
                        if self.young.contains(Address::from_ptr(copied_obj)) {
                            ref_to_young_gen = true;
                        }
                    }
                });

                ptr = ptr.offset(object.size());
            }

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();
        debug_assert!(self.young.from_space().contains(obj_addr), "copy objects only from from-space.");

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = Address::from_ptr(self.old.alloc(obj_size, obj.is_array_ref()));

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;

            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);
        self.promoted_size += obj_size;

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card = self.old.card_from_address(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<commit_msg>swiper: store young.from_space()<commit_after>use std::cmp;

use ctxt::SemContext;

use gc::Address;
use gc::root::IndirectObj;
use gc::swiper::{CARD_SIZE, Region};
use gc::swiper::card::{CardEntry, CardTable};
use gc::swiper::crossing::{Card, CrossingEntry, CrossingMap};
use gc::swiper::in_kilo;
use gc::swiper::old::OldGen;
use gc::swiper::young::YoungGen;

use mem;
use object::Obj;
use timer::{in_ms, Timer};

pub struct MinorCollector<'a, 'ast: 'a> {
    ctxt: &'a SemContext<'ast>,
    young: &'a YoungGen,
    old: &'a OldGen,
    rootset: &'a [IndirectObj],
    card_table: &'a CardTable,
    crossing_map: &'a CrossingMap,

    free: Address,
    promotion_failed: bool,
    promoted_size: usize,
    young_from: Region,
}

impl<'a, 'ast> MinorCollector<'a, 'ast> {
    pub fn new(
        ctxt: &'a SemContext<'ast>,
        young: &'a YoungGen,
        old: &'a OldGen,
        card_table: &'a CardTable,
        crossing_map: &'a CrossingMap,
        rootset: &'a [IndirectObj],
    ) -> MinorCollector<'a, 'ast> {
        MinorCollector {
            ctxt: ctxt,
            young: young,
            old: old,
            rootset: rootset,
            card_table: card_table,
            crossing_map: crossing_map,
            free: Address::null(),
            promotion_failed: false,
            promoted_size: 0,
            young_from: young.from_space(),
        }
    }

    pub fn collect(&mut self) {
        let mut timer = Timer::new(self.ctxt.args.flag_gc_verbose);
        let init_size = self.heap_size();
        let young_init_size = self.young.used_region().size();
        self.free = self.young.to_space().start;

        self.young.unprotect_to_space();

        self.visit_roots();
        self.copy_dirty_cards();
        self.visit_copied_objects();
        self.young.swap_spaces(self.free);

        self.young.protect_to_space();

        timer.stop_with(|dur| {
            let new_size = self.heap_size();
            let young_new_size = self.young.used_region().size();
            let garbage = young_init_size - young_new_size - self.promoted_size;
            let garbage_ratio = (garbage as f64 / young_init_size as f64) * 100f64;

            println!("GC: Minor GC ({:.2} ms, {:.1}K->{:.1}K, young {:.1}K->{:.1}K, \
                      {:.1}K promoted, {:.1}K/{:.0}% garbage)",
                in_ms(dur), in_kilo(init_size), in_kilo(new_size),
                in_kilo(young_init_size), in_kilo(young_new_size),
                in_kilo(self.promoted_size),
                in_kilo(garbage), garbage_ratio);
        });
    }

    fn heap_size(&self) -> usize {
        self.young.used_region().size() + self.old.used_region().size()
    }

    fn visit_roots(&mut self) {
        // detect all references from roots into young generation
        for &root in self.rootset {
            let root_ptr = root.get();

            if self.young.contains(Address::from_ptr(root_ptr)) {
                root.set(self.copy(root_ptr));
            }
        }
    }

    fn visit_copied_objects(&mut self) {
        let mut scan = self.young.to_space().start;

        // visit all fields in copied objects
        while scan < self.free {
            let object = unsafe { &mut *scan.to_mut_ptr::<Obj>() };

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if self.young.contains(Address::from_ptr(field_ptr)) {
                    field.set(self.copy(field_ptr));
                }
            });

            scan = scan.offset(object.size());
        }
    }

    // copy all references from old- into young-generation.
    fn copy_dirty_cards(&mut self) {
        self.card_table.visit_dirty(|card| {
            let crossing_entry = self.crossing_map.get(card);
            let card_start = self.old.address_from_card(card);
            let card_end = card_start.offset(CARD_SIZE);

            match crossing_entry {
                CrossingEntry::NoRefs => panic!("card dirty without any refs"),
                CrossingEntry::LeadingRefs(refs) => {
                    let mut ptr = card_start;

                    for _ in 0..refs {
                        let ind_ptr = IndirectObj::from_address(ptr);
                        let dir_ptr = ind_ptr.get();

                        if self.young_from.contains(Address::from_ptr(dir_ptr)) {
                            ind_ptr.set(self.copy(dir_ptr));
                        }

                        ptr = ptr.offset(mem::ptr_width() as usize);
                    }

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }

                CrossingEntry::FirstObjectOffset(offset) => {
                    let ptr = card_start.offset(offset as usize * mem::ptr_width_usize());

                    // copy all objects from this card
                    self.copy_card(card, ptr, card_end);
                }
            }
        });
    }

    fn copy_card(&mut self, card: Card, mut ptr: Address, card_end: Address) {
        let old_end: Address = self.old.free();
        let mut end = cmp::min(card_end, old_end);
        let mut ref_to_young_gen = false;

        loop {
            while ptr < end {
                let object = unsafe { &mut *ptr.to_mut_ptr::<Obj>() };

                object.visit_reference_fields_within(end, |field| {
                    let field_ptr = field.get();

                    if self.young_from.contains(Address::from_ptr(field_ptr)) {
                        let copied_obj = self.copy(field_ptr);
                        field.set(copied_obj);

                        // determine if copied object is still in young generation
                        if self.young.contains(Address::from_ptr(copied_obj)) {
                            ref_to_young_gen = true;
                        }
                    }
                });

                ptr = ptr.offset(object.size());
            }

            // if we are in the last card of the old generation, promoted objects
            // will increase `end` towards `card_end`. Those newly promoted objects
            // need also be handled.
            if end == card_end {
                break;
            }

            let old_end = self.old.free();
            let next_end = cmp::min(card_end, old_end);

            if end != next_end {
                end = next_end;
            } else {
                break;
            }
        }

        // if there are no references to the young generation in this card,
        // set the card to clean.
        if !ref_to_young_gen {
            self.card_table.set(card, CardEntry::Clean);
        }
    }

    fn copy(&mut self, obj: *mut Obj) -> *mut Obj {
        let obj_addr = Address::from_ptr(obj);
        let obj = unsafe { &mut *obj };

        if let Some(fwd) = obj.header().forwarded() {
            return fwd.to_mut_ptr();
        }

        let obj_size = obj.size();
        debug_assert!(self.young_from.contains(obj_addr), "copy objects only from from-space.");

        // if object is old enough we copy it into the old generation
        if self.young.should_be_promoted(obj_addr) {
            let copy_addr = Address::from_ptr(self.old.alloc(obj_size, obj.is_array_ref()));

            // if there isn't enough space in old gen keep it in the
            // young generation for now
            if copy_addr.is_null() {
                self.promotion_failed = true;

            } else {
                self.promote_object(obj, copy_addr, obj_size);
                return copy_addr.to_mut_ptr();
            }
        }

        let copy_addr = self.free;
        self.free = copy_addr.offset(obj_size);

        obj.copy_to(copy_addr, obj_size);
        obj.header_mut().forward_to(copy_addr);

        copy_addr.to_mut_ptr()
    }

    fn promote_object(&mut self, obj: &mut Obj, copy_addr: Address, obj_size: usize) {
        obj.copy_to(copy_addr, obj_size);
        self.promoted_size += obj_size;

        // Promoted object can have references to the young generation.
        // Set the card table entry to dirty if this is the case.
        self.handle_promoted_object(copy_addr);

        obj.header_mut().forward_to(copy_addr);
    }

    fn handle_promoted_object(&mut self, addr: Address) {
        let card = self.old.card_from_address(addr);

        // card is already dirty, nothing left to do.
        if self.card_table.get(card).is_dirty() {
            return;
        }

        let object: &mut Obj = unsafe { &mut *addr.to_mut_ptr() };
        let mut old_to_young_ref = false;

        object.visit_reference_fields(|field| {
            let field_ptr = field.get();

            if self.young.contains(Address::from_ptr(field_ptr)) {
                old_to_young_ref = true;
            }
        });

        if old_to_young_ref {
            self.card_table.set(card, CardEntry::Dirty);
        }
    }

    pub fn promotion_failed(&self) -> bool {
        self.promotion_failed
    }
}
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2016 Rafael Medina García <rafamedgar@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

/// Main window definition

use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::rc::Rc;
use std::thread;

use glib;
use gtk;
use gtk::prelude::*;
use gtk::{
    AboutDialog,
    Application,
    ApplicationWindow,
    Box,
    Builder,
    HeaderBar,
    Orientation,
    Revealer,
    RevealerTransitionType,
    Type,
    Window,
    WindowPosition,
    WindowType
};
use gtk::{
    Button,
    CellRendererText,
    Calendar,
    ComboBox,
    Entry,
    Label,
    ListStore,
    Popover,
    ScrolledWindow,
    Separator,
    SpinButton,
    ToggleButton,
    TreeIter,
    TreeSelection,
    TreeView,
    TreeViewColumn
};
use rusqlite::Connection;

use common::{State, CURRENCIES, PAYMENTS};
use migrations;
use db;
use db::Receipt;
use gui::edit_window;

/// Creates the main window
pub fn create_window(app: &Application,
                     state: &Rc<RefCell<State>>) -> ApplicationWindow {

    let window = ApplicationWindow::new(&app);

    window.set_title("Receipt Keeper");
    window.set_border_width(10);
    window.set_default_size(800, 600);
    window.set_position(WindowPosition::Center);

    let builder = Builder::new();
    builder.add_from_string(include_str!("main_window.ui"));

    // Header bar
    let header_bar: HeaderBar = builder.get_object("header_bar").unwrap();
    window.set_titlebar(Some(&header_bar));

    // Window container
    let main_box: Box = builder.get_object("main_box").unwrap();
    window.add(&main_box);

    // Fill stores
    let store_currency: ListStore = builder.get_object("store_currency").unwrap();
    fill_store!(combo => store_currency, CURRENCIES);

    let store_type: ListStore = builder.get_object("store_type").unwrap();
    fill_store!(combo => store_type, PAYMENTS);

    let store_table: ListStore = builder.get_object("store_table").unwrap();
    let receipts = db::get_all_receipts(&state.borrow().db_path).unwrap();
    fill_store!(table => store_table, receipts);

    // Events

    // Create a new record
    {
        let builder = builder.clone();
        let app = window.get_application().unwrap();
        let state = state.clone();
        let btn_new: Button = builder.get_object("btn_new").unwrap();

        btn_new.connect_clicked(move |_| {
            let dialog = edit_window::create_window(&app, &state, -1);
            dialog.show();
        });
    }

    // Toggle search box
    {
        let builder = builder.clone();
        let toggle_search: ToggleButton = builder.get_object("toggle_search").unwrap();

        let revealer: Revealer = builder.get_object("revealer").unwrap();

        toggle_search.connect_toggled(move |toggle| {
            if toggle.get_active() {
                revealer.set_reveal_child(true);

            } else {
                revealer.set_reveal_child(false);
            }
        });
    }

    // Toogle date chooser (from)
    {
        let builder = builder.clone();
        let btn_date_from: Button = builder.get_object("btn_date_from").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();

        btn_date_from.connect_clicked(move |_| {
            popover_date.set_relative_to(Some(&entry_date_from));
            popover_date.show_all();
        });
    }

    // Toogle date chooser (to)
    {
        let builder = builder.clone();
        let btn_date_to: Button = builder.get_object("btn_date_to").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();

        btn_date_to.connect_clicked(move |_| {
            popover_date.set_relative_to(Some(&entry_date_to));
            popover_date.show_all();
        });
    }

    // Calendar chooser
    // NOTE: Months start in 0
    {
        let builder = builder.clone();
        let calendar: Calendar = builder.get_object("calendar").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();

        calendar.connect_day_selected(move |calendar| {
            let (year, month, day) = calendar.get_date();

            if popover_date.get_relative_to().unwrap().eq(&entry_date_from) {
                entry_date_from.set_text(format!("{}/{}/{}", day, month+1, year).as_str());

            } else {
                entry_date_to.set_text(format!("{}/{}/{}", day, month+1, year).as_str());
            }
        });
    }

    // Clear search
    {
        let builder = builder.clone();
        let btn_clear: Button = builder.get_object("btn_clear_search").unwrap();

        let popover_menu: Popover = builder.get_object("popover_menu").unwrap();
        let entry_shop: Entry = builder.get_object("entry_shop").unwrap();
        let spin_cost_from: SpinButton = builder.get_object("spin_cost_from").unwrap();
        let spin_cost_to: SpinButton = builder.get_object("spin_cost_to").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();
        let combo_type: ComboBox = builder.get_object("combo_type").unwrap();
        let combo_currency: ComboBox = builder.get_object("combo_currency").unwrap();

        btn_clear.connect_clicked(move |_| {
            entry_shop.set_text("");
            spin_cost_from.set_value(0.0);
            spin_cost_to.set_value(0.0);
            entry_date_from.set_text("");
            entry_date_to.set_text("");
            combo_type.set_active(-1);
            combo_currency.set_active(-1);

            // Hide menu
            popover_menu.hide();

            // TODO filter results
        });
    }

    // Show about dialog
    {
        let builder = builder.clone();
        let btn_about: Button = builder.get_object("btn_about").unwrap();

        let popover_menu: Popover = builder.get_object("popover_menu").unwrap();
        let dialog: AboutDialog = builder.get_object("about_dialog").unwrap();

        btn_about.connect_clicked(move |_| {
            popover_menu.hide();
            dialog.run();
        });
    }

    // Toggle remove and edit buttons
    {
        let builder = builder.clone();
        let table_selection: TreeSelection = builder.get_object("table_selection").unwrap();

        let btn_remove: Button = builder.get_object("btn_remove").unwrap();
        let btn_edit: Button = builder.get_object("btn_edit").unwrap();

        table_selection.connect_changed(move |selection| {
            if selection.count_selected_rows() == 1 {
                btn_remove.set_sensitive(true);
                btn_edit.set_sensitive(true);

            } else {
                btn_remove.set_sensitive(false);
                btn_edit.set_sensitive(false);
            }
        });
    }

    // Show window to view/edit a receipt
    {
        let builder = builder.clone();
        let btn_edit: Button = builder.get_object("btn_edit").unwrap();

        let state = state.clone();
        let app = window.get_application().unwrap();
        let table_selection: TreeSelection = builder.get_object("table_selection").unwrap();

        btn_edit.connect_clicked(move |_| {
            let (model, iter) = table_selection.get_selected().unwrap();
            let id = model.get_value(&iter, 0).get::<i32>().unwrap();

            let dialog: ApplicationWindow;
            let mut exists = false;
            let mut stored: Window = Window::new(WindowType::Toplevel); // Dummy

            {
                // Check if window is already running
                let borrowed = state.borrow();

                if borrowed.window_map.contains_key(&id) {
                    // Load stored window
                    stored = app.get_window_by_id(
                        *borrowed.window_map.get(&id).unwrap()
                    ).unwrap();

                    exists = true;
                }
            }

            if exists {
                // Bring window to front
                unsafe {
                    dialog = mem::transmute::<Window, ApplicationWindow>(stored);
                };

                dialog.present();

            } else {
                // Create new window and add it to the map
                dialog = edit_window::create_window(&app, &state, id);

                state.borrow_mut().window_map.insert(id, dialog.get_id());

                dialog.show();
            }
        });
    }

    // Store builder and database path in thread local storage
    {
        let builder = builder.clone();
        let db_path = state.borrow().db_path.clone();

        REFRESH.with(move |r| {
            *r.borrow_mut() = Some((builder, db_path));
        });
    }

    window
}

/// Refresh the receipt table
///
/// This is usually done when adding or editing a receipt
pub fn refresh_table() -> glib::Continue {
    REFRESH.with(move |r| {
        if let Some((ref builder, ref db_path)) = *r.borrow() {
            let store_table: ListStore = builder.get_object("store_table").unwrap();

            let receipts = db::get_all_receipts(&db_path).unwrap();
            fill_store!(table => store_table, receipts);
        }
    });

    glib::Continue(false)
}

// Keep builder in thread local storage to update the table
thread_local!(
    static REFRESH: RefCell<Option<(Builder, String)>> = RefCell::new(None);
);
<commit_msg>Removed ; in thread_local declaration<commit_after>// MIT License
//
// Copyright (c) 2016 Rafael Medina García <rafamedgar@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

/// Main window definition

use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::rc::Rc;
use std::thread;

use glib;
use gtk;
use gtk::prelude::*;
use gtk::{
    AboutDialog,
    Application,
    ApplicationWindow,
    Box,
    Builder,
    HeaderBar,
    Orientation,
    Revealer,
    RevealerTransitionType,
    Type,
    Window,
    WindowPosition,
    WindowType
};
use gtk::{
    Button,
    CellRendererText,
    Calendar,
    ComboBox,
    Entry,
    Label,
    ListStore,
    Popover,
    ScrolledWindow,
    Separator,
    SpinButton,
    ToggleButton,
    TreeIter,
    TreeSelection,
    TreeView,
    TreeViewColumn
};
use rusqlite::Connection;

use common::{State, CURRENCIES, PAYMENTS};
use migrations;
use db;
use db::Receipt;
use gui::edit_window;

/// Creates the main window
pub fn create_window(app: &Application,
                     state: &Rc<RefCell<State>>) -> ApplicationWindow {

    let window = ApplicationWindow::new(&app);

    window.set_title("Receipt Keeper");
    window.set_border_width(10);
    window.set_default_size(800, 600);
    window.set_position(WindowPosition::Center);

    let builder = Builder::new();
    builder.add_from_string(include_str!("main_window.ui"));

    // Header bar
    let header_bar: HeaderBar = builder.get_object("header_bar").unwrap();
    window.set_titlebar(Some(&header_bar));

    // Window container
    let main_box: Box = builder.get_object("main_box").unwrap();
    window.add(&main_box);

    // Fill stores
    let store_currency: ListStore = builder.get_object("store_currency").unwrap();
    fill_store!(combo => store_currency, CURRENCIES);

    let store_type: ListStore = builder.get_object("store_type").unwrap();
    fill_store!(combo => store_type, PAYMENTS);

    let store_table: ListStore = builder.get_object("store_table").unwrap();
    let receipts = db::get_all_receipts(&state.borrow().db_path).unwrap();
    fill_store!(table => store_table, receipts);

    // Events

    // Create a new record
    {
        let builder = builder.clone();
        let app = window.get_application().unwrap();
        let state = state.clone();
        let btn_new: Button = builder.get_object("btn_new").unwrap();

        btn_new.connect_clicked(move |_| {
            let dialog = edit_window::create_window(&app, &state, -1);
            dialog.show();
        });
    }

    // Toggle search box
    {
        let builder = builder.clone();
        let toggle_search: ToggleButton = builder.get_object("toggle_search").unwrap();

        let revealer: Revealer = builder.get_object("revealer").unwrap();

        toggle_search.connect_toggled(move |toggle| {
            if toggle.get_active() {
                revealer.set_reveal_child(true);

            } else {
                revealer.set_reveal_child(false);
            }
        });
    }

    // Toogle date chooser (from)
    {
        let builder = builder.clone();
        let btn_date_from: Button = builder.get_object("btn_date_from").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();

        btn_date_from.connect_clicked(move |_| {
            popover_date.set_relative_to(Some(&entry_date_from));
            popover_date.show_all();
        });
    }

    // Toogle date chooser (to)
    {
        let builder = builder.clone();
        let btn_date_to: Button = builder.get_object("btn_date_to").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();

        btn_date_to.connect_clicked(move |_| {
            popover_date.set_relative_to(Some(&entry_date_to));
            popover_date.show_all();
        });
    }

    // Calendar chooser
    // NOTE: Months start in 0
    {
        let builder = builder.clone();
        let calendar: Calendar = builder.get_object("calendar").unwrap();

        let popover_date: Popover = builder.get_object("popover_date").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();

        calendar.connect_day_selected(move |calendar| {
            let (year, month, day) = calendar.get_date();

            if popover_date.get_relative_to().unwrap().eq(&entry_date_from) {
                entry_date_from.set_text(format!("{}/{}/{}", day, month+1, year).as_str());

            } else {
                entry_date_to.set_text(format!("{}/{}/{}", day, month+1, year).as_str());
            }
        });
    }

    // Clear search
    {
        let builder = builder.clone();
        let btn_clear: Button = builder.get_object("btn_clear_search").unwrap();

        let popover_menu: Popover = builder.get_object("popover_menu").unwrap();
        let entry_shop: Entry = builder.get_object("entry_shop").unwrap();
        let spin_cost_from: SpinButton = builder.get_object("spin_cost_from").unwrap();
        let spin_cost_to: SpinButton = builder.get_object("spin_cost_to").unwrap();
        let entry_date_from: Entry = builder.get_object("entry_date_from").unwrap();
        let entry_date_to: Entry = builder.get_object("entry_date_to").unwrap();
        let combo_type: ComboBox = builder.get_object("combo_type").unwrap();
        let combo_currency: ComboBox = builder.get_object("combo_currency").unwrap();

        btn_clear.connect_clicked(move |_| {
            entry_shop.set_text("");
            spin_cost_from.set_value(0.0);
            spin_cost_to.set_value(0.0);
            entry_date_from.set_text("");
            entry_date_to.set_text("");
            combo_type.set_active(-1);
            combo_currency.set_active(-1);

            // Hide menu
            popover_menu.hide();

            // TODO filter results
        });
    }

    // Show about dialog
    {
        let builder = builder.clone();
        let btn_about: Button = builder.get_object("btn_about").unwrap();

        let popover_menu: Popover = builder.get_object("popover_menu").unwrap();
        let dialog: AboutDialog = builder.get_object("about_dialog").unwrap();

        btn_about.connect_clicked(move |_| {
            popover_menu.hide();
            dialog.run();
        });
    }

    // Toggle remove and edit buttons
    {
        let builder = builder.clone();
        let table_selection: TreeSelection = builder.get_object("table_selection").unwrap();

        let btn_remove: Button = builder.get_object("btn_remove").unwrap();
        let btn_edit: Button = builder.get_object("btn_edit").unwrap();

        table_selection.connect_changed(move |selection| {
            if selection.count_selected_rows() == 1 {
                btn_remove.set_sensitive(true);
                btn_edit.set_sensitive(true);

            } else {
                btn_remove.set_sensitive(false);
                btn_edit.set_sensitive(false);
            }
        });
    }

    // Show window to view/edit a receipt
    {
        let builder = builder.clone();
        let btn_edit: Button = builder.get_object("btn_edit").unwrap();

        let state = state.clone();
        let app = window.get_application().unwrap();
        let table_selection: TreeSelection = builder.get_object("table_selection").unwrap();

        btn_edit.connect_clicked(move |_| {
            let (model, iter) = table_selection.get_selected().unwrap();
            let id = model.get_value(&iter, 0).get::<i32>().unwrap();

            let dialog: ApplicationWindow;
            let mut exists = false;
            let mut stored: Window = Window::new(WindowType::Toplevel); // Dummy

            {
                // Check if window is already running
                let borrowed = state.borrow();

                if borrowed.window_map.contains_key(&id) {
                    // Load stored window
                    stored = app.get_window_by_id(
                        *borrowed.window_map.get(&id).unwrap()
                    ).unwrap();

                    exists = true;
                }
            }

            if exists {
                // Bring window to front
                unsafe {
                    dialog = mem::transmute::<Window, ApplicationWindow>(stored);
                };

                dialog.present();

            } else {
                // Create new window and add it to the map
                dialog = edit_window::create_window(&app, &state, id);

                state.borrow_mut().window_map.insert(id, dialog.get_id());

                dialog.show();
            }
        });
    }

    // Store builder and database path in thread local storage
    {
        let builder = builder.clone();
        let db_path = state.borrow().db_path.clone();

        REFRESH.with(move |r| {
            *r.borrow_mut() = Some((builder, db_path));
        });
    }

    window
}

/// Refresh the receipt table
///
/// This is usually done when adding or editing a receipt
pub fn refresh_table() -> glib::Continue {
    REFRESH.with(move |r| {
        if let Some((ref builder, ref db_path)) = *r.borrow() {
            let store_table: ListStore = builder.get_object("store_table").unwrap();

            let receipts = db::get_all_receipts(&db_path).unwrap();
            fill_store!(table => store_table, receipts);
        }
    });

    glib::Continue(false)
}

// Keep builder in thread local storage to update the table
thread_local!(
    static REFRESH: RefCell<Option<(Builder, String)>> = RefCell::new(None)
);
<|endoftext|>