<commit_before>use std::c_str::CString;
use std::kinds::marker;
use std::str;
use libc;

use {raw, Repository, Direction, Error, Refspec, StringArray};
use Signature;

/// A structure represending a [remote][1] of a git repository.
///
/// [1]: http://git-scm.com/book/en/Git-Basics-Working-with-Remotes
///
/// The lifetime is the lifetime of the repository that it is attached to. The
/// remote is used to manage fetches and pushes as well as refspecs.
pub struct Remote<'a> {
    raw: *mut raw::git_remote,
    marker1: marker::ContravariantLifetime<'a>,
    marker2: marker::NoSend,
    marker3: marker::NoSync,
}

/// An iterator over the refspecs that a remote contains.
pub struct Refspecs<'a> {
    cur: uint,
    cnt: uint,
    remote: &'a Remote<'a>,
}

impl<'a> Remote<'a> {
    /// Creates a new remote from its raw pointer.
    ///
    /// This method is unsafe as there is no guarantee that `raw` is valid or
    /// tha no other remote is using it.
    pub unsafe fn from_raw(_repo: &Repository,
                           raw: *mut raw::git_remote) -> Remote {
        Remote {
            raw: raw,
            marker1: marker::ContravariantLifetime,
            marker2: marker::NoSend,
            marker3: marker::NoSync,
        }
    }

    /// Ensure the remote name is well-formed.
    pub fn is_valid_name(remote_name: &str) -> bool {
        ::init();
        let remote_name = remote_name.to_c_str();
        unsafe { raw::git_remote_is_valid_name(remote_name.as_ptr()) == 1 }
    }

    /// Return whether a string is a valid remote URL
    pub fn is_valid_url(url: &str) -> bool {
        ::init();
        let url = url.to_c_str();
        unsafe { raw::git_remote_valid_url(url.as_ptr()) == 1 }
    }

    /// Return whether the passed URL is supported by this version of the
    /// library.
    pub fn is_supported_url(url: &str) -> bool {
        ::init();
        let url = url.to_c_str();
        unsafe { raw::git_remote_supported_url(url.as_ptr()) == 1 }
    }

    /// Get the remote's name.
    ///
    /// Returns `None` if this remote has not yet been named or if the name is
    /// not valid utf-8
    pub fn name(&self) -> Option<&str> {
        self.name_bytes().and_then(str::from_utf8)
    }

    /// Get the remote's name, in bytes.
    ///
    /// Returns `None` if this remote has not yet been named
    pub fn name_bytes(&self) -> Option<&[u8]> {
        unsafe { ::opt_bytes(self, raw::git_remote_name(&*self.raw)) }
    }

    /// Get the remote's url.
    ///
    /// Returns `None` if the url is not valid utf-8
    pub fn url(&self) -> Option<&str> {
        str::from_utf8(self.url_bytes())
    }

    /// Get the remote's url as a byte array.
    pub fn url_bytes(&self) -> &[u8] {
        unsafe { ::opt_bytes(self, raw::git_remote_url(&*self.raw)).unwrap() }
    }

    /// Get the remote's pushurl.
    ///
    /// Returns `None` if the pushurl is not valid utf-8
    pub fn pushurl(&self) -> Option<&str> {
        self.pushurl_bytes().and_then(str::from_utf8)
    }

    /// Get the remote's pushurl as a byte array.
    pub fn pushurl_bytes(&self) -> Option<&[u8]> {
        unsafe { ::opt_bytes(self, raw::git_remote_pushurl(&*self.raw)) }
    }

    /// Open a connection to a remote.
    pub fn connect(&mut self, dir: Direction) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_connect(self.raw, dir));
        }
        Ok(())
    }

    /// Check whether the remote is connected
    pub fn connected(&mut self) -> bool {
        unsafe { raw::git_remote_connected(self.raw) == 1 }
    }

    /// Disconnect from the remote
    pub fn disconnect(&mut self) {
        unsafe { raw::git_remote_disconnect(self.raw) }
    }

    /// Save a remote to its repository's configuration
    ///
    /// Anonymous remotes cannot be saved
    pub fn save(&self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_save(&*self.raw)); }
        Ok(())
    }

    /// Add a fetch refspec to the remote
    pub fn add_fetch(&mut self, spec: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_add_fetch(self.raw, spec.to_c_str()));
        }
        Ok(())
    }

    /// Add a push refspec to the remote
    pub fn add_push(&mut self, spec: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_add_push(self.raw, spec.to_c_str()));
        }
        Ok(())
    }

    /// Choose whether to check the server's certificate (applies to HTTPS only)
    ///
    /// The default is yes.
    pub fn set_check_cert(&mut self, check: bool) {
        unsafe { raw::git_remote_check_cert(self.raw, check as libc::c_int) }
    }

    /// Set the remote's url
    ///
    /// Existing connections will not be updated.
    pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_set_url(self.raw, url.to_c_str()));
        }
        Ok(())
    }

    /// Set the remote's pushurl.
    ///
    /// `None` indicates that it should be cleared.
    ///
    /// Existing connections will not be updated.
    pub fn set_pushurl(&mut self, pushurl: Option<&str>) -> Result<(), Error> {
        let pushurl = pushurl.map(|s| s.to_c_str());
        unsafe {
            try_call!(raw::git_remote_set_pushurl(self.raw, pushurl));
        }
        Ok(())
    }

    /// Sets the update FETCH_HEAD setting. By default, FETCH_HEAD will be
    /// updated on every fetch.
    pub fn set_update_fetchhead(&mut self, update: bool) {
        unsafe {
            raw::git_remote_set_update_fetchhead(self.raw, update as libc::c_int)
        }
    }

    /// Set the remote's list of fetch refspecs
    pub fn set_fetch_refspecs<T: ToCStr, I: Iterator<T>>(&mut self, i: I)
                                                         -> Result<(), Error> {
        let v = i.map(|t| t.to_c_str()).collect::<Vec<CString>>();
        let v2 = v.iter().map(|v| v.as_ptr()).collect::<Vec<*const libc::c_char>>();
        let mut arr = raw::git_strarray {
            strings: v2.as_ptr() as *mut _,
            count: v2.len() as libc::size_t,
        };

        unsafe {
            try_call!(raw::git_remote_set_fetch_refspecs(self.raw, &mut arr));
        }
        Ok(())
    }

    /// Set the remote's list of push refspecs
    pub fn set_push_refspecs<T: ToCStr, I: Iterator<T>>(&mut self, i: I)
                                                         -> Result<(), Error> {
        let v = i.map(|t| t.to_c_str()).collect::<Vec<CString>>();
        let v2 = v.iter().map(|v| v.as_ptr()).collect::<Vec<*const libc::c_char>>();
        let mut arr = raw::git_strarray {
            strings: v2.as_ptr() as *mut _,
            count: v2.len() as libc::size_t,
        };

        unsafe {
            try_call!(raw::git_remote_set_push_refspecs(self.raw, &mut arr));
        }
        Ok(())
    }

    /// Clear the refspecs
    ///
    /// Remove all configured fetch and push refspecs from the remote.
    pub fn clear_refspecs(&mut self) {
        unsafe { raw::git_remote_clear_refspecs(self.raw) }
    }

    /// Delete an existing persisted remote.
    ///
    /// All remote-tracking branches and configuration settings for the remote
    /// will be removed.
    pub fn delete(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_delete(self.raw)); }
        Ok(())
    }

    /// Download and index the packfile
    ///
    /// Connect to the remote if it hasn't been done yet, negotiate with the
    /// remote git which objects are missing, download and index the packfile.
    ///
    /// The .idx file will be created and both it and the packfile with be
    /// renamed to their final name.
    pub fn download(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_download(self.raw)); }
        Ok(())
    }

    /// Get the number of refspecs for a remote
    pub fn refspecs(&self) -> Refspecs {
        let cnt = unsafe { raw::git_remote_refspec_count(&*self.raw) as uint };
        Refspecs { cur: 0, cnt: cnt, remote: self }
    }

    /// Give the remote a new name
    ///
    /// All remote-tracking branches and configuration settings for the remote
    /// are updated.
    ///
    /// A temporary in-memory remote cannot be given a name with this method.
    pub fn rename(&mut self, new_name: &str) -> Result<(), Error> {
        let mut problems = raw::git_strarray {
            count: 0,
            strings: 0 as *mut *mut libc::c_char,
        };
        unsafe {
            try_call!(raw::git_remote_rename(&mut problems, self.raw,
                                             new_name.to_c_str()));
            let _s = StringArray::from_raw(problems);
        }
        Ok(())
    }

    /// Download new data and update tips
    ///
    /// Convenience function to connect to a remote, download the data,
    /// disconnect and update the remote-tracking branches.
    pub fn fetch(&mut self, signature: Option<&Signature>,
                 msg: Option<&str>) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_fetch(self.raw,
                                            &*signature.map(|s| s.raw())
                                                       .unwrap_or(0 as *mut _),
                                            msg.map(|s| s.to_c_str())));
        }
        Ok(())
    }

    /// Update the tips to the new state
    pub fn update_tips(&mut self, signature: Option<&Signature>,
                       msg: Option<&str>) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_update_tips(self.raw,
                                                  &*signature.map(|s| s.raw())
                                                             .unwrap_or(0 as *mut _),
                                                  msg.map(|s| s.to_c_str())));
        }
        Ok(())
    }

    /// Retrieve the update FETCH_HEAD setting.
    pub fn update_fetchhead(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_update_fetchhead(self.raw)); }
        Ok(())
    }
}

