<commit_before>use core::clone::Clone;
use core::ops::Deref;
use diesel;
use diesel::expression::sql;
use diesel::prelude::*;
use diesel::BelongingToDsl;
use diesel::types::*;
use std::path::Path;
#[cfg(test)]
use db;
use db::ConnectionSource;
use db::{playlists, playlist_songs, users};
use index::Song;
use vfs::VFSSource;
use errors::*;

#[derive(Insertable)]
#[table_name="playlists"]
struct NewPlaylist {
	name: String,
	owner: i32,
}

#[derive(Identifiable, Queryable)]
pub struct User {
	id: i32,
	name: String,
}

#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(User, foreign_key="owner")]
pub struct Playlist {
	id: i32,
	owner: i32,
	name: String,
}

#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(Playlist, foreign_key="playlist")]
pub struct PlaylistSong {
	id: i32,
	playlist: i32,
	path: String,
	ordering: i32,
}

#[derive(Insertable)]
#[table_name="playlist_songs"]
pub struct NewPlaylistSong {
	playlist: i32,
	path: String,
	ordering: i32,
}

fn list_playlists<T>(owner: &str, db: &T) -> Result<Vec<String>>
	where T: ConnectionSource + VFSSource
{
	let connection = db.get_connection();

	let user: User;
	{
		use self::users::dsl::*;
		user = users
			.filter(name.eq(owner))
			.select((id, name))
			.first(connection.deref())?;
	}

	{
		use self::playlists::dsl::*;
		let found_playlists: Vec<String> = Playlist::belonging_to(&user)
			.select(name)
			.load(connection.deref())?;
		Ok(found_playlists)
	}
}

fn save_playlist<T>(name: &str, owner: &str, content: &Vec<String>, db: &T) -> Result<()>
	where T: ConnectionSource + VFSSource
{
	let user: User;
	let new_playlist: NewPlaylist;
	let playlist: Playlist;
	let vfs = db.get_vfs()?;

	{
		let connection = db.get_connection();

		// Find owner
		{
			use self::users::dsl::*;
			user = users
				.filter(name.eq(owner))
				.select((id, name))
				.get_result(connection.deref())?;
		}

		// Create playlist
		new_playlist = NewPlaylist {
			name: name.into(),
			owner: user.id,
		};

		diesel::insert(&new_playlist)
			.into(playlists::table)
			.execute(connection.deref())?;

		{
			use self::playlists::dsl::*;
			playlist = playlists
				.filter(name.eq(name).and(owner.eq(user.id)))
				.get_result(connection.deref())?;
		}
	}

	let mut new_songs: Vec<NewPlaylistSong> = Vec::new();
	new_songs.reserve(content.len());

	for (i, path) in content.iter().enumerate() {
		let virtual_path = Path::new(&path);
		if let Some(real_path) = vfs.virtual_to_real(virtual_path)
		       .ok()
		       .and_then(|p| p.to_str().map(|s| s.to_owned())) {
			new_songs.push(NewPlaylistSong {
			                   playlist: playlist.id,
			                   path: real_path.into(),
			                   ordering: i as i32,
			               });
		}
	}

	{
		let connection = db.get_connection();
		connection
			.deref()
			.transaction::<_, diesel::result::Error, _>(|| {
				// Delete old content (if any)
				let old_songs = PlaylistSong::belonging_to(&playlist);
				diesel::delete(old_songs).execute(connection.deref())?;

				// Insert content
				diesel::insert(&new_songs)
					.into(playlist_songs::table)
					.execute(connection.deref())?;
				Ok(())
			})?;
	}

	Ok(())
}

fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song>>
	where T: ConnectionSource + VFSSource
{
	let vfs = db.get_vfs()?;
	let songs: Vec<Song>;

	{
		let connection = db.get_connection();
		let user: User;
		let playlist: Playlist;

		// Find owner
		{
			use self::users::dsl::*;
			user = users
				.filter(name.eq(owner))
				.select((id, name))
				.get_result(connection.deref())?;
		}

		// Find playlist
		{
			use self::playlists::dsl::*;
			playlist = playlists
				.filter(name.eq(playlist_name).and(owner.eq(user.id)))
				.get_result(connection.deref())?;
		}

		// Select songs. Not using Diesel because we need to LEFT JOIN using a custom column
		let query = sql::<(Integer, Text, Text, Nullable<Integer>, Nullable<Integer>, Nullable<Text>, Nullable<Text>, Nullable<Text>, Nullable<Integer>, Nullable<Text>, Nullable<Text>)>(r#"
			SELECT s.id, s.path, s.parent, s.track_number, s.disc_number, s.title, s.artist, s.album_artist, s.year, s.album, s.artwork
			FROM playlist_songs ps
			LEFT JOIN songs s ON ps.path = s.path
			WHERE ps.playlist = ?
			ORDER BY ps.ordering
		"#);
		let query = query.clone().bind::<Integer, _>(playlist.id);
		songs = query.get_results(connection.deref())?;
	}

	// Map real path to virtual paths
	let songs = songs
		.into_iter()
		.filter_map(|mut s| {
			let real_path = s.path.clone();
			let real_path = Path::new(&real_path);
			if let Ok(virtual_path) = vfs.real_to_virtual(real_path) {
				if let Some(virtual_path) = virtual_path.to_str() {
					s.path = virtual_path.to_owned();
				}
				return Some(s);
			}
			None
		})
		.collect();

	Ok(songs)
}

fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
	where T: ConnectionSource + VFSSource
{
	let connection = db.get_connection();

	let user: User;
	{
		use self::users::dsl::*;
		user = users
			.filter(name.eq(owner))
			.select((id, name))
			.first(connection.deref())?;
	}

	{
		use self::playlists::dsl::*;
		let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
		diesel::delete(q).execute(connection.deref())?;
	}

	Ok(())
}

#[test]
fn test_create_playlist() {
	let db = db::_get_test_db("create_playlist.sqlite");

	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert!(found_playlists.is_empty());

	save_playlist("chill_and_grill", "test_user", &Vec::new(), &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 1);
	assert_eq!(found_playlists[0], "chill_and_grill");

	let found_playlists = list_playlists("someone_else", &db);
	assert!(found_playlists.is_err());
}

#[test]
fn test_delete_playlist() {
	let db = db::_get_test_db("delete_playlist.sqlite");
	let playlist_content = Vec::new();

	save_playlist("chill_and_grill", "test_user", &playlist_content, &db).unwrap();
	save_playlist("mellow_bungalow", "test_user", &playlist_content, &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 2);

	delete_playlist("chill_and_grill", "test_user", &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 1);
	assert_eq!(found_playlists[0], "mellow_bungalow");

	let delete_result = delete_playlist("mellow_bungalow", "someone_else", &db);
	assert!(delete_result.is_err());
}

#[test]
fn test_fill_playlist() {
	use index;

	let db = db::_get_test_db("fill_playlist.sqlite");
	index::update(&db).unwrap();

	let mut playlist_content: Vec<String> = index::flatten(&db, Path::new("root"))
		.unwrap()
		.into_iter()
		.map(|s| s.path)
		.collect();
	assert_eq!(playlist_content.len(), 12);

	let first_song = playlist_content[0].clone();
	playlist_content.push(first_song);
	assert_eq!(playlist_content.len(), 13);

	save_playlist("all_the_music", "test_user", &playlist_content, &db).unwrap();

	let songs = read_playlist("all_the_music", "test_user", &db).unwrap();
	assert_eq!(songs.len(), 13);
	assert_eq!(songs[0].title, Some("Above The Water".to_owned()));
	assert_eq!(songs[12].title, Some("Above The Water".to_owned()));

	use std::path::PathBuf;
	let mut first_song_path = PathBuf::new();
	first_song_path.push("root");
	first_song_path.push("Khemmis");
	first_song_path.push("Hunted");
	first_song_path.push("01 - Above The Water.mp3");
	assert_eq!(songs[0].path, first_song_path.to_str().unwrap());

	// Save again to verify that we don't dupe the content
	save_playlist("all_the_music", "test_user", &playlist_content, &db).unwrap();
	let songs = read_playlist("all_the_music", "test_user", &db).unwrap();
	assert_eq!(songs.len(), 13);
}
<commit_msg>Removed unecessary field<commit_after>use core::clone::Clone;
use core::ops::Deref;
use diesel;
use diesel::expression::sql;
use diesel::prelude::*;
use diesel::BelongingToDsl;
use diesel::types::*;
use std::path::Path;
#[cfg(test)]
use db;
use db::ConnectionSource;
use db::{playlists, playlist_songs, users};
use index::Song;
use vfs::VFSSource;
use errors::*;

#[derive(Insertable)]
#[table_name="playlists"]
struct NewPlaylist {
	name: String,
	owner: i32,
}

#[derive(Identifiable, Queryable)]
pub struct User {
	id: i32,
}

#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(User, foreign_key="owner")]
pub struct Playlist {
	id: i32,
	owner: i32,
	name: String,
}

#[derive(Identifiable, Queryable, Associations)]
#[belongs_to(Playlist, foreign_key="playlist")]
pub struct PlaylistSong {
	id: i32,
	playlist: i32,
	path: String,
	ordering: i32,
}

#[derive(Insertable)]
#[table_name="playlist_songs"]
pub struct NewPlaylistSong {
	playlist: i32,
	path: String,
	ordering: i32,
}

fn list_playlists<T>(owner: &str, db: &T) -> Result<Vec<String>>
	where T: ConnectionSource + VFSSource
{
	let connection = db.get_connection();

	let user: User;
	{
		use self::users::dsl::*;
		user = users
			.filter(name.eq(owner))
			.select((id,))
			.first(connection.deref())?;
	}

	{
		use self::playlists::dsl::*;
		let found_playlists: Vec<String> = Playlist::belonging_to(&user)
			.select(name)
			.load(connection.deref())?;
		Ok(found_playlists)
	}
}

fn save_playlist<T>(name: &str, owner: &str, content: &Vec<String>, db: &T) -> Result<()>
	where T: ConnectionSource + VFSSource
{
	let user: User;
	let new_playlist: NewPlaylist;
	let playlist: Playlist;
	let vfs = db.get_vfs()?;

	{
		let connection = db.get_connection();

		// Find owner
		{
			use self::users::dsl::*;
			user = users
				.filter(name.eq(owner))
				.select((id,))
				.get_result(connection.deref())?;
		}

		// Create playlist
		new_playlist = NewPlaylist {
			name: name.into(),
			owner: user.id,
		};

		diesel::insert(&new_playlist)
			.into(playlists::table)
			.execute(connection.deref())?;

		{
			use self::playlists::dsl::*;
			playlist = playlists
				.filter(name.eq(name).and(owner.eq(user.id)))
				.get_result(connection.deref())?;
		}
	}

	let mut new_songs: Vec<NewPlaylistSong> = Vec::new();
	new_songs.reserve(content.len());

	for (i, path) in content.iter().enumerate() {
		let virtual_path = Path::new(&path);
		if let Some(real_path) = vfs.virtual_to_real(virtual_path)
		       .ok()
		       .and_then(|p| p.to_str().map(|s| s.to_owned())) {
			new_songs.push(NewPlaylistSong {
			                   playlist: playlist.id,
			                   path: real_path.into(),
			                   ordering: i as i32,
			               });
		}
	}

	{
		let connection = db.get_connection();
		connection
			.deref()
			.transaction::<_, diesel::result::Error, _>(|| {
				// Delete old content (if any)
				let old_songs = PlaylistSong::belonging_to(&playlist);
				diesel::delete(old_songs).execute(connection.deref())?;

				// Insert content
				diesel::insert(&new_songs)
					.into(playlist_songs::table)
					.execute(connection.deref())?;
				Ok(())
			})?;
	}

	Ok(())
}

fn read_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<Vec<Song>>
	where T: ConnectionSource + VFSSource
{
	let vfs = db.get_vfs()?;
	let songs: Vec<Song>;

	{
		let connection = db.get_connection();
		let user: User;
		let playlist: Playlist;

		// Find owner
		{
			use self::users::dsl::*;
			user = users
				.filter(name.eq(owner))
				.select((id,))
				.get_result(connection.deref())?;
		}

		// Find playlist
		{
			use self::playlists::dsl::*;
			playlist = playlists
				.filter(name.eq(playlist_name).and(owner.eq(user.id)))
				.get_result(connection.deref())?;
		}

		// Select songs. Not using Diesel because we need to LEFT JOIN using a custom column
		let query = sql::<(Integer, Text, Text, Nullable<Integer>, Nullable<Integer>, Nullable<Text>, Nullable<Text>, Nullable<Text>, Nullable<Integer>, Nullable<Text>, Nullable<Text>)>(r#"
			SELECT s.id, s.path, s.parent, s.track_number, s.disc_number, s.title, s.artist, s.album_artist, s.year, s.album, s.artwork
			FROM playlist_songs ps
			LEFT JOIN songs s ON ps.path = s.path
			WHERE ps.playlist = ?
			ORDER BY ps.ordering
		"#);
		let query = query.clone().bind::<Integer, _>(playlist.id);
		songs = query.get_results(connection.deref())?;
	}

	// Map real path to virtual paths
	let songs = songs
		.into_iter()
		.filter_map(|mut s| {
			let real_path = s.path.clone();
			let real_path = Path::new(&real_path);
			if let Ok(virtual_path) = vfs.real_to_virtual(real_path) {
				if let Some(virtual_path) = virtual_path.to_str() {
					s.path = virtual_path.to_owned();
				}
				return Some(s);
			}
			None
		})
		.collect();

	Ok(songs)
}

fn delete_playlist<T>(playlist_name: &str, owner: &str, db: &T) -> Result<()>
	where T: ConnectionSource + VFSSource
{
	let connection = db.get_connection();

	let user: User;
	{
		use self::users::dsl::*;
		user = users
			.filter(name.eq(owner))
			.select((id,))
			.first(connection.deref())?;
	}

	{
		use self::playlists::dsl::*;
		let q = Playlist::belonging_to(&user).filter(name.eq(playlist_name));
		diesel::delete(q).execute(connection.deref())?;
	}

	Ok(())
}

#[test]
fn test_create_playlist() {
	let db = db::_get_test_db("create_playlist.sqlite");

	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert!(found_playlists.is_empty());

	save_playlist("chill_and_grill", "test_user", &Vec::new(), &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 1);
	assert_eq!(found_playlists[0], "chill_and_grill");

	let found_playlists = list_playlists("someone_else", &db);
	assert!(found_playlists.is_err());
}

#[test]
fn test_delete_playlist() {
	let db = db::_get_test_db("delete_playlist.sqlite");
	let playlist_content = Vec::new();

	save_playlist("chill_and_grill", "test_user", &playlist_content, &db).unwrap();
	save_playlist("mellow_bungalow", "test_user", &playlist_content, &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 2);

	delete_playlist("chill_and_grill", "test_user", &db).unwrap();
	let found_playlists = list_playlists("test_user", &db).unwrap();
	assert_eq!(found_playlists.len(), 1);
	assert_eq!(found_playlists[0], "mellow_bungalow");

	let delete_result = delete_playlist("mellow_bungalow", "someone_else", &db);
	assert!(delete_result.is_err());
}

#[test]
fn test_fill_playlist() {
	use index;

	let db = db::_get_test_db("fill_playlist.sqlite");
	index::update(&db).unwrap();

	let mut playlist_content: Vec<String> = index::flatten(&db, Path::new("root"))
		.unwrap()
		.into_iter()
		.map(|s| s.path)
		.collect();
	assert_eq!(playlist_content.len(), 12);

	let first_song = playlist_content[0].clone();
	playlist_content.push(first_song);
	assert_eq!(playlist_content.len(), 13);

	save_playlist("all_the_music", "test_user", &playlist_content, &db).unwrap();

	let songs = read_playlist("all_the_music", "test_user", &db).unwrap();
	assert_eq!(songs.len(), 13);
	assert_eq!(songs[0].title, Some("Above The Water".to_owned()));
	assert_eq!(songs[12].title, Some("Above The Water".to_owned()));

	use std::path::PathBuf;
	let mut first_song_path = PathBuf::new();
	first_song_path.push("root");
	first_song_path.push("Khemmis");
	first_song_path.push("Hunted");
	first_song_path.push("01 - Above The Water.mp3");
	assert_eq!(songs[0].path, first_song_path.to_str().unwrap());

	// Save again to verify that we don't dupe the content
	save_playlist("all_the_music", "test_user", &playlist_content, &db).unwrap();
	let songs = read_playlist("all_the_music", "test_user", &db).unwrap();
	assert_eq!(songs.len(), 13);
}
<|endoftext|>


<commit_before>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;
use std::fmt::{Show, Formatter, FormatError};

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;
use Url;
use rust_url;

pub use http::server::response::ResponseWriter as HttpResponse;

use content_type::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader + Send>>,

    /// The headers of the response.
    pub headers: HeaderCollection,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>),
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new Response with the `location` header set to the specified url.
    pub fn redirect(status: status::Status, url: Url) -> Response {
        let mut headers = HeaderCollection::new();
        headers.location = Some(rust_url::Url::parse(url.to_string().as_slice()).unwrap());
        Response {
            body: None,
            headers: headers,
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a response from a file on disk.
    ///
    /// The status code is set to 200 OK and the content type is autodetected based on
    /// the file extension.
    pub fn from_file(path: &Path) -> IoResult<Response> {
        let file = try!(File::open(path));
        let mut response = Response::new();
        response.status = Some(status::Ok);
        response.body = Some(box file as Box<Reader + Send>);
        response.headers.content_type = path.extension_str().and_then(get_content_type);
        Ok(response)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader + Send>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

impl Show for Response {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        try!(writeln!(f, "Response {{"));

        try!(writeln!(f, "    status: {}", self.status));

        try!(write!(f, "}}"));
        Ok(())
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>(cleanup) Cleaned up redirect URL conversion.<commit_after>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;
use std::fmt::{Show, Formatter, FormatError};

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;
use Url;

pub use http::server::response::ResponseWriter as HttpResponse;

use content_type::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader + Send>>,

    /// The headers of the response.
    pub headers: HeaderCollection,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>),
            headers: HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new Response with the `location` header set to the specified url.
    pub fn redirect(status: status::Status, url: Url) -> Response {
        let mut headers = HeaderCollection::new();
        headers.location = Some(url.into_generic_url());
        Response {
            body: None,
            headers: headers,
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a response from a file on disk.
    ///
    /// The status code is set to 200 OK and the content type is autodetected based on
    /// the file extension.
    pub fn from_file(path: &Path) -> IoResult<Response> {
        let file = try!(File::open(path));
        let mut response = Response::new();
        response.status = Some(status::Ok);
        response.body = Some(box file as Box<Reader + Send>);
        response.headers.content_type = path.extension_str().and_then(get_content_type);
        Ok(response)
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader + Send>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

impl Show for Response {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> {
        try!(writeln!(f, "Response {{"));

        try!(writeln!(f, "    status: {}", self.status));

        try!(write!(f, "}}"));
        Ok(())
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>use libc::c_char;
use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::mem::transmute;
use std::rc::Rc;

use crate::sys;

#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = sys::SDL_errorcode::SDL_ENOMEM as i32,
    ReadError = sys::SDL_errorcode::SDL_EFREAD as i32,
    WriteError = sys::SDL_errorcode::SDL_EFWRITE as i32,
    SeekError = sys::SDL_errorcode::SDL_EFSEEK as i32,
    UnsupportedError = sys::SDL_errorcode::SDL_UNSUPPORTED as i32,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match *self {
            NoMemError => write!(f, "Out of memory"),
            ReadError => write!(f, "Error reading from datastream"),
            WriteError => write!(f, "Error writing to datastream"),
            SeekError => write!(f, "Error seeking in datastream"),
            UnsupportedError => write!(f, "Unknown SDL error"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        use self::Error::*;

        match *self {
            NoMemError => "out of memory",
            ReadError => "error reading from datastream",
            WriteError => "error writing to datastream",
            SeekError => "error seeking in datastream",
            UnsupportedError => "unknown SDL error",
        }
    }
}

use std::sync::atomic::AtomicBool;
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = AtomicBool::new(false);

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
#[derive(Clone)]
pub struct Sdl {
    sdldrop: Rc<SdlDrop>,
}

impl Sdl {
    #[inline]
    #[doc(alias = "SDL_Init")]
    fn new() -> Result<Sdl, String> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
            } else if sys::SDL_Init(0) == 0 {
                // Initialize SDL without any explicit subsystems (flags = 0).
                Ok(Sdl {
                    sdldrop: Rc::new(SdlDrop),
                })
            } else {
                IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                Err(get_error())
            }
        }
    }

    /// Initializes the audio subsystem.
    #[inline]
    pub fn audio(&self) -> Result<AudioSubsystem, String> {
        AudioSubsystem::new(self)
    }

    /// Initializes the event subsystem.
    #[inline]
    pub fn event(&self) -> Result<EventSubsystem, String> {
        EventSubsystem::new(self)
    }

    /// Initializes the joystick subsystem.
    #[inline]
    pub fn joystick(&self) -> Result<JoystickSubsystem, String> {
        JoystickSubsystem::new(self)
    }

    /// Initializes the haptic subsystem.
    #[inline]
    pub fn haptic(&self) -> Result<HapticSubsystem, String> {
        HapticSubsystem::new(self)
    }

    /// Initializes the game controller subsystem.
    #[inline]
    pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> {
        GameControllerSubsystem::new(self)
    }

    /// Initializes the timer subsystem.
    #[inline]
    pub fn timer(&self) -> Result<TimerSubsystem, String> {
        TimerSubsystem::new(self)
    }

    /// Initializes the video subsystem.
    #[inline]
    pub fn video(&self) -> Result<VideoSubsystem, String> {
        VideoSubsystem::new(self)
    }

    /// Obtains the SDL event pump.
    ///
    /// At most one `EventPump` is allowed to be alive during the program's execution.
    /// If this function is called while an `EventPump` instance is alive, the function will return
    /// an error.
    #[inline]
    pub fn event_pump(&self) -> Result<EventPump, String> {
        EventPump::new(self)
    }

    #[inline]
    #[doc(hidden)]
    pub fn sdldrop(&self) -> Rc<SdlDrop> {
        self.sdldrop.clone()
    }
}

/// When SDL is no longer in use (the refcount in an `Rc<SdlDrop>` reaches 0), the library is quit.
#[doc(hidden)]
#[derive(Debug)]
pub struct SdlDrop;

impl Drop for SdlDrop {
    #[inline]
    #[doc(alias = "SDL_Quit")]
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe {
            sys::SDL_Quit();
        }
    }
}

// No subsystem can implement `Send` because the destructor, `SDL_QuitSubSystem`,
// utilizes non-atomic reference counting and should thus be called on a single thread.
// Some subsystems have functions designed to be thread-safe, such as adding a timer or accessing
// the event queue. These subsystems implement `Sync`.

macro_rules! subsystem {
    ($name:ident, $flag:expr) => {
        impl $name {
            #[inline]
            #[doc(alias = "SDL_InitSubSystem")]
            fn new(sdl: &Sdl) -> Result<$name, String> {
                let result = unsafe { sys::SDL_InitSubSystem($flag) };

                if result == 0 {
                    Ok($name {
                        _subsystem_drop: Rc::new(SubsystemDrop {
                            _sdldrop: sdl.sdldrop.clone(),
                            flag: $flag,
                        }),
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    };
    ($name:ident, $flag:expr, nosync) => {
        #[derive(Debug, Clone)]
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>,
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl {
                    sdldrop: self._subsystem_drop._sdldrop.clone(),
                }
            }
        }

        subsystem!($name, $flag);
    };
    ($name:ident, $flag:expr, sync) => {
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>,
        }
        unsafe impl Sync for $name {}

        impl std::clone::Clone for $name {
            #[inline]
            fn clone(&self) -> $name {
                $name {
                    _subsystem_drop: self._subsystem_drop.clone(),
                }
            }
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl {
                    sdldrop: self._subsystem_drop._sdldrop.clone(),
                }
            }
        }

        subsystem!($name, $flag);
    };
}

/// When a subsystem is no longer in use (the refcount in an `Rc<SubsystemDrop>` reaches 0),
/// the subsystem is quit.
#[derive(Debug, Clone)]
struct SubsystemDrop {
    _sdldrop: Rc<SdlDrop>,
    flag: u32,
}

impl Drop for SubsystemDrop {
    #[inline]
    #[doc(alias = "SDL_QuitSubSystem")]
    fn drop(&mut self) {
        unsafe {
            sys::SDL_QuitSubSystem(self.flag);
        }
    }
}

subsystem!(AudioSubsystem, sys::SDL_INIT_AUDIO, nosync);
subsystem!(
    GameControllerSubsystem,
    sys::SDL_INIT_GAMECONTROLLER,
    nosync
);
subsystem!(HapticSubsystem, sys::SDL_INIT_HAPTIC, nosync);
subsystem!(JoystickSubsystem, sys::SDL_INIT_JOYSTICK, nosync);
subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
// Timers can be added on other threads.
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
// The event queue can be read from other threads.
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);

static mut IS_EVENT_PUMP_ALIVE: bool = false;

/// A thread-safe type that encapsulates SDL event-pumping functions.
pub struct EventPump {
    _sdldrop: Rc<SdlDrop>,
}

impl EventPump {
    /// Obtains the SDL event pump.
    #[inline]
    #[doc(alias = "SDL_InitSubSystem")]
    fn new(sdl: &Sdl) -> Result<EventPump, String> {
        // Called on the main SDL thread.

        unsafe {
            if IS_EVENT_PUMP_ALIVE {
                Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
            } else {
                // Initialize the events subsystem, just in case none of the other subsystems have done it yet.
                let result = sys::SDL_InitSubSystem(sys::SDL_INIT_EVENTS);

                if result == 0 {
                    IS_EVENT_PUMP_ALIVE = true;

                    Ok(EventPump {
                        _sdldrop: sdl.sdldrop.clone(),
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    }
}

impl Drop for EventPump {
    #[inline]
    #[doc(alias = "SDL_QuitSubSystem")]
    fn drop(&mut self) {
        // Called on the main SDL thread.

        unsafe {
            assert!(IS_EVENT_PUMP_ALIVE);
            sys::SDL_QuitSubSystem(sys::SDL_INIT_EVENTS);
            IS_EVENT_PUMP_ALIVE = false;
        }
    }
}

/// Get platform name
#[inline]
#[doc(alias = "SDL_GetPlatform")]
pub fn get_platform() -> &'static str {
    unsafe { CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap() }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let mut event_pump = sdl_context.event_pump().unwrap();
///
/// for event in event_pump.poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
#[inline]
#[doc(alias = "SDL_GetError")]
pub fn init() -> Result<Sdl, String> {
    Sdl::new()
}

pub fn get_error() -> String {
    unsafe {
        let err = sys::SDL_GetError();
        CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
    }
}

#[doc(alias = "SDL_SetError")]
pub fn set_error(err: &str) -> Result<(), NulError> {
    let c_string = CString::new(err)?;
    unsafe {
        sys::SDL_SetError(c_string.as_ptr() as *const c_char);
    }
    Ok(())
}

#[doc(alias = "SDL_Error")]
pub fn set_error_from_code(err: Error) {
    unsafe {
        sys::SDL_Error(transmute(err));
    }
}

#[doc(alias = "SDL_ClearError")]
pub fn clear_error() {
    unsafe {
        sys::SDL_ClearError();
    }
}
<commit_msg>sdl.rs: fix memory safety issue in set_error<commit_after>use libc::c_char;
use std::error;
use std::ffi::{CStr, CString, NulError};
use std::fmt;
use std::mem::transmute;
use std::rc::Rc;

use crate::sys;

#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum Error {
    NoMemError = sys::SDL_errorcode::SDL_ENOMEM as i32,
    ReadError = sys::SDL_errorcode::SDL_EFREAD as i32,
    WriteError = sys::SDL_errorcode::SDL_EFWRITE as i32,
    SeekError = sys::SDL_errorcode::SDL_EFSEEK as i32,
    UnsupportedError = sys::SDL_errorcode::SDL_UNSUPPORTED as i32,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match *self {
            NoMemError => write!(f, "Out of memory"),
            ReadError => write!(f, "Error reading from datastream"),
            WriteError => write!(f, "Error writing to datastream"),
            SeekError => write!(f, "Error seeking in datastream"),
            UnsupportedError => write!(f, "Unknown SDL error"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        use self::Error::*;

        match *self {
            NoMemError => "out of memory",
            ReadError => "error reading from datastream",
            WriteError => "error writing to datastream",
            SeekError => "error seeking in datastream",
            UnsupportedError => "unknown SDL error",
        }
    }
}

use std::sync::atomic::AtomicBool;
/// Only one Sdl context can be alive at a time.
/// Set to false by default (not alive).
static IS_SDL_CONTEXT_ALIVE: AtomicBool = AtomicBool::new(false);

/// The SDL context type. Initialize with `sdl2::init()`.
///
/// From a thread-safety perspective, `Sdl` represents the main thread.
/// As such, `Sdl` is a useful type for ensuring that SDL types that can only
/// be used on the main thread are initialized that way.
///
/// For instance, `SDL_PumpEvents()` is not thread safe, and may only be
/// called on the main thread.
/// All functionality that calls `SDL_PumpEvents()` is thus put into an
/// `EventPump` type, which can only be obtained through `Sdl`.
/// This guarantees that the only way to call event-pumping functions is on
/// the main thread.
#[derive(Clone)]
pub struct Sdl {
    sdldrop: Rc<SdlDrop>,
}

impl Sdl {
    #[inline]
    #[doc(alias = "SDL_Init")]
    fn new() -> Result<Sdl, String> {
        unsafe {
            use std::sync::atomic::Ordering;

            // Atomically switch the `IS_SDL_CONTEXT_ALIVE` global to true
            let was_alive = IS_SDL_CONTEXT_ALIVE.swap(true, Ordering::Relaxed);

            if was_alive {
                Err("Cannot initialize `Sdl` more than once at a time.".to_owned())
            } else if sys::SDL_Init(0) == 0 {
                // Initialize SDL without any explicit subsystems (flags = 0).
                Ok(Sdl {
                    sdldrop: Rc::new(SdlDrop),
                })
            } else {
                IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
                Err(get_error())
            }
        }
    }

    /// Initializes the audio subsystem.
    #[inline]
    pub fn audio(&self) -> Result<AudioSubsystem, String> {
        AudioSubsystem::new(self)
    }

    /// Initializes the event subsystem.
    #[inline]
    pub fn event(&self) -> Result<EventSubsystem, String> {
        EventSubsystem::new(self)
    }

    /// Initializes the joystick subsystem.
    #[inline]
    pub fn joystick(&self) -> Result<JoystickSubsystem, String> {
        JoystickSubsystem::new(self)
    }

    /// Initializes the haptic subsystem.
    #[inline]
    pub fn haptic(&self) -> Result<HapticSubsystem, String> {
        HapticSubsystem::new(self)
    }

    /// Initializes the game controller subsystem.
    #[inline]
    pub fn game_controller(&self) -> Result<GameControllerSubsystem, String> {
        GameControllerSubsystem::new(self)
    }

    /// Initializes the timer subsystem.
    #[inline]
    pub fn timer(&self) -> Result<TimerSubsystem, String> {
        TimerSubsystem::new(self)
    }

    /// Initializes the video subsystem.
    #[inline]
    pub fn video(&self) -> Result<VideoSubsystem, String> {
        VideoSubsystem::new(self)
    }

    /// Obtains the SDL event pump.
    ///
    /// At most one `EventPump` is allowed to be alive during the program's execution.
    /// If this function is called while an `EventPump` instance is alive, the function will return
    /// an error.
    #[inline]
    pub fn event_pump(&self) -> Result<EventPump, String> {
        EventPump::new(self)
    }

    #[inline]
    #[doc(hidden)]
    pub fn sdldrop(&self) -> Rc<SdlDrop> {
        self.sdldrop.clone()
    }
}

/// When SDL is no longer in use (the refcount in an `Rc<SdlDrop>` reaches 0), the library is quit.
#[doc(hidden)]
#[derive(Debug)]
pub struct SdlDrop;

impl Drop for SdlDrop {
    #[inline]
    #[doc(alias = "SDL_Quit")]
    fn drop(&mut self) {
        use std::sync::atomic::Ordering;

        let was_alive = IS_SDL_CONTEXT_ALIVE.swap(false, Ordering::Relaxed);
        assert!(was_alive);

        unsafe {
            sys::SDL_Quit();
        }
    }
}

// No subsystem can implement `Send` because the destructor, `SDL_QuitSubSystem`,
// utilizes non-atomic reference counting and should thus be called on a single thread.
// Some subsystems have functions designed to be thread-safe, such as adding a timer or accessing
// the event queue. These subsystems implement `Sync`.

macro_rules! subsystem {
    ($name:ident, $flag:expr) => {
        impl $name {
            #[inline]
            #[doc(alias = "SDL_InitSubSystem")]
            fn new(sdl: &Sdl) -> Result<$name, String> {
                let result = unsafe { sys::SDL_InitSubSystem($flag) };

                if result == 0 {
                    Ok($name {
                        _subsystem_drop: Rc::new(SubsystemDrop {
                            _sdldrop: sdl.sdldrop.clone(),
                            flag: $flag,
                        }),
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    };
    ($name:ident, $flag:expr, nosync) => {
        #[derive(Debug, Clone)]
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>,
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl {
                    sdldrop: self._subsystem_drop._sdldrop.clone(),
                }
            }
        }

        subsystem!($name, $flag);
    };
    ($name:ident, $flag:expr, sync) => {
        pub struct $name {
            /// Subsystems cannot be moved or (usually) used on non-main threads.
            /// Luckily, Rc restricts use to the main thread.
            _subsystem_drop: Rc<SubsystemDrop>,
        }
        unsafe impl Sync for $name {}

        impl std::clone::Clone for $name {
            #[inline]
            fn clone(&self) -> $name {
                $name {
                    _subsystem_drop: self._subsystem_drop.clone(),
                }
            }
        }

        impl $name {
            /// Obtain an SDL context.
            #[inline]
            pub fn sdl(&self) -> Sdl {
                Sdl {
                    sdldrop: self._subsystem_drop._sdldrop.clone(),
                }
            }
        }

        subsystem!($name, $flag);
    };
}

/// When a subsystem is no longer in use (the refcount in an `Rc<SubsystemDrop>` reaches 0),
/// the subsystem is quit.
#[derive(Debug, Clone)]
struct SubsystemDrop {
    _sdldrop: Rc<SdlDrop>,
    flag: u32,
}

impl Drop for SubsystemDrop {
    #[inline]
    #[doc(alias = "SDL_QuitSubSystem")]
    fn drop(&mut self) {
        unsafe {
            sys::SDL_QuitSubSystem(self.flag);
        }
    }
}

subsystem!(AudioSubsystem, sys::SDL_INIT_AUDIO, nosync);
subsystem!(
    GameControllerSubsystem,
    sys::SDL_INIT_GAMECONTROLLER,
    nosync
);
subsystem!(HapticSubsystem, sys::SDL_INIT_HAPTIC, nosync);
subsystem!(JoystickSubsystem, sys::SDL_INIT_JOYSTICK, nosync);
subsystem!(VideoSubsystem, sys::SDL_INIT_VIDEO, nosync);
// Timers can be added on other threads.
subsystem!(TimerSubsystem, sys::SDL_INIT_TIMER, sync);
// The event queue can be read from other threads.
subsystem!(EventSubsystem, sys::SDL_INIT_EVENTS, sync);

static mut IS_EVENT_PUMP_ALIVE: bool = false;

/// A thread-safe type that encapsulates SDL event-pumping functions.
pub struct EventPump {
    _sdldrop: Rc<SdlDrop>,
}

impl EventPump {
    /// Obtains the SDL event pump.
    #[inline]
    #[doc(alias = "SDL_InitSubSystem")]
    fn new(sdl: &Sdl) -> Result<EventPump, String> {
        // Called on the main SDL thread.

        unsafe {
            if IS_EVENT_PUMP_ALIVE {
                Err("an `EventPump` instance is already alive - there can only be one `EventPump` in use at a time.".to_owned())
            } else {
                // Initialize the events subsystem, just in case none of the other subsystems have done it yet.
                let result = sys::SDL_InitSubSystem(sys::SDL_INIT_EVENTS);

                if result == 0 {
                    IS_EVENT_PUMP_ALIVE = true;

                    Ok(EventPump {
                        _sdldrop: sdl.sdldrop.clone(),
                    })
                } else {
                    Err(get_error())
                }
            }
        }
    }
}

impl Drop for EventPump {
    #[inline]
    #[doc(alias = "SDL_QuitSubSystem")]
    fn drop(&mut self) {
        // Called on the main SDL thread.

        unsafe {
            assert!(IS_EVENT_PUMP_ALIVE);
            sys::SDL_QuitSubSystem(sys::SDL_INIT_EVENTS);
            IS_EVENT_PUMP_ALIVE = false;
        }
    }
}

/// Get platform name
#[inline]
#[doc(alias = "SDL_GetPlatform")]
pub fn get_platform() -> &'static str {
    unsafe { CStr::from_ptr(sys::SDL_GetPlatform()).to_str().unwrap() }
}

/// Initializes the SDL library.
/// This must be called before using any other SDL function.
///
/// # Example
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let mut event_pump = sdl_context.event_pump().unwrap();
///
/// for event in event_pump.poll_iter() {
///     // ...
/// }
///
/// // SDL_Quit() is called here as `sdl_context` is dropped.
/// ```
#[inline]
#[doc(alias = "SDL_GetError")]
pub fn init() -> Result<Sdl, String> {
    Sdl::new()
}

pub fn get_error() -> String {
    unsafe {
        let err = sys::SDL_GetError();
        CStr::from_ptr(err as *const _).to_str().unwrap().to_owned()
    }
}

#[doc(alias = "SDL_SetError")]
pub fn set_error(err: &str) -> Result<(), NulError> {
    let c_string = CString::new(err)?;
    unsafe {
        sys::SDL_SetError(b"%s\0".as_ptr() as *const c_char, c_string.as_ptr() as *const c_char);
    }
    Ok(())
}

#[doc(alias = "SDL_Error")]
pub fn set_error_from_code(err: Error) {
    unsafe {
        sys::SDL_Error(transmute(err));
    }
}

#[doc(alias = "SDL_ClearError")]
pub fn clear_error() {
    unsafe {
        sys::SDL_ClearError();
    }
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#![crate_name="thrift-client"]
#![crate_type="bin"]

//use std::io::net::ip::SocketAddr;
// TODO: does not work
//use std::from_str::from_str;

// TODO: move to TProtocol
// Decided to leave the same naming as in the C++ code; to be discussed
#[allow(dead_code)]
#[allow(non_camel_case_types)]
enum MessageType {
    T_CALL = 1,
    T_REPLY = 2,
    T_EXCEPTION = 3,
    T_ONEWAY = 4,
}

// TODO: move to Socket
#[allow(dead_code)]
struct TSocket {
    host: String,
    port: i32,
}

impl TSocket {

    fn new(h: String, p: i32) -> TSocket {
        TSocket { host: h, port: p }
    }

    // TODO: return type
    fn open(&self) -> Option<()> {
        // TODO
        Some(())
    }

    fn flush(&self) {
        // TODO
    }

    fn close(&self) {
        // TODO
    }
}

// TODO: move to Transport
struct ThriftBufferedTransport<'a> {
    socket: &'a TSocket,
}

impl<'a> ThriftBufferedTransport<'a> {

    fn new(s: &'a TSocket) -> ThriftBufferedTransport<'a> {
        ThriftBufferedTransport { socket: s }
    }

    fn open(&self) -> Option<()> {
        self.socket.open()
    }

    fn write_end(&self) -> i32 {
        // default is do nothing
        0
    }

    fn flush(&self) {
        // TODO

        // Flush the underlying transport.
        self.socket.flush();
    }

    fn close(&self) {
        // TODO
        self.socket.close();
    }
}

// TODO: move to Protocol
struct ThriftBinaryProtocol<'a> {
    transport: &'a ThriftBufferedTransport<'a>, // TODO: replace with Transport
}

impl<'a> ThriftBinaryProtocol<'a> {

    fn new(t: &'a ThriftBufferedTransport) -> ThriftBinaryProtocol<'a> {
        ThriftBinaryProtocol { transport: t }
    }

    fn get_transport(&self) -> &ThriftBufferedTransport {
        self.transport
    }

    fn write_message_begin(&self, name: &str, message_type: MessageType, seq_id: i32) -> i32 {
        println!("Protocol: message begin");

        // TODO: strict write
        let mut wsize: i32 = 0;
        wsize += self.write_string(name);
        wsize += self.write_byte(message_type as i8);
        wsize += self.write_i32(seq_id);
        wsize
    }

    fn write_string(&self, s: &str) -> i32 {
        println!("Protocol: {}", s);
        s.len() as i32
    }

    fn write_byte(&self, b: i8) -> i32 {
        println!("Protocol: {}", b);
        1
    }

    fn write_i32(&self, i: i32) -> i32 {
        println!("Protocol: {}", i);
        4
    }

    fn write_message_end(&self) -> i32 {
        println!("Protocol: end");
        0
    }
}

// TODO: shall be generated
#[allow(dead_code)]
struct CalculatorPingArgs;

impl CalculatorPingArgs {

    #[allow(unused_variables)]
    fn write(&self, oprot: &ThriftBinaryProtocol) {
        println!("CalculatorPingArgs::write");
    }
}

#[allow(dead_code)]
struct CalculatorClient<'a> {
    oprotocol: &'a ThriftBinaryProtocol<'a>,
    iprotocol: &'a ThriftBinaryProtocol<'a>,
}

impl<'a> CalculatorClient<'a> {

    fn new(protocol: &'a ThriftBinaryProtocol) -> CalculatorClient<'a> {
        CalculatorClient { oprotocol: protocol, iprotocol: protocol }
    }

    fn ping(&self) {
        self.send_ping();
        self.receive_ping();
    }

    fn send_ping(&self) {
        let cseqid: i32 = 0;
        self.oprotocol.write_message_begin("ping", MessageType::T_CALL, cseqid);

        let args = CalculatorPingArgs;
        args.write(self.oprotocol);

        self.oprotocol.write_message_end();

        self.oprotocol.get_transport().write_end();
        self.oprotocol.get_transport().flush();
    }

    fn receive_ping(&self) {
        // TODO
    }
}

pub fn main() {
    // TODO: does not work
    //let addr: SocketAddr =
    //    from_str("127.0.0.1:9090").expect("malformed address");
    let socket = TSocket::new(String::from_str("localhost"), 9090);
    let transport = ThriftBufferedTransport::new(&socket);
    let protocol = ThriftBinaryProtocol::new(&transport);
    let client = CalculatorClient::new(&protocol);

    transport.open()
        .expect("failed to connect");

    client.ping();

    println!("PASS")

    transport.close();
}
<commit_msg>First ping from client to server<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#![crate_name="thrift-client"]
#![crate_type="bin"]

// TODO: move to TProtocol
// Decided to leave the same naming as in the C++ code; to be discussed
#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum MessageType {
    T_CALL = 1,
    T_REPLY = 2,
    T_EXCEPTION = 3,
    T_ONEWAY = 4,
}

#[allow(dead_code)]
#[allow(non_camel_case_types)]
pub enum TThriftType {
  T_STOP       = 0,
  T_VOID       = 1,
  T_BOOL       = 2,
  T_BYTE       = 3,
//  T_I08        = 3, TODO
  T_I16        = 6,
  T_I32        = 8,
  T_U64        = 9,
  T_I64        = 10,
  T_DOUBLE     = 4,
  T_STRING     = 11,
//  T_UTF7       = 11, TODO
  T_STRUCT     = 12,
  T_MAP        = 13,
  T_SET        = 14,
  T_LIST       = 15,
  T_UTF8       = 16,
  T_UTF16      = 17,
}

static BINARY_PROTOCOL_VERSION_1: u32 = 0x80010000;

pub trait Transport {

    fn open(& mut self) -> Option<()> {
        // default is nothing
        Some(())
    }

    fn write_i8(& mut self, value: i8) -> i32;

    fn write_be_i32(& mut self, value: i32) -> i32;

    fn write(& mut self, value: &[u8]) -> i32;

    fn write_end(& mut self) -> i32 {
        // default is nothing
        0
    }

    fn flush(& mut self);

    fn close(& mut self) {
        // default is nothing
    }
}

// Matches corresponding C++ class.
// TODO: move to Transport
pub struct ThriftTransport<T: std::io::Writer> {
    writer: T
}

impl <T: std::io::Writer> ThriftTransport<T> {

    fn new(w: T) -> ThriftTransport<T> {
        ThriftTransport { writer: w }
    }
}

impl <T: std::io::Writer> Transport for ThriftTransport<T> {

    fn write_i8(& mut self, value: i8) -> i32 {
        match self.writer.write_i8(value) {
          Ok(_) => 4,
          Err(e) => panic!(e),
        }
    }

    fn write_be_i32(& mut self, value: i32) -> i32 {
        match self.writer.write_be_i32(value) {
          Ok(_) => 4,
          Err(e) => panic!(e),
        }
    }

    fn write(& mut self, value: &[u8]) -> i32 {
        match self.writer.write(value) {
          Ok(_) => 4,
          Err(e) => panic!(e),
        }
    }

    fn flush(& mut self) {
        // Flush the underlying transport.
        self.writer.flush();
    }
}

pub trait Protocol {

//    fn get_transport(&'a self) -> & mut ThriftBufferedTransport {
//        self.transport
//    }

    fn write_message_begin(& mut self, name: &str, message_type: MessageType, seq_id: i32) -> i32 {
        println!("Protocol: message begin");

        // TODO: strict write
        let mut wsize: i32 = 0;
        wsize += self.write_string(name);
        wsize += self.write_i8(message_type as i8);
        wsize += self.write_i32(seq_id);
        wsize
    }

    fn write_string(& mut self, s: &str) -> i32 {
        println!("Protocol: {}", s);
        s.len() as i32
    }

    fn write_i8(& mut self, b: i8) -> i32 {
        println!("Protocol: {}", b);
        1
    }

    fn write_i32(& mut self, i: i32) -> i32 {
        println!("Protocol: {}", i);
        4
    }

    fn write_binary(& mut self, value: &[u8]) -> i32;

    fn write_field_stop(& mut self) -> i32 {
        self.write_i8(TThriftType::T_STOP as i8)
    }

    fn write_message_end(& mut self) -> i32 {
        println!("Protocol: end");
        0
    }

    fn flush(& mut self);
}

// TODO: move to Protocol
struct ThriftBinaryProtocol<T: Transport> {
    transport: T
}

impl <T: Transport> ThriftBinaryProtocol<T> {

    fn new(transport: T) -> ThriftBinaryProtocol<T> {
        ThriftBinaryProtocol { transport: transport }
    }
}

impl <T: Transport> Protocol for ThriftBinaryProtocol<T> {

    // TODO: remove
    fn flush(& mut self) {
        self.transport.flush();
    }

  fn write_message_begin(
    & mut self,
    name: &str,
    message_type: MessageType,
    sequence_id: i32
  ) -> i32 {
    let version = BINARY_PROTOCOL_VERSION_1 as i32 | message_type as i32;
    self.write_i32(version);
    self.write_string(name);
    self.write_i32(sequence_id);
    0
  }

  fn write_i8(& mut self, value: i8) -> i32 {
      self.transport.write_i8(value)
  }

  fn write_i32(& mut self, value: i32) -> i32 {
      self.transport.write_be_i32(value)
  }

  fn write_string(& mut self, value: &str) -> i32 {
    self.write_binary(value.as_bytes());
    0
  }

  fn write_binary(& mut self, value: &[u8]) -> i32 {
    self.write_i32(value.len() as i32);
    self.transport.write(value)
  }
}

// TODO: shall be generated
// TODO: shall we add smth like _dummy: () here?
#[allow(dead_code)]
struct CalculatorPingArgs;

impl CalculatorPingArgs {

    #[allow(unused_variables)]
    fn write(&self, oprot: & mut Protocol) {
        println!("CalculatorPingArgs::write");

  //        oprot->incrementRecursionDepth();
  //xfer += oprot->writeStructBegin("Calculator_ping_args");

  //xfer += oprot->writeFieldStop();
        oprot.write_field_stop();
  //xfer += oprot->writeStructEnd();
  //oprot->decrementRecursionDepth();

    }
}

struct CalculatorClient<T: Protocol> {
    protocol: T
}

impl <T: Protocol> CalculatorClient<T> {

    fn new(protocol: T) -> CalculatorClient<T> {
        CalculatorClient { protocol: protocol }
    }

    fn ping(& mut self) {
        self.send_ping();
        self.receive_ping();
    }

    fn send_ping(& mut self) {
        let cseqid: i32 = 0;
        self.protocol.write_message_begin("ping", MessageType::T_CALL, cseqid);

        let args = CalculatorPingArgs;
        args.write(& mut self.protocol);

        self.protocol.write_message_end();

        //self.oprotocol.get_transport().write_end();
        //self.oprotocol.get_transport().flush();
        self.protocol.flush();
    }

    fn receive_ping(&self) {
        // TODO
    }
}

pub fn main() {
    let server_address = "127.0.0.1:9090";
    let addr : std::io::net::ip::SocketAddr = std::str::FromStr::from_str(server_address).expect("bad server address");
    let tcp = std::io::TcpStream::connect(addr).unwrap();
    let transport = ThriftTransport::new(tcp);
    let protocol = ThriftBinaryProtocol::new(transport);
    let mut client = CalculatorClient::new(protocol);

    client.ping();
    client.ping();
    client.ping();
    client.ping();
    client.ping();

    println!("PASS");
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually close the file.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);
        // Reading zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // This can never fail because `count` was capped to be smaller than
            // `isize::max_value()`.
            let count = isize::try_from(count).unwrap();
            // We want to read at most `count` bytes. We are sure that `count` is not negative
            // because it was a target's `usize`. Also we are sure that its smaller than
            // `usize::max_value()` because it is a host's `isize`.
            let mut bytes = vec![0; count as usize];
            let result = handle
                .file
                .read(&mut bytes)
                // `File::read` never returns a value larger than `count`, so this cannot fail.
                .map(|c| i64::try_from(c).unwrap());

            match result {
                Ok(read_bytes) => {
                    // If reading to `bytes` did not fail, we write those bytes to the buffer.
                    this.memory.write_bytes(buf, bytes)?;
                    Ok(read_bytes)
                }
                Err(e) => {
                    this.set_last_error_from_io_error(e)?;
                    Ok(-1)
                }
            }
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);
        // Writing zero bytes should not change `buf`.
        if count == 0 {
            return Ok(0);
        }
        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| i64::try_from(c).unwrap());
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<commit_msg>remove no-longer-needed zero checks<commit_after>use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs::{remove_file, File, OpenOptions};
use std::io::{Read, Write};

use rustc::ty::layout::Size;

use crate::stacked_borrows::Tag;
use crate::*;

#[derive(Debug)]
pub struct FileHandle {
    file: File,
}

pub struct FileHandler {
    handles: HashMap<i32, FileHandle>,
    low: i32,
}

impl Default for FileHandler {
    fn default() -> Self {
        FileHandler {
            handles: Default::default(),
            // 0, 1 and 2 are reserved for stdin, stdout and stderr.
            low: 3,
        }
    }
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn open(
        &mut self,
        path_op: OpTy<'tcx, Tag>,
        flag_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("open")?;

        let flag = this.read_scalar(flag_op)?.to_i32()?;

        let mut options = OpenOptions::new();

        let o_rdonly = this.eval_libc_i32("O_RDONLY")?;
        let o_wronly = this.eval_libc_i32("O_WRONLY")?;
        let o_rdwr = this.eval_libc_i32("O_RDWR")?;
        // The first two bits of the flag correspond to the access mode in linux, macOS and
        // windows. We need to check that in fact the access mode flags for the current platform
        // only use these two bits, otherwise we are in an unsupported platform and should error.
        if (o_rdonly | o_wronly | o_rdwr) & !0b11 != 0 {
            throw_unsup_format!("Access mode flags on this platform are unsupported");
        }
        // Now we check the access mode
        let access_mode = flag & 0b11;

        if access_mode == o_rdonly {
            options.read(true);
        } else if access_mode == o_wronly {
            options.write(true);
        } else if access_mode == o_rdwr {
            options.read(true).write(true);
        } else {
            throw_unsup_format!("Unsupported access mode {:#x}", access_mode);
        }
        // We need to check that there aren't unsupported options in `flag`. For this we try to
        // reproduce the content of `flag` in the `mirror` variable using only the supported
        // options.
        let mut mirror = access_mode;

        let o_append = this.eval_libc_i32("O_APPEND")?;
        if flag & o_append != 0 {
            options.append(true);
            mirror |= o_append;
        }
        let o_trunc = this.eval_libc_i32("O_TRUNC")?;
        if flag & o_trunc != 0 {
            options.truncate(true);
            mirror |= o_trunc;
        }
        let o_creat = this.eval_libc_i32("O_CREAT")?;
        if flag & o_creat != 0 {
            options.create(true);
            mirror |= o_creat;
        }
        let o_cloexec = this.eval_libc_i32("O_CLOEXEC")?;
        if flag & o_cloexec != 0 {
            // We do not need to do anything for this flag because `std` already sets it.
            // (Technically we do not support *not* setting this flag, but we ignore that.)
            mirror |= o_cloexec;
        }
        // If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
        // then we throw an error.
        if flag != mirror {
            throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
        }

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let fd = options.open(path).map(|file| {
            let mut fh = &mut this.machine.file_handler;
            fh.low += 1;
            fh.handles.insert(fh.low, FileHandle { file }).unwrap_none();
            fh.low
        });

        this.try_unwrap_io_result(fd)
    }

    fn fcntl(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        cmd_op: OpTy<'tcx, Tag>,
        _arg1_op: Option<OpTy<'tcx, Tag>>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("fcntl")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let cmd = this.read_scalar(cmd_op)?.to_i32()?;
        // We only support getting the flags for a descriptor.
        if cmd == this.eval_libc_i32("F_GETFD")? {
            // Currently this is the only flag that `F_GETFD` returns. It is OK to just return the
            // `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
            // always sets this flag when opening a file. However we still need to check that the
            // file itself is open.
            if this.machine.file_handler.handles.contains_key(&fd) {
                Ok(this.eval_libc_i32("FD_CLOEXEC")?)
            } else {
                this.handle_not_found()
            }
        } else {
            throw_unsup_format!("The {:#x} command is not supported for `fcntl`)", cmd);
        }
    }

    fn close(&mut self, fd_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("close")?;

        let fd = this.read_scalar(fd_op)?.to_i32()?;

        if let Some(handle) = this.machine.file_handler.handles.remove(&fd) {
            // `File::sync_all` does the checks that are done when closing a file. We do this to
            // to handle possible errors correctly.
            let result = this.try_unwrap_io_result(handle.file.sync_all().map(|_| 0i32));
            // Now we actually close the file.
            drop(handle);
            // And return the result.
            result
        } else {
            this.handle_not_found()
        }
    }

    fn read(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("read")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            // This can never fail because `count` was capped to be smaller than
            // `isize::max_value()`.
            let count = isize::try_from(count).unwrap();
            // We want to read at most `count` bytes. We are sure that `count` is not negative
            // because it was a target's `usize`. Also we are sure that its smaller than
            // `usize::max_value()` because it is a host's `isize`.
            let mut bytes = vec![0; count as usize];
            let result = handle
                .file
                .read(&mut bytes)
                // `File::read` never returns a value larger than `count`, so this cannot fail.
                .map(|c| i64::try_from(c).unwrap());

            match result {
                Ok(read_bytes) => {
                    // If reading to `bytes` did not fail, we write those bytes to the buffer.
                    this.memory.write_bytes(buf, bytes)?;
                    Ok(read_bytes)
                }
                Err(e) => {
                    this.set_last_error_from_io_error(e)?;
                    Ok(-1)
                }
            }
        } else {
            this.handle_not_found()
        }
    }

    fn write(
        &mut self,
        fd_op: OpTy<'tcx, Tag>,
        buf_op: OpTy<'tcx, Tag>,
        count_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i64> {
        let this = self.eval_context_mut();

        this.check_no_isolation("write")?;

        let ptr_size = this.pointer_size().bits();

        // We cap the number of read bytes to the largest value that we are able to fit in both the
        // host's and target's `isize`.
        let count = this
            .read_scalar(count_op)?
            .to_machine_usize(&*this.tcx)?
            .min((1 << (ptr_size - 1)) - 1) // max value of target `isize`
            .min(isize::max_value() as u64);

        let fd = this.read_scalar(fd_op)?.to_i32()?;
        let buf = this.read_scalar(buf_op)?.not_undef()?;

        if let Some(handle) = this.machine.file_handler.handles.get_mut(&fd) {
            let bytes = this.memory.read_bytes(buf, Size::from_bytes(count))?;
            let result = handle.file.write(&bytes).map(|c| i64::try_from(c).unwrap());
            this.try_unwrap_io_result(result)
        } else {
            this.handle_not_found()
        }
    }

    fn unlink(&mut self, path_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.check_no_isolation("unlink")?;

        let path = this.read_os_string_from_c_string(this.read_scalar(path_op)?.not_undef()?)?;

        let result = remove_file(path).map(|_| 0);

        this.try_unwrap_io_result(result)
    }

    /// Function used when a handle is not found inside `FileHandler`. It returns `Ok(-1)`and sets
    /// the last OS error to `libc::EBADF` (invalid file descriptor). This function uses
    /// `T: From<i32>` instead of `i32` directly because some fs functions return different integer
    /// types (like `read`, that returns an `i64`).
    fn handle_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
        let this = self.eval_context_mut();
        let ebadf = this.eval_libc("EBADF")?;
        this.set_last_error(ebadf)?;
        Ok((-1).into())
    }
}
<|endoftext|>


<commit_before>use openvr_sys;
use openvr_sys::Enum_ETrackedPropertyError::*;

use subsystems::*;
use error::*;

/// Describes a string property of a tracked device
#[derive(Debug, Copy, Clone)]
pub enum TrackedDeviceStringProperty {
    TrackingSystemName,
    ModelNumber,
    SerialNumber,
    RenderModelName,
    ManufacturerName,
    TrackingFirmwareVersion,
    HardwareRevision,
    AllWirelessDongleDescriptions,
    ConnectedWirelessDongle,
    FirmwareManualUpdateURL,
    FirmwareProgrammingTarget,
    DisplayMCImageLeft,
    DisplayMCImageRight,
    DisplayGCImage,
    CameraFirmwareDescription,
    AttachedDeviceId,
    ModeLabel
}

impl TrackedDeviceStringProperty {
    pub fn to_raw(&self) -> openvr_sys::Enum_ETrackedDeviceProperty {
        use openvr_sys::Enum_ETrackedDeviceProperty::*;
        use self::TrackedDeviceStringProperty::*;

        match *self {
            TrackingSystemName => ETrackedDeviceProperty_Prop_TrackingSystemName_String,
            ModelNumber => ETrackedDeviceProperty_Prop_ModelNumber_String,
            SerialNumber => ETrackedDeviceProperty_Prop_SerialNumber_String,
            RenderModelName => ETrackedDeviceProperty_Prop_RenderModelName_String,
            ManufacturerName => ETrackedDeviceProperty_Prop_ManufacturerName_String,
            TrackingFirmwareVersion => ETrackedDeviceProperty_Prop_TrackingFirmwareVersion_String,
            HardwareRevision => ETrackedDeviceProperty_Prop_HardwareRevision_String,
            AllWirelessDongleDescriptions => ETrackedDeviceProperty_Prop_AllWirelessDongleDescriptions_String,
            ConnectedWirelessDongle => ETrackedDeviceProperty_Prop_ConnectedWirelessDongle_String,
            FirmwareManualUpdateURL => ETrackedDeviceProperty_Prop_Firmware_ManualUpdateURL_String,
            FirmwareProgrammingTarget => ETrackedDeviceProperty_Prop_Firmware_ProgrammingTarget_String,
            DisplayMCImageLeft => ETrackedDeviceProperty_Prop_DisplayMCImageLeft_String,
            DisplayMCImageRight => ETrackedDeviceProperty_Prop_DisplayMCImageRight_String,
            DisplayGCImage => ETrackedDeviceProperty_Prop_DisplayGCImage_String,
            CameraFirmwareDescription => ETrackedDeviceProperty_Prop_CameraFirmwareDescription_String,
            AttachedDeviceId => ETrackedDeviceProperty_Prop_AttachedDeviceId_String,
            ModeLabel => ETrackedDeviceProperty_Prop_ModeLabel_String
        }
    }
}

/// Describes the class of a tracked device
#[derive(Debug, Copy, Clone)]
pub enum TrackedDeviceClass {
    Invalid,
    HMD,
    Controller,
    TrackingReference,
    Other,
}

impl TrackedDeviceClass {
    pub fn to_raw(&self) -> openvr_sys::Enum_ETrackedDeviceClass {
        use self::TrackedDeviceClass::*;
        use openvr_sys::Enum_ETrackedDeviceClass::*;

        match *self {
            Invalid => ETrackedDeviceClass_TrackedDeviceClass_Invalid,
            HMD => ETrackedDeviceClass_TrackedDeviceClass_HMD,
            Controller => ETrackedDeviceClass_TrackedDeviceClass_Controller,
            TrackingReference => ETrackedDeviceClass_TrackedDeviceClass_TrackingReference,
            Other => ETrackedDeviceClass_TrackedDeviceClass_Other,
        }
    }

    pub fn from_raw(raw: openvr_sys::Enum_ETrackedDeviceClass) -> Self {
        use self::TrackedDeviceClass::*;
        use openvr_sys::Enum_ETrackedDeviceClass::*;

        match raw {
            ETrackedDeviceClass_TrackedDeviceClass_Invalid => Invalid,
            ETrackedDeviceClass_TrackedDeviceClass_HMD => HMD,
            ETrackedDeviceClass_TrackedDeviceClass_Controller => Controller,
            ETrackedDeviceClass_TrackedDeviceClass_TrackingReference => TrackingReference,
            ETrackedDeviceClass_TrackedDeviceClass_Other => Other,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct TrackedDevicePose {
    pub index: usize,
    pub to_device: [[f32; 4]; 3],
    pub velocity: [f32; 3],
    pub angular_velocity: [f32; 3],
    pub is_valid: bool,
    pub is_connected: bool,
}

impl TrackedDevicePose {
    // returns the device class of the tracked object
    pub fn device_class(&self) -> TrackedDeviceClass {
        unsafe {
            let system = * { system().unwrap().0 as *mut openvr_sys::Struct_VR_IVRSystem_FnTable};
            TrackedDeviceClass::from_raw(system.GetTrackedDeviceClass.unwrap()(self.index as u32))
        }
    }

    /// gets a propery as a string
    pub fn get_property_string(&self, property: TrackedDeviceStringProperty) -> Result<String, Error<openvr_sys::Enum_ETrackedPropertyError>> {
        unsafe {
            let system = * { system().unwrap().0 as *mut openvr_sys::Struct_VR_IVRSystem_FnTable};

            let val_out = String::with_capacity(256);
            let mut err = ETrackedPropertyError_TrackedProp_Success;

            let size = system.GetStringTrackedDeviceProperty.unwrap()(
                self.index as u32,
                property.to_raw(),
                val_out.as_ptr() as *mut i8,
                256,
                &mut err
            );

            if size > 0 {
                return Ok(String::from_raw_parts(val_out.as_ptr() as *mut _, (size - 1) as usize, (size - 1) as usize));
            } else {
                return Err(Error::from_raw(err));
            }
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct TrackedDevicePoses {
    pub count: usize,
    pub poses: [TrackedDevicePose; 16],
}

pub struct TrackedDevicePosesIterator<'a> {
    pub target: &'a TrackedDevicePoses,
    pub index: usize
}

impl TrackedDevicePoses {
    pub fn as_slice(&self) -> &[TrackedDevicePose] {
        &self.poses[0..self.count]
    }

    /// creates an iterator that will iterate over all connected devices
    pub fn connected_iter(&self) -> TrackedDevicePosesIterator {
        TrackedDevicePosesIterator { target: self, index: 0 }
    }
}

impl<'a> Iterator for TrackedDevicePosesIterator<'a> {
    type Item = &'a TrackedDevicePose;

    fn next(&mut self) -> Option<&'a TrackedDevicePose> {
        // end reached
        if self.index == self.target.count {
            return None;
        }

        let res = &self.target.poses[self.index];
        self.index += 1;

        Some(res)
    }
}

pub unsafe fn to_tracked(data: [openvr_sys::TrackedDevicePose_t; 16]) -> TrackedDevicePoses {
    use std;
    let mut out: TrackedDevicePoses = std::mem::zeroed();
    for (i, d) in data.iter().enumerate() {
        if d.bDeviceIsConnected > 0 {
            out.count = i + 1;
        }
        out.poses[i].index = i;
        out.poses[i].is_connected = d.bDeviceIsConnected > 0;
        out.poses[i].is_valid = d.bPoseIsValid > 0;
        out.poses[i].to_device = d.mDeviceToAbsoluteTracking.m;
        out.poses[i].velocity = d.vVelocity.v;
        out.poses[i].angular_velocity = d.vAngularVelocity.v;
    }
    out
}
<commit_msg>fix for ffi string crash<commit_after>use openvr_sys;
use openvr_sys::Enum_ETrackedPropertyError::*;

use subsystems::*;
use error::*;
use std::slice;
use std::str;

/// Describes a string property of a tracked device
#[derive(Debug, Copy, Clone)]
pub enum TrackedDeviceStringProperty {
    TrackingSystemName,
    ModelNumber,
    SerialNumber,
    RenderModelName,
    ManufacturerName,
    TrackingFirmwareVersion,
    HardwareRevision,
    AllWirelessDongleDescriptions,
    ConnectedWirelessDongle,
    FirmwareManualUpdateURL,
    FirmwareProgrammingTarget,
    DisplayMCImageLeft,
    DisplayMCImageRight,
    DisplayGCImage,
    CameraFirmwareDescription,
    AttachedDeviceId,
    ModeLabel
}

impl TrackedDeviceStringProperty {
    pub fn to_raw(&self) -> openvr_sys::Enum_ETrackedDeviceProperty {
        use openvr_sys::Enum_ETrackedDeviceProperty::*;
        use self::TrackedDeviceStringProperty::*;

        match *self {
            TrackingSystemName => ETrackedDeviceProperty_Prop_TrackingSystemName_String,
            ModelNumber => ETrackedDeviceProperty_Prop_ModelNumber_String,
            SerialNumber => ETrackedDeviceProperty_Prop_SerialNumber_String,
            RenderModelName => ETrackedDeviceProperty_Prop_RenderModelName_String,
            ManufacturerName => ETrackedDeviceProperty_Prop_ManufacturerName_String,
            TrackingFirmwareVersion => ETrackedDeviceProperty_Prop_TrackingFirmwareVersion_String,
            HardwareRevision => ETrackedDeviceProperty_Prop_HardwareRevision_String,
            AllWirelessDongleDescriptions => ETrackedDeviceProperty_Prop_AllWirelessDongleDescriptions_String,
            ConnectedWirelessDongle => ETrackedDeviceProperty_Prop_ConnectedWirelessDongle_String,
            FirmwareManualUpdateURL => ETrackedDeviceProperty_Prop_Firmware_ManualUpdateURL_String,
            FirmwareProgrammingTarget => ETrackedDeviceProperty_Prop_Firmware_ProgrammingTarget_String,
            DisplayMCImageLeft => ETrackedDeviceProperty_Prop_DisplayMCImageLeft_String,
            DisplayMCImageRight => ETrackedDeviceProperty_Prop_DisplayMCImageRight_String,
            DisplayGCImage => ETrackedDeviceProperty_Prop_DisplayGCImage_String,
            CameraFirmwareDescription => ETrackedDeviceProperty_Prop_CameraFirmwareDescription_String,
            AttachedDeviceId => ETrackedDeviceProperty_Prop_AttachedDeviceId_String,
            ModeLabel => ETrackedDeviceProperty_Prop_ModeLabel_String
        }
    }
}

/// Describes the class of a tracked device
#[derive(Debug, Copy, Clone)]
pub enum TrackedDeviceClass {
    Invalid,
    HMD,
    Controller,
    TrackingReference,
    Other,
}

impl TrackedDeviceClass {
    pub fn to_raw(&self) -> openvr_sys::Enum_ETrackedDeviceClass {
        use self::TrackedDeviceClass::*;
        use openvr_sys::Enum_ETrackedDeviceClass::*;

        match *self {
            Invalid => ETrackedDeviceClass_TrackedDeviceClass_Invalid,
            HMD => ETrackedDeviceClass_TrackedDeviceClass_HMD,
            Controller => ETrackedDeviceClass_TrackedDeviceClass_Controller,
            TrackingReference => ETrackedDeviceClass_TrackedDeviceClass_TrackingReference,
            Other => ETrackedDeviceClass_TrackedDeviceClass_Other,
        }
    }

    pub fn from_raw(raw: openvr_sys::Enum_ETrackedDeviceClass) -> Self {
        use self::TrackedDeviceClass::*;
        use openvr_sys::Enum_ETrackedDeviceClass::*;

        match raw {
            ETrackedDeviceClass_TrackedDeviceClass_Invalid => Invalid,
            ETrackedDeviceClass_TrackedDeviceClass_HMD => HMD,
            ETrackedDeviceClass_TrackedDeviceClass_Controller => Controller,
            ETrackedDeviceClass_TrackedDeviceClass_TrackingReference => TrackingReference,
            ETrackedDeviceClass_TrackedDeviceClass_Other => Other,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct TrackedDevicePose {
    pub index: usize,
    pub to_device: [[f32; 4]; 3],
    pub velocity: [f32; 3],
    pub angular_velocity: [f32; 3],
    pub is_valid: bool,
    pub is_connected: bool,
}

impl TrackedDevicePose {
    // returns the device class of the tracked object
    pub fn device_class(&self) -> TrackedDeviceClass {
        unsafe {
            let system = * { system().unwrap().0 as *mut openvr_sys::Struct_VR_IVRSystem_FnTable};
            TrackedDeviceClass::from_raw(system.GetTrackedDeviceClass.unwrap()(self.index as u32))
        }
    }

    /// gets a propery as a string
    pub fn get_property_string(&self, property: TrackedDeviceStringProperty) -> Result<String, Error<openvr_sys::Enum_ETrackedPropertyError>> {
        unsafe {
            let system = * { system().unwrap().0 as *mut openvr_sys::Struct_VR_IVRSystem_FnTable};

            let val_out = String::with_capacity(256);
            let mut err = ETrackedPropertyError_TrackedProp_Success;

            let size = system.GetStringTrackedDeviceProperty.unwrap()(
                self.index as u32,
                property.to_raw(),
                val_out.as_ptr() as *mut i8,
                256,
                &mut err
            );

            if size > 0 {
                let ptr = val_out.as_ptr() as *mut u8;
                let mem = slice::from_raw_parts(ptr, size as usize);
                let str = str::from_utf8(mem).unwrap();
                return Ok(String::from(str));
            } else {
                return Err(Error::from_raw(err));
            }
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct TrackedDevicePoses {
    pub count: usize,
    pub poses: [TrackedDevicePose; 16],
}

pub struct TrackedDevicePosesIterator<'a> {
    pub target: &'a TrackedDevicePoses,
    pub index: usize
}

impl TrackedDevicePoses {
    pub fn as_slice(&self) -> &[TrackedDevicePose] {
        &self.poses[0..self.count]
    }

    /// creates an iterator that will iterate over all connected devices
    pub fn connected_iter(&self) -> TrackedDevicePosesIterator {
        TrackedDevicePosesIterator { target: self, index: 0 }
    }
}

impl<'a> Iterator for TrackedDevicePosesIterator<'a> {
    type Item = &'a TrackedDevicePose;

    fn next(&mut self) -> Option<&'a TrackedDevicePose> {
        // end reached
        if self.index == self.target.count {
            return None;
        }

        let res = &self.target.poses[self.index];
        self.index += 1;

        Some(res)
    }
}

pub unsafe fn to_tracked(data: [openvr_sys::TrackedDevicePose_t; 16]) -> TrackedDevicePoses {
    use std;
    let mut out: TrackedDevicePoses = std::mem::zeroed();
    for (i, d) in data.iter().enumerate() {
        if d.bDeviceIsConnected > 0 {
            out.count = i + 1;
        }
        out.poses[i].index = i;
        out.poses[i].is_connected = d.bDeviceIsConnected > 0;
        out.poses[i].is_valid = d.bPoseIsValid > 0;
        out.poses[i].to_device = d.mDeviceToAbsoluteTracking.m;
        out.poses[i].velocity = d.vVelocity.v;
        out.poses[i].angular_velocity = d.vAngularVelocity.v;
    }
    out
}
<|endoftext|>


<commit_before>pub enum Conjunction {
    And,
    Or
}

impl Conjunction {
    pub fn to_sql(&self) -> &str {
        match *self {
            Conjunction::And => " AND ",
            Conjunction::Or => " OR "
        }
    }
}

struct Where<'a> {
    pub conjunction: Conjunction,
    pub where_cl: &'a [&'a str]
}

impl<'a> Where<'a> {
    pub fn to_sql(&self) -> String {
        let mut rv = String::new();
        rv.push_str(&self.where_cl
                    .connect(self.conjunction.to_sql()));
        rv
    }

    pub fn get_nested(&self) -> String {
        format!("({})", self.to_sql())
    }
}

#[cfg(test)]
mod tests {
    use super::{Conjunction, Where};

    #[test]
    fn test_conjunction() {
        let and = Conjunction::And;
        let or = Conjunction::Or;

        assert_eq!(and.to_sql(), " AND ");
        assert_eq!(or.to_sql(), " OR ");
    }

    #[test]
    fn test_where_clause() {
        let where_clause = Where {
            conjunction: Conjunction::And,
            where_cl: &["fizz = bazz", "lala = blah"]
        };
        assert_eq!(where_clause.to_sql(), "fizz = bazz AND lala = blah");
    }

    #[test]
    fn test_where_nested_clause() {
        let where_clause_foo = Where {
            conjunction: Conjunction::And,
            where_cl: &["foo = bar", "fizz = bazz"]
        };

        let where_clause_bar = Where {
            conjunction: Conjunction::Or,
            where_cl: &["None = Null"]
        };

        let where_nested = Where {
            conjunction: Conjunction::And,
            where_cl: &[&where_clause_foo.get_nested(),
                        &where_clause_bar.get_nested()]
        };
        assert_eq!(where_nested.to_sql(), "(foo = bar AND fizz = bazz) AND (None = Null)");
    }
}
<commit_msg>Updated Where clause<commit_after>pub enum Operator {
    And,
    Or
}

impl Operator {
    pub fn to_sql(&self) -> &str {
        match *self {
            Operator::And => "AND",
            Operator::Or => "OR"
        }
    }
}

trait ToSQL {
    fn to_sql(&self) -> String;
}

struct Where<'a, T: 'a> {
    pub operator: Operator,
    pub clause: &'a [&'a T]
}

impl<'a> ToSQL for &'a str {
    fn to_sql(&self) -> String {
        self.to_string()
    }
}

impl<'a, T: ToSQL> ToSQL for Where<'a, T> {
    fn to_sql(&self) -> String {
        let operator = &format!(" {} ", self.operator.to_sql());
        let mut rv = String::new();
        rv.push_str(&self.clause.into_iter()
                    .map(|x| x.to_sql())
                    .collect::<Vec<_>>()
                    .connect(operator));
        rv
    }
}

#[cfg(test)]
mod tests {
    use super::{Operator, Where};

    #[test]
    fn test_operator() {
        let and = Operator::And;
        let or = Operator::Or;

        assert_eq!(and.to_sql(), "AND");
        assert_eq!(or.to_sql(), "OR");
    }

    #[test]
    fn test_generic() {
        let foo = Where {
            operator: Operator::And,
            clause: &["foo", "bar"]
        };
    }
}
<|endoftext|>