impl<'a> Iterator<Refspec<'a>> for Refspecs<'a> {
    fn next(&mut self) -> Option<Refspec<'a>> {
        if self.cur == self.cnt { return None }
        let ret = unsafe {
            let ptr = raw::git_remote_get_refspec(&*self.remote.raw,
                                                  self.cur as libc::size_t);
            assert!(!ptr.is_null());
            Refspec::from_raw(self.remote, ptr)
        };
        self.cur += 1;
        Some(ret)
    }
}

impl<'a> Clone for Remote<'a> {
    fn clone(&self) -> Remote<'a> {
        let mut ret = 0 as *mut raw::git_remote;
        let rc = unsafe { call!(raw::git_remote_dup(&mut ret, self.raw)) };
        assert_eq!(rc, 0);
        Remote {
            raw: ret,
            marker1: marker::ContravariantLifetime,
            marker2: marker::NoSend,
            marker3: marker::NoSync,
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Remote<'a> {
    fn drop(&mut self) {
        unsafe { raw::git_remote_free(self.raw) }
    }
}

#[cfg(test)]
mod tests {
    use std::io::TempDir;
    use {Repository, Remote, Signature};

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();
        repo.remote_create("origin", "/path/to/nowhere").unwrap();
        drop(repo);

        let repo = Repository::init(td.path()).unwrap();
        let origin = repo.remote_load("origin").unwrap();
        assert_eq!(origin.name(), Some("origin"));
        assert_eq!(origin.url(), Some("/path/to/nowhere"));
    }

    #[test]
    fn create_remote() {
        let td = TempDir::new("test").unwrap();
        let remote = td.path().join("remote");
        Repository::init_bare(&remote).unwrap();

        let (_td, repo) = ::test::repo_init();
        let url = if cfg!(unix) {
            format!("file://{}", remote.display())
        } else {
            format!("file:///{}", remote.display().to_string()
                                        .as_slice().replace("\\", "/"))
        };
        let mut origin = repo.remote_create("origin", url.as_slice()).unwrap();
        assert_eq!(origin.name(), Some("origin"));
        assert_eq!(origin.url(), Some(url.as_slice()));
        assert_eq!(origin.pushurl(), None);

        {
            let mut specs = origin.refspecs();
            let spec = specs.next().unwrap();
            assert!(specs.next().is_none());
            assert_eq!(spec.str(), Some("+refs/heads/*:refs/remotes/origin/*"));
            assert_eq!(spec.dst(), Some("refs/remotes/origin/*"));
            assert_eq!(spec.src(), Some("refs/heads/*"));
            assert!(spec.is_force());
        }
        {
            let remotes = repo.remote_list().unwrap();
            assert_eq!(remotes.len(), 1);
            assert_eq!(remotes.get(0), Some("origin"));
            assert_eq!(remotes.iter().count(), 1);
            assert_eq!(remotes.iter().next().unwrap(), Some("origin"));
        }

        origin.connect(::Push).unwrap();
        assert!(origin.connected());
        origin.disconnect();

        origin.connect(::Fetch).unwrap();
        assert!(origin.connected());
        origin.download().unwrap();
        origin.disconnect();

        origin.save().unwrap();

        origin.add_fetch("foo").unwrap();
        origin.add_fetch("bar").unwrap();
        origin.set_check_cert(true);
        origin.clear_refspecs();

        origin.set_fetch_refspecs(["foo"].iter().map(|a| *a)).unwrap();
        origin.set_push_refspecs(["foo"].iter().map(|a| *a)).unwrap();

        origin.rename("origin2").unwrap();
        let sig = Signature::default(&repo).unwrap();
        origin.fetch(Some(&sig), None).unwrap();
        origin.fetch(None, Some("foo")).unwrap();
        origin.update_tips(Some(&sig), None).unwrap();
        origin.update_tips(None, Some("foo")).unwrap();
        origin.delete().unwrap();
    }

    #[test]
    fn create_remote_anonymous() {
        let td = TempDir::new("test").unwrap();
        let repo = Repository::init(td.path()).unwrap();

        let origin = repo.remote_create_anonymous("/path/to/nowhere",
                                                  "master").unwrap();
        assert_eq!(origin.name(), None);
        drop(origin.clone());
    }

    #[test]
    fn is_valid() {
        assert!(Remote::is_valid_name("foobar"));
        assert!(!Remote::is_valid_name("\x01"));
        assert!(Remote::is_valid_url("http://example.com/foo/bar"));
        assert!(!Remote::is_valid_url("test"));
    }
}
<commit_msg>Fix a couple of typos<commit_after>use std::c_str::CString;
use std::kinds::marker;
use std::str;
use libc;

use {raw, Repository, Direction, Error, Refspec, StringArray};
use Signature;

/// A structure representing a [remote][1] of a git repository.
///
/// [1]: http://git-scm.com/book/en/Git-Basics-Working-with-Remotes
///
/// The lifetime is the lifetime of the repository that it is attached to. The
/// remote is used to manage fetches and pushes as well as refspecs.
pub struct Remote<'a> {
    raw: *mut raw::git_remote,
    marker1: marker::ContravariantLifetime<'a>,
    marker2: marker::NoSend,
    marker3: marker::NoSync,
}

/// An iterator over the refspecs that a remote contains.
pub struct Refspecs<'a> {
    cur: uint,
    cnt: uint,
    remote: &'a Remote<'a>,
}

impl<'a> Remote<'a> {
    /// Creates a new remote from its raw pointer.
    ///
    /// This method is unsafe as there is no guarantee that `raw` is valid or
    /// that no other remote is using it.
    pub unsafe fn from_raw(_repo: &Repository,
                           raw: *mut raw::git_remote) -> Remote {
        Remote {
            raw: raw,
            marker1: marker::ContravariantLifetime,
            marker2: marker::NoSend,
            marker3: marker::NoSync,
        }
    }

    /// Ensure the remote name is well-formed.
    pub fn is_valid_name(remote_name: &str) -> bool {
        ::init();
        let remote_name = remote_name.to_c_str();
        unsafe { raw::git_remote_is_valid_name(remote_name.as_ptr()) == 1 }
    }

    /// Return whether a string is a valid remote URL
    pub fn is_valid_url(url: &str) -> bool {
        ::init();
        let url = url.to_c_str();
        unsafe { raw::git_remote_valid_url(url.as_ptr()) == 1 }
    }

    /// Return whether the passed URL is supported by this version of the
    /// library.
    pub fn is_supported_url(url: &str) -> bool {
        ::init();
        let url = url.to_c_str();
        unsafe { raw::git_remote_supported_url(url.as_ptr()) == 1 }
    }

    /// Get the remote's name.
    ///
    /// Returns `None` if this remote has not yet been named or if the name is
    /// not valid utf-8
    pub fn name(&self) -> Option<&str> {
        self.name_bytes().and_then(str::from_utf8)
    }

    /// Get the remote's name, in bytes.
    ///
    /// Returns `None` if this remote has not yet been named
    pub fn name_bytes(&self) -> Option<&[u8]> {
        unsafe { ::opt_bytes(self, raw::git_remote_name(&*self.raw)) }
    }

    /// Get the remote's url.
    ///
    /// Returns `None` if the url is not valid utf-8
    pub fn url(&self) -> Option<&str> {
        str::from_utf8(self.url_bytes())
    }

    /// Get the remote's url as a byte array.
    pub fn url_bytes(&self) -> &[u8] {
        unsafe { ::opt_bytes(self, raw::git_remote_url(&*self.raw)).unwrap() }
    }

    /// Get the remote's pushurl.
    ///
    /// Returns `None` if the pushurl is not valid utf-8
    pub fn pushurl(&self) -> Option<&str> {
        self.pushurl_bytes().and_then(str::from_utf8)
    }

    /// Get the remote's pushurl as a byte array.
    pub fn pushurl_bytes(&self) -> Option<&[u8]> {
        unsafe { ::opt_bytes(self, raw::git_remote_pushurl(&*self.raw)) }
    }

    /// Open a connection to a remote.
    pub fn connect(&mut self, dir: Direction) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_connect(self.raw, dir));
        }
        Ok(())
    }

    /// Check whether the remote is connected
    pub fn connected(&mut self) -> bool {
        unsafe { raw::git_remote_connected(self.raw) == 1 }
    }

    /// Disconnect from the remote
    pub fn disconnect(&mut self) {
        unsafe { raw::git_remote_disconnect(self.raw) }
    }

    /// Save a remote to its repository's configuration
    ///
    /// Anonymous remotes cannot be saved
    pub fn save(&self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_save(&*self.raw)); }
        Ok(())
    }

    /// Add a fetch refspec to the remote
    pub fn add_fetch(&mut self, spec: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_add_fetch(self.raw, spec.to_c_str()));
        }
        Ok(())
    }

    /// Add a push refspec to the remote
    pub fn add_push(&mut self, spec: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_add_push(self.raw, spec.to_c_str()));
        }
        Ok(())
    }

    /// Choose whether to check the server's certificate (applies to HTTPS only)
    ///
    /// The default is yes.
    pub fn set_check_cert(&mut self, check: bool) {
        unsafe { raw::git_remote_check_cert(self.raw, check as libc::c_int) }
    }

    /// Set the remote's url
    ///
    /// Existing connections will not be updated.
    pub fn set_url(&mut self, url: &str) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_set_url(self.raw, url.to_c_str()));
        }
        Ok(())
    }

    /// Set the remote's pushurl.
    ///
    /// `None` indicates that it should be cleared.
    ///
    /// Existing connections will not be updated.
    pub fn set_pushurl(&mut self, pushurl: Option<&str>) -> Result<(), Error> {
        let pushurl = pushurl.map(|s| s.to_c_str());
        unsafe {
            try_call!(raw::git_remote_set_pushurl(self.raw, pushurl));
        }
        Ok(())
    }

    /// Sets the update FETCH_HEAD setting. By default, FETCH_HEAD will be
    /// updated on every fetch.
    pub fn set_update_fetchhead(&mut self, update: bool) {
        unsafe {
            raw::git_remote_set_update_fetchhead(self.raw, update as libc::c_int)
        }
    }

    /// Set the remote's list of fetch refspecs
    pub fn set_fetch_refspecs<T: ToCStr, I: Iterator<T>>(&mut self, i: I)
                                                         -> Result<(), Error> {
        let v = i.map(|t| t.to_c_str()).collect::<Vec<CString>>();
        let v2 = v.iter().map(|v| v.as_ptr()).collect::<Vec<*const libc::c_char>>();
        let mut arr = raw::git_strarray {
            strings: v2.as_ptr() as *mut _,
            count: v2.len() as libc::size_t,
        };

        unsafe {
            try_call!(raw::git_remote_set_fetch_refspecs(self.raw, &mut arr));
        }
        Ok(())
    }

    /// Set the remote's list of push refspecs
    pub fn set_push_refspecs<T: ToCStr, I: Iterator<T>>(&mut self, i: I)
                                                         -> Result<(), Error> {
        let v = i.map(|t| t.to_c_str()).collect::<Vec<CString>>();
        let v2 = v.iter().map(|v| v.as_ptr()).collect::<Vec<*const libc::c_char>>();
        let mut arr = raw::git_strarray {
            strings: v2.as_ptr() as *mut _,
            count: v2.len() as libc::size_t,
        };

        unsafe {
            try_call!(raw::git_remote_set_push_refspecs(self.raw, &mut arr));
        }
        Ok(())
    }

    /// Clear the refspecs
    ///
    /// Remove all configured fetch and push refspecs from the remote.
    pub fn clear_refspecs(&mut self) {
        unsafe { raw::git_remote_clear_refspecs(self.raw) }
    }

    /// Delete an existing persisted remote.
    ///
    /// All remote-tracking branches and configuration settings for the remote
    /// will be removed.
    pub fn delete(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_delete(self.raw)); }
        Ok(())
    }

    /// Download and index the packfile
    ///
    /// Connect to the remote if it hasn't been done yet, negotiate with the
    /// remote git which objects are missing, download and index the packfile.
    ///
    /// The .idx file will be created and both it and the packfile with be
    /// renamed to their final name.
    pub fn download(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_download(self.raw)); }
        Ok(())
    }

    /// Get the number of refspecs for a remote
    pub fn refspecs(&self) -> Refspecs {
        let cnt = unsafe { raw::git_remote_refspec_count(&*self.raw) as uint };
        Refspecs { cur: 0, cnt: cnt, remote: self }
    }

    /// Give the remote a new name
    ///
    /// All remote-tracking branches and configuration settings for the remote
    /// are updated.
    ///
    /// A temporary in-memory remote cannot be given a name with this method.
    pub fn rename(&mut self, new_name: &str) -> Result<(), Error> {
        let mut problems = raw::git_strarray {
            count: 0,
            strings: 0 as *mut *mut libc::c_char,
        };
        unsafe {
            try_call!(raw::git_remote_rename(&mut problems, self.raw,
                                             new_name.to_c_str()));
            let _s = StringArray::from_raw(problems);
        }
        Ok(())
    }

    /// Download new data and update tips
    ///
    /// Convenience function to connect to a remote, download the data,
    /// disconnect and update the remote-tracking branches.
    pub fn fetch(&mut self, signature: Option<&Signature>,
                 msg: Option<&str>) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_fetch(self.raw,
                                            &*signature.map(|s| s.raw())
                                                       .unwrap_or(0 as *mut _),
                                            msg.map(|s| s.to_c_str())));
        }
        Ok(())
    }

    /// Update the tips to the new state
    pub fn update_tips(&mut self, signature: Option<&Signature>,
                       msg: Option<&str>) -> Result<(), Error> {
        unsafe {
            try_call!(raw::git_remote_update_tips(self.raw,
                                                  &*signature.map(|s| s.raw())
                                                             .unwrap_or(0 as *mut _),
                                                  msg.map(|s| s.to_c_str())));
        }
        Ok(())
    }

    /// Retrieve the update FETCH_HEAD setting.
    pub fn update_fetchhead(&mut self) -> Result<(), Error> {
        unsafe { try_call!(raw::git_remote_update_fetchhead(self.raw)); }
        Ok(())
    }
}

impl<'a> Iterator<Refspec<'a>> for Refspecs<'a> {
    fn next(&mut self) -> Option<Refspec<'a>> {
        if self.cur == self.cnt { return None }
        let ret = unsafe {
            let ptr = raw::git_remote_get_refspec(&*self.remote.raw,
                                                  self.cur as libc::size_t);
            assert!(!ptr.is_null());
            Refspec::from_raw(self.remote, ptr)
        };
        self.cur += 1;
        Some(ret)
    }
}

impl<'a> Clone for Remote<'a> {
    fn clone(&self) -> Remote<'a> {
        let mut ret = 0 as *mut raw::git_remote;
        let rc = unsafe { call!(raw::git_remote_dup(&mut ret, self.raw)) };
        assert_eq!(rc, 0);
        Remote {
            raw: ret,
            marker1: marker::ContravariantLifetime,
            marker2: marker::NoSend,
            marker3: marker::NoSync,
        }
    }
}

#[unsafe_destructor]
impl<'a> Drop for Remote<'a> {
    fn drop(&mut self) {
        unsafe { raw::git_remote_free(self.raw) }
    }
}

#[cfg(test)]
mod tests {
    use std::io::TempDir;
    use {Repository, Remote, Signature};

    #[test]
    fn smoke() {
        let (td, repo) = ::test::repo_init();
        repo.remote_create("origin", "/path/to/nowhere").unwrap();
        drop(repo);

        let repo = Repository::init(td.path()).unwrap();
        let origin = repo.remote_load("origin").unwrap();
        assert_eq!(origin.name(), Some("origin"));
        assert_eq!(origin.url(), Some("/path/to/nowhere"));
    }

    #[test]
    fn create_remote() {
        let td = TempDir::new("test").unwrap();
        let remote = td.path().join("remote");
        Repository::init_bare(&remote).unwrap();

        let (_td, repo) = ::test::repo_init();
        let url = if cfg!(unix) {
            format!("file://{}", remote.display())
        } else {
            format!("file:///{}", remote.display().to_string()
                                        .as_slice().replace("\\", "/"))
        };
        let mut origin = repo.remote_create("origin", url.as_slice()).unwrap();
        assert_eq!(origin.name(), Some("origin"));
        assert_eq!(origin.url(), Some(url.as_slice()));
        assert_eq!(origin.pushurl(), None);

        {
            let mut specs = origin.refspecs();
            let spec = specs.next().unwrap();
            assert!(specs.next().is_none());
            assert_eq!(spec.str(), Some("+refs/heads/*:refs/remotes/origin/*"));
            assert_eq!(spec.dst(), Some("refs/remotes/origin/*"));
            assert_eq!(spec.src(), Some("refs/heads/*"));
            assert!(spec.is_force());
        }
        {
            let remotes = repo.remote_list().unwrap();
            assert_eq!(remotes.len(), 1);
            assert_eq!(remotes.get(0), Some("origin"));
            assert_eq!(remotes.iter().count(), 1);
            assert_eq!(remotes.iter().next().unwrap(), Some("origin"));
        }

        origin.connect(::Push).unwrap();
        assert!(origin.connected());
        origin.disconnect();

        origin.connect(::Fetch).unwrap();
        assert!(origin.connected());
        origin.download().unwrap();
        origin.disconnect();

        origin.save().unwrap();

        origin.add_fetch("foo").unwrap();
        origin.add_fetch("bar").unwrap();
        origin.set_check_cert(true);
        origin.clear_refspecs();

        origin.set_fetch_refspecs(["foo"].iter().map(|a| *a)).unwrap();
        origin.set_push_refspecs(["foo"].iter().map(|a| *a)).unwrap();

        origin.rename("origin2").unwrap();
        let sig = Signature::default(&repo).unwrap();
        origin.fetch(Some(&sig), None).unwrap();
        origin.fetch(None, Some("foo")).unwrap();
        origin.update_tips(Some(&sig), None).unwrap();
        origin.update_tips(None, Some("foo")).unwrap();
        origin.delete().unwrap();
    }

    #[test]
    fn create_remote_anonymous() {
        let td = TempDir::new("test").unwrap();
        let repo = Repository::init(td.path()).unwrap();

        let origin = repo.remote_create_anonymous("/path/to/nowhere",
                                                  "master").unwrap();
        assert_eq!(origin.name(), None);
        drop(origin.clone());
    }

    #[test]
    fn is_valid() {
        assert!(Remote::is_valid_name("foobar"));
        assert!(!Remote::is_valid_name("\x01"));
        assert!(Remote::is_valid_url("http://example.com/foo/bar"));
        assert!(!Remote::is_valid_url("test"));
    }
}
<|endoftext|>


<commit_before>#![feature(struct_variant)]

#[deriving(PartialEq, Show)]
enum Result<T> {
  Failed(String, String),  //ineff fix this
  Success(T, String)
}

impl<A> Result<A> {
  fn map<B>(self, f: |A| -> B) -> Result<B> {
    match self {
      Failed(e, remaining) => Failed(e.clone(), remaining),
      Success(v, remaining) => { Success(f(v), remaining) }}
  }
}

#[test]
fn map_should_only_modify_result() {
  let increment = |i: int| -> int { i + 1 };
  let initial = Success(1, str("unchanged"));
  assert_eq!(initial.map(increment), Success(2, str("unchanged")));
}

struct Parser<'a, T> {
  parser: |String|: 'a -> Result<T>
}

enum Either<E, R> {
  Left(E),
  Right(R)
}

struct Pred<'a, Arg, Res> {
  func: |Arg|: 'a -> Res
}

impl<'a, Arg, Res> Pred<'a, Arg, Res> {
  fn call(&mut self, arg: Arg) -> Res {
    (self.func)(arg)
  }
}

fn alloc_pred<Arg, Res>(f: |Arg| -> Res) -> Pred<Arg, Res> {
  Pred { func: f }
}

impl<'a, T> Parser<'a, T> {
  fn run(self, filename: String, mut input: String) -> Either<String, T> {
    println!("{}", filename);
    match self {
      Parser { parser } => {
        match parser(input) {
          Failed(errmsg, _) => Left(errmsg),
          Success(v, _) => Right(v)
        }
      }
    }
  }
}

pub trait Parser<A> {
  fn run(input : String) -> Result<A> {}
}

struct SatifsyParser<'a> {
  predicate: |char| -> bool
}

impl<'a> SatisfyParser<'a> {
  fn check_sat(&mut self, input: String) -> Result<char> {
    match input.pop_char() {
      Some(c) =>
        if (self.predicate)(c) { Success('a', input) }
        else { Success('a', input) },
      None => Failed(str("fucked"), input)
    }
  }
}

// impl<'a> Parser<char> {
  pub fn satisfy<'a>(pred: &Pred<'a, char, bool>) -> Parser<'a, char> {
    Parser { parser: |mut input: String| {
      match input.pop_char() {
        Some(c) => {
          let pred2 = pred;
          if true { Success('a', input) } else { Success('a', input) }},
        None => Failed(str("fucked"), input)
      }
    }}
  }
    //let my_pred = |c: char| true;
    /*Parser {
      parser: |input: String| {
                match input.pop_char() {
                  Some(first) => match my_pred(first) {
                    false => {
                      input.push_char(first);
                      Failed(str("statisfy: failed when demanding input"), input)
                    },
                    true => Success(first, input)
                  },
                  None => Failed(str("statisfy failed"), input)
                }
              }
    } */
// }

// impl Parser {
// fn satisfy(pred: |A|
//

fn str(s: &str) -> String { String::from_str(s) }

fn main() {
  let parserA = satisfy(&alloc_pred(|c: char| c == 'A'));
  let result = parserA.run(String::from_str("Testing!"), str("ABBBC"));
  let unpacked = match result {
    Left(e) => fail!(e),
    Right(v) => v
  };
  println!("{}", unpacked)
}
<commit_msg>First pass at initial redesign<commit_after>#![feature(struct_variant)]
use std::iter::FromIterator;

#[deriving(Show)]
pub enum Result<T> {
  Failed(String, Input),  //ineff fix this
  Success(T, Input)
}

impl<A> Result<A> {
  fn map<B>(self, f: |A| -> B) -> Result<B> {
    match self {
      Failed(e, remaining) => Failed(e.clone(), remaining),
      Success(v, remaining) => { Success(f(v), remaining) }}
  }
}

#[deriving(Show)]
pub struct Input {
  buffer: Vec<char>
}

impl Input {
  fn from_str(source: &str) -> Input {
    Input { buffer: FromIterator::from_iter(source.chars()) }
  }

  fn from_string(source: String) -> Input {
    Input { buffer: FromIterator::from_iter(source.as_slice().chars()) }
  }

  fn peek(&self) -> Option<char> {
    if self.buffer.len() > 0 {
      Some(self.buffer[0])
    } else {
      None
    }
  }

  fn pop(&mut self) -> Option<char> {
    self.buffer.pop()
  }
  // Eventually allow us to move through a mutable stream in
  // given a "view" of our data allowing for back tracking
  // in the parser
  // fn fast_forward
  // fn rewind
}

#[test]
fn input_should_only_be_modified_by_pop() {
  let input = Input::from_str("foo");
  let first = input.pop();
  let second = input.pop();
  let third = input.pop();
  let four = input.pop();
  assert_equal!(first, Some('f'));
  assert_equal!(second, Some('o'));
  assert_equal!(third, Some('o'));
  assert_equal!(four, None)
}

#[test]
fn map_should_only_modify_result() {
  let increment = |i: int| -> int { i + 1 };
  let initial = Success(1, str("unchanged"));
  assert_eq!(initial.map(increment), Success(2, str("unchanged")));
}

pub trait Parser<A> {
  fn run(&mut self, input : Input) -> Result<A>;
}

pub struct SatisfyParser<'a> {
  predicate: |char|: 'a -> bool
}

impl<'a> SatisfyParser<'a> {
  fn check_sat(&mut self, mut input: Input) -> Result<char> {
    match input.peek() {
      Some(c) =>
        if (self.predicate)(c) {
          match input.pop() {
            None => Failed(str("Failed!"), input),
            Some(c) => Success(c, input)
          }
        }
        else { Failed(str("Failed!"), input) },
      None => Failed(str("fucked"), input)
    }
  }
}

impl<'a> Parser<char> for SatisfyParser<'a> {
  fn run(&mut self, input: Input) -> Result<char> {
    self.check_sat(input)
  }
}

fn satisfy<'a>(pred: |char|: 'a -> bool) -> SatisfyParser<'a> {
    SatisfyParser { predicate: pred }
}

struct StringParser {
    str_match: String
}

fn string<'a>(str: String) -> StringParser {
  StringParser
}

#[test]
fn string_parser_should_match_only_the_literal_string() {
  let string_parser = string(str("A string"));
  let here_parser = string(str("here"));
  let input = Input::from_string("A string here");
  let result = match string_parser.run(input) {
    Failed(err, rest) => fail!(fmt("Parse was not successful: {}", err)),
    Success(v, input) => here_parser.run(input)
  };
  assert_equal!(result, Success(v, ""))
}

fn str(s: &str) -> String { String::from_str(s) }

fn main() {
  let mut parserA = satisfy(|c: char| c == 'A');
  let input = Input::from_string(String::from_str("A"));
  let result = parserA.run(input);
  let unpacked = match result {
    Failed(e, _) => fail!(e),
    Success(v, _) => v
  };
  println!("{}", unpacked)
}
<|endoftext|>


<commit_before>use std::net::{SocketAddrV4, UdpSocket};
use std::str;

use crate::common::{messages, parsing, SearchOptions};
use crate::errors::SearchError;
use crate::gateway::Gateway;

/// Search gateway, using the given `SearchOptions`.
///
/// The default `SearchOptions` should suffice in most cases.
/// It can be created with `Default::default()` or `SearchOptions::default()`.
///
/// # Example
/// ```no_run
/// use igd::{search_gateway, SearchOptions, Result};
///
/// fn main() -> Result {
///     let gateway = search_gateway(Default::default())?;
///     let ip = gateway.get_external_ip()?;
///     println!("External IP address: {}", ip);
///     Ok(())
/// }
/// ```
pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;

    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let location = parsing::parse_search_result(text)?;

        let control_url = match get_control_url(&location) {
            Ok(o) => o,
            Err(..) => continue,
        };

        return Ok(Gateway {
            addr: location.0,
            control_url: control_url,
        });
    }
}

fn get_control_url(location: &(SocketAddrV4, String)) -> Result<String, SearchError> {
    let url = format!("http://{}:{}{}", location.0.ip(), location.0.port(), location.1);
    let response = attohttpc::get(&url).send()?;
    parsing::parse_control_url(&response.bytes()?[..])
}
<commit_msg>Deconstruct location to increase readability<commit_after>use std::net::{SocketAddrV4, UdpSocket};
use std::str;

use crate::common::{messages, parsing, SearchOptions};
use crate::errors::SearchError;
use crate::gateway::Gateway;

/// Search gateway, using the given `SearchOptions`.
///
/// The default `SearchOptions` should suffice in most cases.
/// It can be created with `Default::default()` or `SearchOptions::default()`.
///
/// # Example
/// ```no_run
/// use igd::{search_gateway, SearchOptions, Result};
///
/// fn main() -> Result {
///     let gateway = search_gateway(Default::default())?;
///     let ip = gateway.get_external_ip()?;
///     println!("External IP address: {}", ip);
///     Ok(())
/// }
/// ```
pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
    let socket = UdpSocket::bind(options.bind_addr)?;
    socket.set_read_timeout(options.timeout)?;

    socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;

    loop {
        let mut buf = [0u8; 1500];
        let (read, _) = socket.recv_from(&mut buf)?;
        let text = str::from_utf8(&buf[..read])?;

        let (addr, root_url) = parsing::parse_search_result(text)?;

        let control_url = match get_control_url(&addr, &root_url) {
            Ok(o) => o,
            Err(..) => continue,
        };

        return Ok(Gateway {
            addr,
            control_url: control_url,
        });
    }
}

fn get_control_url(addr: &SocketAddrV4, root_url: &String) -> Result<String, SearchError> {
    let url = format!("http://{}:{}{}", addr.ip(), addr.port(), root_url);
    let response = attohttpc::get(&url).send()?;
    parsing::parse_control_url(&response.bytes()?[..])
}
<|endoftext|>


<commit_before>// ---------------------------------------------------------------------------------------
// Ruthenium, an ack-like searcher, (c) 2015 Georg Brandl.
// Licensed under the MIT license.
// ---------------------------------------------------------------------------------------

use std::cmp::min;
use std::path::Path;
use regex_dfa::Program as Regex;

use options::{Casing, Opts};

#[derive(Debug)]
pub struct Match {
    pub lineno: usize,
    pub line: String,
    pub spans: Vec<(usize, usize)>,
}

impl Match {
    fn new(lineno: usize, line: &str) -> Match {
        Match {
            lineno: lineno,
            line: line.into(),
            spans: Vec::new(),
        }
    }
}

#[derive(Debug)]
pub struct FileResult {
    pub fname: String,
    pub is_binary: bool,
    pub matches: Vec<Match>,
}

impl FileResult {
    fn new(fname: String) -> FileResult {
        FileResult {
            fname: fname,
            is_binary: false,
            matches: Vec::new(),
        }
    }
}

pub fn create_rx(opts: &Opts) -> Regex {
    let mut pattern = opts.pattern.to_owned();
    if opts.literal {
        const ESCAPE: &'static str = ".?*+|^$(){}[]\\";
        pattern = pattern.chars().map(|c| {
            if ESCAPE.find(c).is_some() {
                format!("\\{}", c)
            } else {
                format!("{}", c)
            }
        }).collect();
    }
    if let Casing::Insensitive = opts.casing {
        pattern = format!("(?i){}", pattern);
    } else if let Casing::Smart = opts.casing {
        if !pattern.chars().any(|c| c.is_uppercase()) {
            pattern = format!("(?i){}", pattern);
        }
    }
    Regex::from_regex(&pattern).unwrap()
}

pub fn is_binary(buf: &[u8], len: usize) -> bool {
    if len == 0 {
        return false;
    }
    if len >= 3 && &buf[0..3] == b"\xEF\xBB\xBF" {
        // UTF-8 BOM
        return false;
    }
    let n = min(512, len);
    for b in buf[..n].iter() {
        if *b == b'\x00' {
            return true;  // null byte always means binary
        }
    }
    false
}

pub fn search(regex: &Regex, opts: &Opts, path: &Path, buf: &[u8]) -> FileResult {
    let len = buf.len();
    let mut matches = 0;
    let mut result = FileResult::new(path.to_string_lossy().into_owned());
    if is_binary(buf, len) {
        result.is_binary = true;
        if opts.do_binaries {
            if let Ok(content) = ::std::str::from_utf8(buf) {
                if let Some((_, _)) = regex.shortest_match(&content) {
                    result.matches.push(Match::new(0, ""));
                }
            }
        }
    } else {
        let mut start = 0;
        let mut lineno = 0;
        while start < len {
            lineno += 1;
            let end = buf[start..].iter().position(|&x| x == b'\n').unwrap_or(len - start);
            let line = &buf[start..start+end];
            if let Ok(line) = ::std::str::from_utf8(line) {
                if let Some(idx) = regex.shortest_match(&line) {
                    let mut searchfrom = idx.1;
                    let mut m = Match::new(lineno, line);
                    m.spans.push(idx);
                    while let Some((i0, i1)) = regex.shortest_match(&line[searchfrom..]) {
                        m.spans.push((searchfrom + i0, searchfrom + i1));
                        searchfrom += i1;
                    }
                    result.matches.push(m);
                    matches += 1;
                    if opts.only_files.is_some() {
                        // need only one match for this mode
                        break;
                    } else if matches >= opts.max_count {
                        break;
                    }
                }
            }
            start += end + 1;
        }
    }
    result
}
<commit_msg>Normalize paths starting with "./".<commit_after>// ---------------------------------------------------------------------------------------
// Ruthenium, an ack-like searcher, (c) 2015 Georg Brandl.
// Licensed under the MIT license.
// ---------------------------------------------------------------------------------------

use std::cmp::min;
use std::path::Path;
use regex_dfa::Program as Regex;

use options::{Casing, Opts};

#[derive(Debug)]
pub struct Match {
    pub lineno: usize,
    pub line: String,
    pub spans: Vec<(usize, usize)>,
}

impl Match {
    fn new(lineno: usize, line: &str) -> Match {
        Match {
            lineno: lineno,
            line: line.into(),
            spans: Vec::new(),
        }
    }
}

#[derive(Debug)]
pub struct FileResult {
    pub fname: String,
    pub is_binary: bool,
    pub matches: Vec<Match>,
}

impl FileResult {
    fn new(fname: String) -> FileResult {
        FileResult {
            fname: fname,
            is_binary: false,
            matches: Vec::new(),
        }
    }
}

pub fn create_rx(opts: &Opts) -> Regex {
    let mut pattern = opts.pattern.to_owned();
    if opts.literal {
        const ESCAPE: &'static str = ".?*+|^$(){}[]\\";
        pattern = pattern.chars().map(|c| {
            if ESCAPE.find(c).is_some() {
                format!("\\{}", c)
            } else {
                format!("{}", c)
            }
        }).collect();
    }
    if let Casing::Insensitive = opts.casing {
        pattern = format!("(?i){}", pattern);
    } else if let Casing::Smart = opts.casing {
        if !pattern.chars().any(|c| c.is_uppercase()) {
            pattern = format!("(?i){}", pattern);
        }
    }
    Regex::from_regex(&pattern).unwrap()
}

fn normalized_path(path: &Path) -> String {
    let s = path.to_string_lossy();
    if s.starts_with("./") {
        String::from(&s[2..])
    } else if s.starts_with("//") {
        String::from(&s[1..])
    } else {
        String::from(&s[..])
    }
}

fn is_binary(buf: &[u8], len: usize) -> bool {
    if len == 0 {
        return false;
    }
    if len >= 3 && &buf[0..3] == b"\xEF\xBB\xBF" {
        // UTF-8 BOM
        return false;
    }
    let n = min(512, len);
    for b in buf[..n].iter() {
        if *b == b'\x00' {
            return true;  // null byte always means binary
        }
    }
    false
}

pub fn search(regex: &Regex, opts: &Opts, path: &Path, buf: &[u8]) -> FileResult {
    let len = buf.len();
    let mut matches = 0;
    let mut result = FileResult::new(normalized_path(path));
    let mut lines = Vec::new();
    if is_binary(buf, len) {
        result.is_binary = true;
        if opts.do_binaries {
            if let Ok(content) = ::std::str::from_utf8(buf) {
                if let Some((_, _)) = regex.shortest_match(&content) {
                    result.matches.push(Match::new(0, ""));
                }
            }
        }
    } else {
        let mut start = 0;
        let mut lineno = 0;
        while start < len {
            lineno += 1;
            let end = buf[start..].iter().position(|&x| x == b'\n').unwrap_or(len - start);
            let line = &buf[start..start+end];
            if let Ok(line) = ::std::str::from_utf8(line) {
                if let Some(idx) = regex.shortest_match(&line) {
                    let mut searchfrom = idx.1;
                    let mut m = Match::new(lineno, line);
                    m.spans.push(idx);
                    while let Some((i0, i1)) = regex.shortest_match(&line[searchfrom..]) {
                        m.spans.push((searchfrom + i0, searchfrom + i1));
                        searchfrom += i1;
                    }
                    result.matches.push(m);
                    matches += 1;
                    if opts.only_files.is_some() {
                        // need only one match for this mode
                        break;
                    } else if matches >= opts.max_count {
                        break;
                    }
                }
            }
            start += end + 1;
        }
    }
    result
}
<|endoftext|>


<commit_before>use std::thread;
use std::path::Path;
use std::cell::RefCell;

use websocket::{Server, Message, Sender, Receiver};
use websocket::message::Type;
use websocket::header::WebSocketProtocol;

extern crate md5;

extern crate git2;
use self::git2::Repository;
use self::git2::Reference;
use self::git2::Statuses;
use self::git2::StatusOptions;
use self::git2::Delta;
use self::git2::ObjectType;

extern crate yaml_rust;
use self::yaml_rust::YamlLoader;

use project;

use serde_xml as xml;

#[derive(Debug, Serialize, Deserialize)]
struct Commit {
    message: String,
    include: Vec<String>
}

impl Commit {
}

pub fn start() {
    let server = Server::bind("127.0.0.1:2794").unwrap();
    thread::spawn(move || {
        for connection in server {
            thread::spawn(move || {
                let request = connection.unwrap().read_request().unwrap();
                let headers = request.headers.clone();
                request.validate().unwrap();
                let mut response = request.accept();
                println!("Connected");

                if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
                    if protocols.contains(&("rust-websocket".to_string())) {
                        response.headers.set(WebSocketProtocol(vec!["superconductor".to_string()]));
                    }
                }

                let mut client = response.send().unwrap();
                let message = Message::text(generate_payload());
                client.send_message(&message).unwrap();

                let (mut sender, mut receiver) = client.split();
                for message in receiver.incoming_messages() {
                    let message: Message = message.unwrap();
                    match message.opcode {
                        Type::Close => {
                            let message = Message::close();
                            sender.send_message(&message).unwrap();
                            println!("Client disconnected");
                            return;
                        },
                        Type::Ping => {
                            let message = Message::pong(message.payload);
                            sender.send_message(&message).unwrap();
                        },
                        _ => {
                            let payload = String::from_utf8_lossy(message.payload.as_ref());
                            let commit: Commit = xml::from_str(&payload).unwrap();
                            println!("{:?}", commit);
                            let repo = Repository::discover(".").unwrap();
                            let head = repo.head().unwrap().peel(ObjectType::Commit).unwrap();
                            let mut index = repo.index().unwrap();

                            let to_remove = index.iter().fold(vec![], |mut acc, entry| {
                                let entry_path = String::from_utf8_lossy(entry.path.as_ref());
                                match commit.include.iter().find(|i| i.as_ref() == entry_path) {
                                    None => acc.push(entry_path.into_owned()),
                                    _ => {}
                                };
                                acc
                            });

                            println!("removed: {:?}", to_remove);
                            repo.reset_default(Some(&head), to_remove.iter());
                            for change in commit.include {
                                let path = Path::new(&change);
                                index.add_path(path);
                            }
                            index.write().unwrap();
                            let message = Message::text(generate_payload());
                            sender.send_message(&message).unwrap();
                        }
                    }
                }
            });
        }
    });
}

fn generate_payload() -> String {
    let current = project::current();
    let repo = Repository::discover(".").unwrap();
    let mut revwalk = repo.revwalk().unwrap();
    revwalk.set_sorting(git2::SORT_REVERSE);
    revwalk.push_head();
    let mut status_opts = StatusOptions::new();
    status_opts.include_untracked(true);

    let payload = html! {
        state {
            user {
                name  (current.user.name)
                    email (current.user.email)
            }
            task {
                id (current.task.id)
            }
            log {
                @for rev in revwalk {
                    @let commit = repo.find_commit(rev.unwrap()).unwrap() {
                        commit {
                            id (commit.id())
                            user {
                                @let author = commit.author() {
                                    name  (author.name().unwrap())
                                        @let email = author.email().unwrap().trim() {
                                            email (email)
                                            image (format!("https://www.gravatar.com/avatar/{:x}?s=64", md5::compute(email.to_lowercase())))
                                        }
                                }
                            }
                            @let mut message = commit.message().unwrap().split("\n---") {
                                message (message.next().unwrap())
                                @if let Some(yaml) = message.next() {
                                    @for (objective, values) in YamlLoader::load_from_str(yaml).unwrap()[0].as_hash().unwrap() {
                                        objective {
                                            name (objective.as_str().unwrap_or("None"))
                                            @for (name, value) in values.as_hash().unwrap() {
                                                property {
                                                    name (name.as_str().unwrap_or("None"))
                                                    after (value.as_str().unwrap_or("None"))
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            changes {
                @for change in repo.statuses(Some(&mut status_opts)).unwrap().iter() {
                    change {
                        path (change.path().unwrap())
                        included @match change.head_to_index().map(|d| d.status()).unwrap_or(Delta::Unreadable) {
                            Delta::Modified | Delta::Added | Delta::Deleted => "true",
                            _ => "false"
                        }
                        removal @match change.head_to_index().map(|d| d.status()).unwrap_or(Delta::Unreadable) {
                            Delta::Deleted => "true",
                            _ => "false"
                        }
                    }
                }
            }
        }
    }.into_string();
    payload
}
<commit_msg>fixes unwrap exception<commit_after>use std::thread;
use std::path::Path;
use std::cell::RefCell;

use websocket::{Server, Message, Sender, Receiver};
use websocket::message::Type;
use websocket::header::WebSocketProtocol;

extern crate md5;

extern crate git2;
use self::git2::Repository;
use self::git2::Reference;
use self::git2::Statuses;
use self::git2::StatusOptions;
use self::git2::Delta;
use self::git2::ObjectType;

extern crate yaml_rust;
use self::yaml_rust::YamlLoader;

use project;

use serde_xml as xml;

#[derive(Debug, Serialize, Deserialize)]
struct Commit {
    message: String,
    include: Vec<String>
}

impl Commit {
}

pub fn start() {
    let server = Server::bind("127.0.0.1:2794").unwrap();
    thread::spawn(move || {
        for connection in server {
            thread::spawn(move || {
                let request = connection.unwrap().read_request().unwrap();
                let headers = request.headers.clone();
                request.validate().unwrap();
                let mut response = request.accept();
                println!("Connected");

                if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
                    if protocols.contains(&("rust-websocket".to_string())) {
                        response.headers.set(WebSocketProtocol(vec!["superconductor".to_string()]));
                    }
                }

                let mut client = response.send().unwrap();
                let message = Message::text(generate_payload());
                client.send_message(&message).unwrap();

                let (mut sender, mut receiver) = client.split();
                for message in receiver.incoming_messages() {
                    let message: Message = message.unwrap_or(Message::close());
                    match message.opcode {
                        Type::Close => {
                            let message = Message::close();
                            sender.send_message(&message).unwrap();
                            println!("Client disconnected");
                            return;
                        },
                        Type::Ping => {
                            let message = Message::pong(message.payload);
                            sender.send_message(&message).unwrap();
                        },
                        _ => {
                            let payload = String::from_utf8_lossy(message.payload.as_ref());
                            let commit: Commit = xml::from_str(&payload).unwrap();
                            println!("{:?}", commit);
                            let repo = Repository::discover(".").unwrap();
                            let head = repo.head().unwrap().peel(ObjectType::Commit).unwrap();
                            let mut index = repo.index().unwrap();

                            let to_remove = index.iter().fold(vec![], |mut acc, entry| {
                                let entry_path = String::from_utf8_lossy(entry.path.as_ref());
                                match commit.include.iter().find(|i| i.as_ref() == entry_path) {
                                    None => acc.push(entry_path.into_owned()),
                                    _ => {}
                                };
                                acc
                            });

                            repo.reset_default(Some(&head), to_remove.iter());
                            for change in commit.include {
                                let path = Path::new(&change);
                                index.add_path(path);
                            }
                            index.write().unwrap();
                            let message = Message::text(generate_payload());
                            sender.send_message(&message).unwrap();
                        }
                    }
                }
            });
        }
    });
}

fn generate_payload() -> String {
    let current = project::current();
    let repo = Repository::discover(".").unwrap();
    let mut revwalk = repo.revwalk().unwrap();
    revwalk.set_sorting(git2::SORT_REVERSE);
    revwalk.push_head();
    let mut status_opts = StatusOptions::new();
    status_opts.include_untracked(true);

    let payload = html! {
        state {
            user {
                name  (current.user.name)
                    email (current.user.email)
            }
            task {
                id (current.task.id)
            }
            log {
                @for rev in revwalk {
                    @let commit = repo.find_commit(rev.unwrap()).unwrap() {
                        commit {
                            id (commit.id())
                            user {
                                @let author = commit.author() {
                                    name  (author.name().unwrap())
                                        @let email = author.email().unwrap().trim() {
                                            email (email)
                                            image (format!("https://www.gravatar.com/avatar/{:x}?s=64", md5::compute(email.to_lowercase())))
                                        }
                                }
                            }
                            @let mut message = commit.message().unwrap().split("\n---") {
                                message (message.next().unwrap())
                                @if let Some(yaml) = message.next() {
                                    @for (objective, values) in YamlLoader::load_from_str(yaml).unwrap()[0].as_hash().unwrap() {
                                        objective {
                                            name (objective.as_str().unwrap_or("None"))
                                            @for (name, value) in values.as_hash().unwrap() {
                                                property {
                                                    name (name.as_str().unwrap_or("None"))
                                                    after (value.as_str().unwrap_or("None"))
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            changes {
                @for change in repo.statuses(Some(&mut status_opts)).unwrap().iter() {
                    change {
                        path (change.path().unwrap())
                        included @match change.head_to_index().map(|d| d.status()).unwrap_or(Delta::Unreadable) {
                            Delta::Modified | Delta::Added | Delta::Deleted => "true",
                            _ => "false"
                        }
                        removal @match change.head_to_index().map(|d| d.status()).unwrap_or(Delta::Unreadable) {
                            Delta::Deleted => "true",
                            _ => "false"
                        }
                    }
                }
            }
        }
    }.into_string();
    payload
}
<|endoftext|>


<commit_before>//! `Server` listens for HTTP requests.
//!
//!```rust
//!# use rustful::server::Server;
//!# use rustful::router::Router;
//!# let routes = [];
//!let server = Server {
//!	handlers: Router::from_routes(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use request::Request;
use response::Response;
use response::status::NotFound;

use http;
use http::server::{ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath};
use http::headers::content_type::MediaType;
use http::method::Post;

use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use std::str::from_utf8_owned;
use std::uint;
use std::io::BufReader;
use std::vec::Vec;

use collections::hashmap::HashMap;

use time;

use HTTP = http::server::Server;

///A handler function for routing.
pub type HandlerFn = fn(&Request, &mut Response);

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	pub handlers: Router<HandlerFn>,

	///The port where the server will listen for requests
	pub port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(0, 0, 0, 0),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &http::server::request::Request, writer: &mut ResponseWriter) {
		let mut response = Response::new(writer);
		response.headers.date = Some(time::now_utc());
		response.headers.content_type = Some(MediaType {
			type_: StrBuf::from_str("text"),
			subtype: StrBuf::from_str("plain"),
			parameters: vec!((StrBuf::from_str("charset"), StrBuf::from_str("UTF-8")))
		});
		response.headers.server = Some(StrBuf::from_str("rustful"));

		let found = match build_request(request) {
			Some(mut request) => match self.handlers.find(request.method.clone(), request.path) {
				Some((&handler, variables)) => {
					request.variables = variables;
					handler(&request, &mut response);
					true
				},
				None => false
			},
			None => false
		};

		if !found {
			let content = bytes!("File not found");
			response.headers.content_length = Some(content.len());
			response.status = NotFound;
			match response.write(content) {
				Err(e) => println!("error while writing 404: {}", e),
				_ => {}
			}
		}

		response.end();
	}
}

fn build_request<'a>(request: &'a http::server::request::Request) -> Option<Request<'a>> {
	let path = match request.request_uri {
		AbsoluteUri(ref url) => {
			Some((url.path.as_slice(), url.query.iter().map(|v| v.clone()).collect(), url.fragment.as_ref().map(|v| v.as_slice())))
		},
		AbsolutePath(ref path) => Some(parse_path(path.as_slice())),
		_ => None
	};

	match path {
		Some((path, query, fragment)) => {
			let post = if request.method == Post {
				parse_parameters(request.body.as_slice())
			} else {
				HashMap::new()
			};

			Some(Request {
				headers: *request.headers.clone(),
				method: request.method.clone(),
				path: path,
				variables: HashMap::new(),
				post: post,
				query: query,
				fragment: fragment,
				body: request.body.as_slice()
			})
		}
		None => None
	}
}

fn parse_parameters(source: &str) -> HashMap<~str, ~str> {
	let mut parameters = HashMap::new();
	for parameter in source.split('&') {
		let mut parts = parameter.split('=');
		parts.next().map(|name|
			parameters.insert(
				url_decode(name),
				parts.next().map(|v| url_decode(v)).unwrap_or_else(|| "".to_owned())
			)
		);
	}

	parameters
}

fn parse_path<'a>(path: &'a str) -> (&'a str, HashMap<~str, ~str>, Option<&'a str>) {
	match path.find('?') {
		Some(index) => {
			let (query, fragment) = parse_fragment(path.slice(index+1, path.len()));
			(path.slice(0, index), parse_parameters(query), fragment)
		},
		None => {
			let (path, fragment) = parse_fragment(path);
			(path, HashMap::new(), fragment)
		}
	}
}

fn parse_fragment<'a>(path: &'a str) -> (&'a str, Option<&'a str>) {
	match path.find('#') {
		Some(index) => (path.slice(0, index), Some(path.slice(index+1, path.len()))),
		None => (path, None)
	}
}

fn url_decode(string: &str) -> ~str {
	let mut rdr = BufReader::new(string.as_bytes());
	let mut out = Vec::new();

	loop {
		let mut buf = [0];
		let ch = match rdr.read(buf) {
			Err(..) => break,
			Ok(..) => buf[0] as char
		};
		match ch {
			'+' => out.push(' ' as u8),
			'%' => {
				let mut bytes = [0, 0];
				match rdr.read(bytes) {
					Ok(2) => {}
					_ => fail!()
				}
				let ch = uint::parse_bytes(bytes, 16u).unwrap() as u8;
				out.push(ch);
			}
			ch => out.push(ch as u8)
		}
	}

	from_utf8_owned(out.as_slice().to_owned()).unwrap_or(string.to_owned())
}

#[test]
fn parsing_parameters() {
	let parameters = parse_parameters("a=1&aa=2&ab=202");
	let a = "1".to_owned();
	let aa = "2".to_owned();
	let ab = "202".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"aa".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parsing_parameters_with_plus() {
	let parameters = parse_parameters("a=1&aa=2+%2B+extra+meat&ab=202+fifth+avenue");
	let a = "1".to_owned();
	let aa = "2 + extra meat".to_owned();
	let ab = "202 fifth avenue".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"aa".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parsing_strange_parameters() {
	let parameters = parse_parameters("a=1=2&=2&ab=");
	let a = "1".to_owned();
	let aa = "2".to_owned();
	let ab = "".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parse_path_parts() {
	let with = "this".to_owned();
	let and = "that".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=that#lol");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some("lol"));
}

#[test]
fn parse_strange_path() {
	let with = "this".to_owned();
	let and = "what?".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=what?#");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some(""));
}

#[test]
fn parse_missing_path_parts() {
	let with = "this".to_owned();
	let and = "that".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=that");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, None);

	let (path, query, fragment) = parse_path("/path/to/something#lol");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.len(), 0);
	assert_eq!(fragment, Some("lol"));

	let (path, query, fragment) = parse_path("?with=this&and=that#lol");
	assert_eq!(path, "");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some("lol"));
}<commit_msg>url.query contains StrBuf intead of ~str<commit_after>//! `Server` listens for HTTP requests.
//!
//!```rust
//!# use rustful::server::Server;
//!# use rustful::router::Router;
//!# let routes = [];
//!let server = Server {
//!	handlers: Router::from_routes(routes),
//!	port: 8080
//!};
//!
//!server.run();
//!```

use router::Router;
use request::Request;
use response::Response;
use response::status::NotFound;

use http;
use http::server::{ResponseWriter, Config};
use http::server::request::{AbsoluteUri, AbsolutePath};
use http::headers::content_type::MediaType;
use http::method::Post;

use std::io::net::ip::{SocketAddr, Ipv4Addr, Port};
use std::str::from_utf8_owned;
use std::uint;
use std::io::BufReader;
use std::vec::Vec;

use collections::hashmap::HashMap;

use time;

use HTTP = http::server::Server;

///A handler function for routing.
pub type HandlerFn = fn(&Request, &mut Response);

#[deriving(Clone)]
pub struct Server {
	///A routing tree with response handlers
	pub handlers: Router<HandlerFn>,

	///The port where the server will listen for requests
	pub port: Port
}

impl Server {
	///Start the server and run forever.
	///This will only return if the initial connection fails.
	pub fn run(&self) {
		self.clone().serve_forever();
	}
}

impl HTTP for Server {
	fn get_config(&self) -> Config {
		Config {
			bind_address: SocketAddr {
				ip: Ipv4Addr(0, 0, 0, 0),
				port: self.port
			}
		}
	}

	fn handle_request(&self, request: &http::server::request::Request, writer: &mut ResponseWriter) {
		let mut response = Response::new(writer);
		response.headers.date = Some(time::now_utc());
		response.headers.content_type = Some(MediaType {
			type_: StrBuf::from_str("text"),
			subtype: StrBuf::from_str("plain"),
			parameters: vec!((StrBuf::from_str("charset"), StrBuf::from_str("UTF-8")))
		});
		response.headers.server = Some(StrBuf::from_str("rustful"));

		let found = match build_request(request) {
			Some(mut request) => match self.handlers.find(request.method.clone(), request.path) {
				Some((&handler, variables)) => {
					request.variables = variables;
					handler(&request, &mut response);
					true
				},
				None => false
			},
			None => false
		};

		if !found {
			let content = bytes!("File not found");
			response.headers.content_length = Some(content.len());
			response.status = NotFound;
			match response.write(content) {
				Err(e) => println!("error while writing 404: {}", e),
				_ => {}
			}
		}

		response.end();
	}
}

fn build_request<'a>(request: &'a http::server::request::Request) -> Option<Request<'a>> {
	let path = match request.request_uri {
		AbsoluteUri(ref url) => {
			Some((
				url.path.as_slice(),
				url.query.iter().map(|&(ref k, ref v)| (k.to_owned(), v.to_owned()) ).collect(),
				url.fragment.as_ref().map(|v| v.as_slice())
			))
		},
		AbsolutePath(ref path) => Some(parse_path(path.as_slice())),
		_ => None
	};

	match path {
		Some((path, query, fragment)) => {
			let post = if request.method == Post {
				parse_parameters(request.body.as_slice())
			} else {
				HashMap::new()
			};

			Some(Request {
				headers: *request.headers.clone(),
				method: request.method.clone(),
				path: path,
				variables: HashMap::new(),
				post: post,
				query: query,
				fragment: fragment,
				body: request.body.as_slice()
			})
		}
		None => None
	}
}

fn parse_parameters(source: &str) -> HashMap<~str, ~str> {
	let mut parameters = HashMap::new();
	for parameter in source.split('&') {
		let mut parts = parameter.split('=');
		parts.next().map(|name|
			parameters.insert(
				url_decode(name),
				parts.next().map(|v| url_decode(v)).unwrap_or_else(|| "".to_owned())
			)
		);
	}

	parameters
}

fn parse_path<'a>(path: &'a str) -> (&'a str, HashMap<~str, ~str>, Option<&'a str>) {
	match path.find('?') {
		Some(index) => {
			let (query, fragment) = parse_fragment(path.slice(index+1, path.len()));
			(path.slice(0, index), parse_parameters(query), fragment)
		},
		None => {
			let (path, fragment) = parse_fragment(path);
			(path, HashMap::new(), fragment)
		}
	}
}

fn parse_fragment<'a>(path: &'a str) -> (&'a str, Option<&'a str>) {
	match path.find('#') {
		Some(index) => (path.slice(0, index), Some(path.slice(index+1, path.len()))),
		None => (path, None)
	}
}

fn url_decode(string: &str) -> ~str {
	let mut rdr = BufReader::new(string.as_bytes());
	let mut out = Vec::new();

	loop {
		let mut buf = [0];
		let ch = match rdr.read(buf) {
			Err(..) => break,
			Ok(..) => buf[0] as char
		};
		match ch {
			'+' => out.push(' ' as u8),
			'%' => {
				let mut bytes = [0, 0];
				match rdr.read(bytes) {
					Ok(2) => {}
					_ => fail!()
				}
				let ch = uint::parse_bytes(bytes, 16u).unwrap() as u8;
				out.push(ch);
			}
			ch => out.push(ch as u8)
		}
	}

	from_utf8_owned(out.as_slice().to_owned()).unwrap_or(string.to_owned())
}

#[test]
fn parsing_parameters() {
	let parameters = parse_parameters("a=1&aa=2&ab=202");
	let a = "1".to_owned();
	let aa = "2".to_owned();
	let ab = "202".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"aa".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parsing_parameters_with_plus() {
	let parameters = parse_parameters("a=1&aa=2+%2B+extra+meat&ab=202+fifth+avenue");
	let a = "1".to_owned();
	let aa = "2 + extra meat".to_owned();
	let ab = "202 fifth avenue".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"aa".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parsing_strange_parameters() {
	let parameters = parse_parameters("a=1=2&=2&ab=");
	let a = "1".to_owned();
	let aa = "2".to_owned();
	let ab = "".to_owned();
	assert_eq!(parameters.find(&"a".to_owned()), Some(&a));
	assert_eq!(parameters.find(&"".to_owned()), Some(&aa));
	assert_eq!(parameters.find(&"ab".to_owned()), Some(&ab));
}

#[test]
fn parse_path_parts() {
	let with = "this".to_owned();
	let and = "that".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=that#lol");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some("lol"));
}

#[test]
fn parse_strange_path() {
	let with = "this".to_owned();
	let and = "what?".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=what?#");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some(""));
}

#[test]
fn parse_missing_path_parts() {
	let with = "this".to_owned();
	let and = "that".to_owned();
	let (path, query, fragment) = parse_path("/path/to/something?with=this&and=that");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, None);

	let (path, query, fragment) = parse_path("/path/to/something#lol");
	assert_eq!(path, "/path/to/something");
	assert_eq!(query.len(), 0);
	assert_eq!(fragment, Some("lol"));

	let (path, query, fragment) = parse_path("?with=this&and=that#lol");
	assert_eq!(path, "");
	assert_eq!(query.find(&"with".to_owned()), Some(&with));
	assert_eq!(query.find(&"and".to_owned()), Some(&and));
	assert_eq!(fragment, Some("lol"));
}<|endoftext|>