<commit_before>
use std::cmp;

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode(pix: [u16; 4]) -> u16 {
	((pix[0] >> 3) << 11) |
	((pix[1] >> 2) << 5) |
	(pix[2] >> 3)
}

fn bc1_interp(min: [u16; 4], max: [u16; 4], index: usize) -> [u8; 4] {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[(coefs.1 * min[0] + coefs.0 * max[0]) as u8,
	 (coefs.1 * min[1] + coefs.0 * max[1]) as u8,
	 (coefs.1 * min[2] + coefs.0 * max[2]) as u8,
	 (coefs.1 * min[3] + coefs.0 * max[3]) as u8]
}

fn dist_one(a: i16, b: i16) -> u32 {
	(a - b).abs() as u32
}

fn dist(a: &[u8; 4], b: &[u8; 4]) -> u32 {
	dist_one(a[0] as i16, b[0] as i16) +
	dist_one(a[1] as i16, b[1] as i16) +
	dist_one(a[2] as i16, b[2] as i16) +
	dist_one(a[3] as i16, b[3] as i16)
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize)) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::new();

    let block_size = 4;

    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let mut min = [255u16; 4];
   			let mut max = [0u16; 4];
   			for p in pixels.into_iter() {
   				for i in 0..4 {
   					min[i] = cmp::min(min[i], p[i] as u16);
   					max[i] = cmp::max(max[i], p[i] as u16);
   				}
   			}

   			let mut interps = [[0u8; 4]; 4];
   			for i in 0..4 {
   				interps[i] = bc1_interp(min, max, i);
   			}

   			// if we want alpha swap min/max (or encode min first)
			let min = bc1_encode(min);
			let max = bc1_encode(max);

			out.push((max & 0xFF) as u8);
			out.push((max >> 8) as u8);

			out.push((min & 0xFF) as u8);
			out.push((min >> 8) as u8);

			let mut mask = 0u32;

			for pix in pixels.into_iter().rev() {
				let best = (0..4).into_iter().min_by_key(|x| dist(&interps[*x], pix)).unwrap() as u32;
				mask = (mask << 2) | (best & 0x03);
			}

			for _ in 0..4 {
				out.push((mask & 0xFF) as u8);
				mask >>= 8;
			}
		}
	}

	Ok(out)
}
<commit_msg>Modularisation.<commit_after>
use std::cmp;

fn is_blk(size: (usize, usize)) -> bool {
	return size.0 % 4 == 0 && size.1 % 4 == 0;
}

fn bc1_encode_endpoint(pix: &[u16; 4]) -> u16 {
	((pix[0] >> 3) << 11) |
	((pix[1] >> 2) << 5) |
	(pix[2] >> 3)
}

fn bc1_interp(min: &[u16; 4], max: &[u16; 4], index: usize) -> [u8; 4] {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] + coefs.0 * max[0]) / 3) as u8,
	 ((coefs.1 * min[1] + coefs.0 * max[1]) / 3) as u8,
	 ((coefs.1 * min[2] + coefs.0 * max[2]) / 3) as u8,
	 ((coefs.1 * min[3] + coefs.0 * max[3]) / 3) as u8]
}

fn bc1_table(min: &[u16; 4], max: &[u16; 4]) -> [[u8; 4]; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_interp(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[[u8; 4]; 16]) -> ([u16; 4], [u16; 4]) {
	let mut min = [65535u16; 4];
	let mut max = [0u16; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i] as u16);
			max[i] = cmp::max(max[i], p[i] as u16);
		}
	}
	(min, max)
}

fn bc1_dist_one(a: i16, b: i16) -> u32 {
	(a - b).abs() as u32
}

fn bc1_dist(a: &[u8; 4], b: &[u8; 4]) -> u32 {
	bc1_dist_one(a[0] as i16, b[0] as i16) +
	bc1_dist_one(a[1] as i16, b[1] as i16) +
	bc1_dist_one(a[2] as i16, b[2] as i16) +
	bc1_dist_one(a[3] as i16, b[3] as i16)
}

fn bc1_total_dist(min: &[u16; 4], max: &[u16; 4], pixels: &[[u8; 4]; 16]) -> u32 {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_interp(&min, &max, i);
	}

	let mut sum = 0u32;
	for pix in pixels.into_iter().rev() {
		sum += (0..4).into_iter().map(|x| bc1_dist(&interps[x], pix)).min().unwrap();
	}
	sum
}

fn bc1_encode(table: &[[u8; 4]; 4], pixels: &[[u8; 4]; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize)) -> Result<Vec<u8>, ()> {
	if !is_blk(size) {
		return Err(());
	}

    let mut out = Vec::new();

    let block_size = 4;

    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let (min, max) = bc1_minmax(&pixels);
   			let table = bc1_table(&min, &max);

			/*let mut d = bc1_total_dist(&min, &max, pixels);
			for i in 0..4 {
				for a in 1..10 {
					let mut min = min;
					min[i] += a;
					let d2 = bc1_total_dist(&min, &max, pixels);
					if d2 < d {
						println!("{:?}", (d2, d, i, a));
					}
				}
			}*/

   			// if we want alpha swap min/max (or encode min first)
			let min = bc1_encode_endpoint(&min);
			let max = bc1_encode_endpoint(&max);

			out.push((max & 0xFF) as u8);
			out.push((max >> 8) as u8);

			out.push((min & 0xFF) as u8);
			out.push((min >> 8) as u8);

			let mut encoded = bc1_encode(&table, &pixels);
			for _ in 0..4 {
				out.push((encoded & 0xFF) as u8);
				encoded >>= 8;
			}
		}
	}

	Ok(out)
}
<|endoftext|>


<commit_before><commit_msg>Fix formatting<commit_after><|endoftext|>


<commit_before>//! A [Todo-Backend](http://todobackend.com/) implementation with rotor-http.
//!
//! Todo-Backed is a showcase to compare different languages and frameworks
//! for building web services. One has to implement a simple todo list
//! that is checked by automatic tests available on the website.
//!
//! NOTE: This example only works with Nightly Rust for now because
//! of serde macros.
//!
//! To run use `cargo run --example todobackend --features nightly`.
//!
//! Now you can edit your own todo list via
//! http://todobackend.com/client/index.html?http://localhost:3000
//! (The web page is just for the frontend, the backend is this Rust
//! program.)

#![cfg_attr(feature="nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros))]
#![allow(warnings)]

extern crate rotor;
extern crate rotor_http;
#[cfg(feature="nightly")] extern crate serde;
#[cfg(feature="nightly")] extern crate serde_json;

use std::borrow::Cow;
use std::collections::HashMap;
use std::str::from_utf8;
use std::time::Duration;

use rotor::{Scope, Time};
use rotor::mio::tcp::TcpListener;
use rotor_http::server::{self, Fsm, Head, RecvMode, Response, Server};

/// Represents a single Todo entry.
///
/// The entry is serialized and deserialized by serde without
/// any handwritten code.
#[cfg(feature="nightly")]
#[derive(Serialize, Deserialize, Debug)]
struct Todo {
    url: Option<String>,
    title: String,
    #[serde(default)]
    completed: bool,
    #[serde(default)]
    order: u64,
}

#[cfg(feature="nightly")]
impl Todo {
    fn set_url(&mut self, id: u64) {
        self.url = Some(format!("http://localhost:3000/todo/{}", id));
    }

    fn patch(&mut self, patch: TodoPatch) {
        if let Some(title) = patch.title {
            self.title = title;
        }
        if let Some(completed) = patch.completed {
            self.completed = completed;
        }
        if let Some(order) = patch.order {
            self.order = order
        }
    }
}

/// One can patch the title and the status of todos. It is also
/// possible to change the ordering.
#[cfg(feature="nightly")]
#[derive(Deserialize, Debug)]
struct TodoPatch {
    title: Option<String>,
    completed: Option<bool>,
    order: Option<u64>,
}

#[cfg(feature="nightly")]
/// Context is used for global storage.
struct Context {
    /// The last id assigned to a todo entry.
    ///
    /// New ids are incremented by one.
    last_id: u64,
    /// The in memory "database" used for the todo-list.
    database: HashMap<u64, Todo>,
}

#[cfg(feature="nightly")]
impl server::Context for Context {}

#[cfg(feature="nightly")]
trait Database {
    /// Get a unique new id.
    fn id(&mut self) -> u64;
    /// List all existing Todos.
    fn list(&self) -> Vec<&Todo>;
    /// Create a new Todo.
    fn create(&mut self, u64, Todo);
    /// Clear the database.
    fn clear(&mut self);
    /// Get one todo by id.
    fn get(&mut self, u64) -> Option<&Todo>;
    /// Get a mutable reference of a todo.
    ///
    /// This is useful for updating or patching an entry.
    fn get_mut(&mut self, u64) -> Option<&mut Todo>;
    /// Delete an entry.
    fn delete(&mut self, id: u64);
}

#[cfg(feature="nightly")]
impl Database for Context {
    fn id(&mut self) -> u64 {
        self.last_id += 1;
        self.last_id
    }

    fn list(&self) -> Vec<&Todo> {
        self.database.values().collect()
    }

    fn create(&mut self, id: u64, todo: Todo) {
        assert!(self.database.insert(id, todo).is_none());
    }

    fn clear(&mut self) {
        self.database.clear();
    }

    fn get(&mut self, id: u64) -> Option<&Todo> {
        self.database.get(&id)
    }

    fn get_mut(&mut self, id: u64) -> Option<&mut Todo> {
        self.database.get_mut(&id)
    }

    fn delete(&mut self, id: u64) {
        self.database.remove(&id);
    }
}

/// The main server state machine. After parsing the header
/// the type of request determined. Each variant of the
/// enum represents a single action. Some are directly related
/// to todo entries others are for error handling or and CORS.
#[derive(Debug, Clone)]
enum TodoBackend {
    List,
    Create,
    Clear,
    Get(u64),
    Patch(u64),
    Delete(u64),
    Preflight,
    MethodNotAllowed(&'static [u8]),
    NotFound,
}

#[cfg(feature="nightly")]
impl Server for TodoBackend {
    type Context = Context;
    fn headers_received(head: Head, _response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<(Self, RecvMode, Time)>
    {
        use TodoBackend::*;
        Some((if head.method == "OPTIONS" {
                Preflight
            } else if head.path == "/" {
                match head.method {
                    "GET" => List,
                    "POST" => Create,
                    "DELETE" => Clear,
                    _ => MethodNotAllowed(b"GET, POST, DELETE"),
                }
            } else if head.path.starts_with("/todo/") {
                let id = head.path[6..].parse().unwrap();
                match head.method {
                    "GET" => Get(id),
                    "PATCH" => Patch(id),
                    "DELETE" => Delete(id),
                    _ => MethodNotAllowed(b"GET, PATCH, DELETE"),
                }
            } else {
                NotFound
            }, RecvMode::Buffered(1024), scope.now() + Duration::new(10, 0)))

    }

    fn request_received(self, data: &[u8], response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<Self>
    {
        use self::TodoBackend::*;
        let text_data = from_utf8(data).unwrap();
        let (status, reason, body) = match self {
            List => (200, "OK", Cow::Owned(serde_json::to_string(&scope.list()).unwrap().into_bytes())),
            Create => {
                let mut todo: Todo = serde_json::from_str(text_data).unwrap();
                let id = scope.id();
                todo.set_url(id);
                let body = serde_json::to_string(&todo).unwrap().into_bytes();
                scope.create(id, todo);
                (201, "Created", Cow::Owned(body))
            }
            Clear => {
                scope.clear();
                (200, "OK", Cow::Borrowed(&b"[]"[..]))
            }
            Get(id) => (200, "OK",
                Cow::Owned(serde_json::to_string(&scope.get(id)).unwrap().into_bytes())),
            Patch(id) => {
                let patch: TodoPatch = serde_json::from_str(text_data).unwrap();
                if let Some(todo) = scope.get_mut(id) {
                    todo.patch(patch);
                    let body = serde_json::to_string(&todo).unwrap().into_bytes();
                    (200, "OK", Cow::Owned(body))
                } else {
                    (404, "Not found", Cow::Borrowed(&b"{}"[..]))
                }
            }
            Delete(id) => {
                scope.delete(id);
                (200, "OK", Cow::Borrowed(&b"{}"[..]))
            }
            Preflight => {
                response.status(200, "OK");
                response.add_length(0).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Access-Control-Allow-Methods",
                                    b"GET, POST, DELETE, PATCH").unwrap();
                response.add_header("Access-Control-Allow-Headers",
                                    b"Content-Type").unwrap();
                response.add_header("Access-Control-Max-Age", b"60").unwrap();
                response.done_headers().unwrap();
                response.done();
                return None;
            }
            MethodNotAllowed(methods) => {
                let reason = "Method Not Allowed";
                response.status(405, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.add_header("Allow", methods).unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            }
            NotFound => {
                let reason = "Not Found";
                response.status(404, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            },
        };
        response.status(status, reason);
        response.add_length(body.len() as u64).unwrap();
        response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
        response.add_header("Content-Type", b"application/json").unwrap();
        response.done_headers().unwrap();
        response.write_body(&body[..]);
        response.done();
        None
    }
    // It is save to leave out `request_chunk` and `request_end` since we
    // only use buffered requests in this example.
    fn request_chunk(self, _chunk: &[u8], _response: &mut Response,
        _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }
    fn request_end(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }

    fn timeout(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<(Self, Time)>
    {
        unimplemented!();
    }
    fn wakeup(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self>
    {
        unimplemented!();
    }
}

#[cfg(feature="nightly")]
fn main() {
    let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap();
    let mut loop_inst = event_loop.instantiate(Context {
        last_id: 0,
        database: HashMap::new(),
    });
    let lst = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
    loop_inst.add_machine_with(|scope| {
        Fsm::<TodoBackend, _>::new(lst, scope)
    }).unwrap();
    loop_inst.run().unwrap();
}

#[cfg(not(feature="nightly"))]
fn main() { panic!("See NOTE in source. You must use --features nightly.")}
<commit_msg>Fix todobackend example (which runs only on nightly)<commit_after>//! A [Todo-Backend](http://todobackend.com/) implementation with rotor-http.
//!
//! Todo-Backed is a showcase to compare different languages and frameworks
//! for building web services. One has to implement a simple todo list
//! that is checked by automatic tests available on the website.
//!
//! NOTE: This example only works with Nightly Rust for now because
//! of serde macros.
//!
//! To run use `cargo run --example todobackend --features nightly`.
//!
//! Now you can edit your own todo list via
//! http://todobackend.com/client/index.html?http://localhost:3000
//! (The web page is just for the frontend, the backend is this Rust
//! program.)

#![cfg_attr(feature="nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature="nightly", plugin(serde_macros))]
#![allow(warnings)]

extern crate rotor;
extern crate rotor_http;
#[cfg(feature="nightly")] extern crate serde;
#[cfg(feature="nightly")] extern crate serde_json;

use std::borrow::Cow;
use std::collections::HashMap;
use std::str::from_utf8;
use std::time::Duration;

use rotor::{Scope, Time};
use rotor::mio::tcp::TcpListener;
use rotor_http::server::{self, Fsm, Head, RecvMode, Response, Server};

/// Represents a single Todo entry.
///
/// The entry is serialized and deserialized by serde without
/// any handwritten code.
#[cfg(feature="nightly")]
#[derive(Serialize, Deserialize, Debug)]
struct Todo {
    url: Option<String>,
    title: String,
    #[serde(default)]
    completed: bool,
    #[serde(default)]
    order: u64,
}

#[cfg(feature="nightly")]
impl Todo {
    fn set_url(&mut self, id: u64) {
        self.url = Some(format!("http://localhost:3000/todo/{}", id));
    }

    fn patch(&mut self, patch: TodoPatch) {
        if let Some(title) = patch.title {
            self.title = title;
        }
        if let Some(completed) = patch.completed {
            self.completed = completed;
        }
        if let Some(order) = patch.order {
            self.order = order
        }
    }
}

/// One can patch the title and the status of todos. It is also
/// possible to change the ordering.
#[cfg(feature="nightly")]
#[derive(Deserialize, Debug)]
struct TodoPatch {
    title: Option<String>,
    completed: Option<bool>,
    order: Option<u64>,
}

#[cfg(feature="nightly")]
/// Context is used for global storage.
struct Context {
    /// The last id assigned to a todo entry.
    ///
    /// New ids are incremented by one.
    last_id: u64,
    /// The in memory "database" used for the todo-list.
    database: HashMap<u64, Todo>,
}

#[cfg(feature="nightly")]
trait Database {
    /// Get a unique new id.
    fn id(&mut self) -> u64;
    /// List all existing Todos.
    fn list(&self) -> Vec<&Todo>;
    /// Create a new Todo.
    fn create(&mut self, u64, Todo);
    /// Clear the database.
    fn clear(&mut self);
    /// Get one todo by id.
    fn get(&mut self, u64) -> Option<&Todo>;
    /// Get a mutable reference of a todo.
    ///
    /// This is useful for updating or patching an entry.
    fn get_mut(&mut self, u64) -> Option<&mut Todo>;
    /// Delete an entry.
    fn delete(&mut self, id: u64);
}

#[cfg(feature="nightly")]
impl Database for Context {
    fn id(&mut self) -> u64 {
        self.last_id += 1;
        self.last_id
    }

    fn list(&self) -> Vec<&Todo> {
        self.database.values().collect()
    }

    fn create(&mut self, id: u64, todo: Todo) {
        assert!(self.database.insert(id, todo).is_none());
    }

    fn clear(&mut self) {
        self.database.clear();
    }

    fn get(&mut self, id: u64) -> Option<&Todo> {
        self.database.get(&id)
    }

    fn get_mut(&mut self, id: u64) -> Option<&mut Todo> {
        self.database.get_mut(&id)
    }

    fn delete(&mut self, id: u64) {
        self.database.remove(&id);
    }
}

/// The main server state machine. After parsing the header
/// the type of request determined. Each variant of the
/// enum represents a single action. Some are directly related
/// to todo entries others are for error handling or and CORS.
#[derive(Debug, Clone)]
enum TodoBackend {
    List,
    Create,
    Clear,
    Get(u64),
    Patch(u64),
    Delete(u64),
    Preflight,
    MethodNotAllowed(&'static [u8]),
    NotFound,
}

#[cfg(feature="nightly")]
impl Server for TodoBackend {
    type Seed = ();
    type Context = Context;
    fn headers_received(_seed: (), head: Head, _response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<(Self, RecvMode, Time)>
    {
        use TodoBackend::*;
        Some((if head.method == "OPTIONS" {
                Preflight
            } else if head.path == "/" {
                match head.method {
                    "GET" => List,
                    "POST" => Create,
                    "DELETE" => Clear,
                    _ => MethodNotAllowed(b"GET, POST, DELETE"),
                }
            } else if head.path.starts_with("/todo/") {
                let id = head.path[6..].parse().unwrap();
                match head.method {
                    "GET" => Get(id),
                    "PATCH" => Patch(id),
                    "DELETE" => Delete(id),
                    _ => MethodNotAllowed(b"GET, PATCH, DELETE"),
                }
            } else {
                NotFound
            }, RecvMode::Buffered(1024), scope.now() + Duration::new(10, 0)))

    }

    fn request_received(self, data: &[u8], response: &mut Response,
        scope: &mut Scope<Context>)
        -> Option<Self>
    {
        use self::TodoBackend::*;
        let text_data = from_utf8(data).unwrap();
        let (status, reason, body) = match self {
            List => (200, "OK", Cow::Owned(serde_json::to_string(&scope.list()).unwrap().into_bytes())),
            Create => {
                let mut todo: Todo = serde_json::from_str(text_data).unwrap();
                let id = scope.id();
                todo.set_url(id);
                let body = serde_json::to_string(&todo).unwrap().into_bytes();
                scope.create(id, todo);
                (201, "Created", Cow::Owned(body))
            }
            Clear => {
                scope.clear();
                (200, "OK", Cow::Borrowed(&b"[]"[..]))
            }
            Get(id) => (200, "OK",
                Cow::Owned(serde_json::to_string(&scope.get(id)).unwrap().into_bytes())),
            Patch(id) => {
                let patch: TodoPatch = serde_json::from_str(text_data).unwrap();
                if let Some(todo) = scope.get_mut(id) {
                    todo.patch(patch);
                    let body = serde_json::to_string(&todo).unwrap().into_bytes();
                    (200, "OK", Cow::Owned(body))
                } else {
                    (404, "Not found", Cow::Borrowed(&b"{}"[..]))
                }
            }
            Delete(id) => {
                scope.delete(id);
                (200, "OK", Cow::Borrowed(&b"{}"[..]))
            }
            Preflight => {
                response.status(200, "OK");
                response.add_length(0).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Access-Control-Allow-Methods",
                                    b"GET, POST, DELETE, PATCH").unwrap();
                response.add_header("Access-Control-Allow-Headers",
                                    b"Content-Type").unwrap();
                response.add_header("Access-Control-Max-Age", b"60").unwrap();
                response.done_headers().unwrap();
                response.done();
                return None;
            }
            MethodNotAllowed(methods) => {
                let reason = "Method Not Allowed";
                response.status(405, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.add_header("Allow", methods).unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            }
            NotFound => {
                let reason = "Not Found";
                response.status(404, reason);
                response.add_length(reason.len() as u64).unwrap();
                response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
                response.add_header("Content-Type", b"text/plain").unwrap();
                response.done_headers().unwrap();
                response.write_body(reason.as_bytes());
                response.done();
                return None;
            },
        };
        response.status(status, reason);
        response.add_length(body.len() as u64).unwrap();
        response.add_header("Access-Control-Allow-Origin", b"*").unwrap();
        response.add_header("Content-Type", b"application/json").unwrap();
        response.done_headers().unwrap();
        response.write_body(&body[..]);
        response.done();
        None
    }
    // It is save to leave out `request_chunk` and `request_end` since we
    // only use buffered requests in this example.
    fn request_chunk(self, _chunk: &[u8], _response: &mut Response,
        _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }
    fn request_end(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self> { unreachable!(); }

    fn timeout(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<(Self, Time)>
    {
        unimplemented!();
    }
    fn wakeup(self, _response: &mut Response, _scope: &mut Scope<Context>)
        -> Option<Self>
    {
        unimplemented!();
    }
}

#[cfg(feature="nightly")]
fn main() {
    let event_loop = rotor::Loop::new(&rotor::Config::new()).unwrap();
    let mut loop_inst = event_loop.instantiate(Context {
        last_id: 0,
        database: HashMap::new(),
    });
    let lst = TcpListener::bind(&"127.0.0.1:3000".parse().unwrap()).unwrap();
    loop_inst.add_machine_with(|scope| {
        Fsm::<TodoBackend, _>::new(lst, scope)
    }).unwrap();
    loop_inst.run().unwrap();
}

#[cfg(not(feature="nightly"))]
fn main() { panic!("See NOTE in source. You must use --features nightly.")}
<|endoftext|>


<commit_before><commit_msg>tweak<commit_after><|endoftext|>


<commit_before>extern crate iron;
extern crate router;
extern crate handlebars;

use self::iron::{Request, IronResult, Response, status};
use self::handlebars::to_json;
use presentation::helper::templating::*;
use presentation::model::section::Section;
use presentation::model::rank::Rank;
use presentation::helper::session;
use presentation::helper::util::{get_formdata, to_ironresult, redirect};
use business::crud::remove_round;
use business::round_data::*;

pub fn get_score(req: &mut Request) -> IronResult<Response> {
    let round_data = to_ironresult(get_all_round_data())?;
    let mut ranks = round_data
        .into_iter()
        .filter(|x| !x.is_last_answer_wrong)
        .map(|x| {
            let points = x.answer_count as i32 * 30;
            let categories: String = x.categories
                .into_iter()
                .map(move |y| y.text)
                .fold("".to_owned(), |mut acc, x| {
                    acc.push_str(&x);
                    acc
                });
            println!("start: {}, end: {}", x.start_time.format("%Y-%m-%d %H:%M:%S").to_string(), x.end_time.format("%Y-%m-%d %H:%M:%S").to_string());
            let game_length = x
                .end_time
                .signed_duration_since(x.start_time)
                .num_seconds() as i32;
            let game_start = x.start_time.format("%Y-%m-%d %H:%M:%S").to_string();
            Rank {
                round_id: x.id,
                name: x.player.name,
                score: points / game_length,
                points: points,
                game_start: game_start,
                game_length: game_length,
                categories: categories,
            }
        })
        .collect::<Vec<Rank>>();
    ranks.sort_by_key(|x| x.score);
    let data = btreemap! {
        "ranks".to_string() => to_json(&ranks),
    };

    let template = generate_site(req, "quiz/scoreboard", data, Some(&Section::Quiz));
    Ok(Response::with((template, status::Ok)))
}

pub fn post_score_remove(req: &mut Request) -> IronResult<Response> {
    if session::get_admin(req)?.is_none() {
        return redirect(req, "get_root");
    }
    let id = get_formdata(req, "round_id")?;
    let id_as_int = to_ironresult(id.parse::<i32>())?;
    to_ironresult(remove_round(id_as_int))?;
    redirect(req, "get_quiz_score")
}
<commit_msg>Run rustfmt<commit_after>extern crate iron;
extern crate router;
extern crate handlebars;

use self::iron::{Request, IronResult, Response, status};
use self::handlebars::to_json;
use presentation::helper::templating::*;
use presentation::model::section::Section;
use presentation::model::rank::Rank;
use presentation::helper::session;
use presentation::helper::util::{get_formdata, to_ironresult, redirect};
use business::crud::remove_round;
use business::round_data::*;

pub fn get_score(req: &mut Request) -> IronResult<Response> {
    let round_data = to_ironresult(get_all_round_data())?;
    let mut ranks = round_data
        .into_iter()
        .filter(|x| !x.is_last_answer_wrong)
        .map(|x| {
            let points = x.answer_count as i32 * 30;
            let categories: String = x.categories
                .into_iter()
                .map(move |y| y.text)
                .fold("".to_owned(), |mut acc, x| {
                    acc.push_str(&x);
                    acc
                });
            println!("start: {}, end: {}",
                     x.start_time.format("%Y-%m-%d %H:%M:%S").to_string(),
                     x.end_time.format("%Y-%m-%d %H:%M:%S").to_string());
            let game_length = x.end_time
                .signed_duration_since(x.start_time)
                .num_seconds() as i32;
            let game_start = x.start_time.format("%Y-%m-%d %H:%M:%S").to_string();
            Rank {
                round_id: x.id,
                name: x.player.name,
                score: points / game_length,
                points: points,
                game_start: game_start,
                game_length: game_length,
                categories: categories,
            }
        })
        .collect::<Vec<Rank>>();
    ranks.sort_by_key(|x| x.score);
    let data = btreemap! {
        "ranks".to_string() => to_json(&ranks),
    };

    let template = generate_site(req, "quiz/scoreboard", data, Some(&Section::Quiz));
    Ok(Response::with((template, status::Ok)))
}

pub fn post_score_remove(req: &mut Request) -> IronResult<Response> {
    if session::get_admin(req)?.is_none() {
        return redirect(req, "get_root");
    }
    let id = get_formdata(req, "round_id")?;
    let id_as_int = to_ironresult(id.parse::<i32>())?;
    to_ironresult(remove_round(id_as_int))?;
    redirect(req, "get_quiz_score")
}
<|endoftext|>


<commit_before><commit_msg>fix satic init in tests<commit_after><|endoftext|>


<commit_before>// Copyright 2022 The ChromiumOS Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::num::ParseIntError;
use std::str::ParseBoolError;

#[cfg(all(unix, feature = "audio_cras"))]
use libcras::CrasClientType;
#[cfg(all(unix, feature = "audio_cras"))]
use libcras::CrasSocketType;
use serde::Deserialize;
use serde_keyvalue::FromKeyValues;
use thiserror::Error as ThisError;

use crate::virtio::snd::sys::StreamSourceBackend as SysStreamSourceBackend;

#[derive(ThisError, Debug)]
pub enum Error {
    /// Unknown snd parameter value.
    #[error("Invalid snd parameter value ({0}): {1}")]
    InvalidParameterValue(String, String),
    /// Failed to parse bool value.
    #[error("Invalid bool value: {0}")]
    InvalidBoolValue(ParseBoolError),
    /// Failed to parse int value.
    #[error("Invalid int value: {0}")]
    InvalidIntValue(ParseIntError),
    // Invalid backend.
    #[error("Backend is not implemented")]
    InvalidBackend,
    /// Failed to parse parameters.
    #[error("Invalid snd parameter: {0}")]
    UnknownParameter(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(try_from = "&str")]
pub enum StreamSourceBackend {
    NULL,
    Sys(SysStreamSourceBackend),
}

impl TryFrom<&str> for StreamSourceBackend {
    type Error = Error;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match s {
            "null" => Ok(StreamSourceBackend::NULL),
            _ => SysStreamSourceBackend::try_from(s).map(StreamSourceBackend::Sys),
        }
    }
}

/// Holds the parameters for a cras sound device
#[derive(Debug, Clone, Deserialize, FromKeyValues)]
#[serde(deny_unknown_fields, default)]
pub struct Parameters {
    pub capture: bool,
    pub num_output_devices: u32,
    pub num_input_devices: u32,
    pub backend: StreamSourceBackend,
    pub num_output_streams: u32,
    pub num_input_streams: u32,
    #[cfg(all(unix, feature = "audio_cras"))]
    #[serde(deserialize_with = "libcras::deserialize_cras_client_type")]
    pub client_type: CrasClientType,
    #[cfg(all(unix, feature = "audio_cras"))]
    pub socket_type: CrasSocketType,
}

impl Default for Parameters {
    fn default() -> Self {
        Parameters {
            capture: false,
            num_output_devices: 1,
            num_input_devices: 1,
            backend: StreamSourceBackend::NULL,
            num_output_streams: 1,
            num_input_streams: 1,
            #[cfg(all(unix, feature = "audio_cras"))]
            client_type: CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            #[cfg(all(unix, feature = "audio_cras"))]
            socket_type: CrasSocketType::Unified,
        }
    }
}

impl Parameters {
    pub(crate) fn get_total_output_streams(&self) -> u32 {
        self.num_output_devices * self.num_output_streams
    }

    pub(crate) fn get_total_input_streams(&self) -> u32 {
        self.num_input_devices * self.num_input_streams
    }

    pub(crate) fn get_total_streams(&self) -> u32 {
        self.get_total_output_streams() + self.get_total_input_streams()
    }
}

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

    fn check_failure(s: &str) {
        serde_keyvalue::from_key_values::<Parameters>(s).expect_err("parse should have failed");
    }

    fn check_success(
        s: &str,
        capture: bool,
        backend: StreamSourceBackend,
        num_output_devices: u32,
        num_input_devices: u32,
        num_output_streams: u32,
        num_input_streams: u32,
    ) {
        let params: Parameters =
            serde_keyvalue::from_key_values(s).expect("parse should have succeded");
        assert_eq!(params.capture, capture);
        assert_eq!(params.backend, backend);
        assert_eq!(params.num_output_devices, num_output_devices);
        assert_eq!(params.num_input_devices, num_input_devices);
        assert_eq!(params.num_output_streams, num_output_streams);
        assert_eq!(params.num_input_streams, num_input_streams);
    }

    #[test]
    fn parameters_fromstr() {
        check_failure("capture=none");
        check_success(
            "capture=false",
            false,
            StreamSourceBackend::NULL,
            1,
            1,
            1,
            1,
        );
        check_success(
            "capture=true,num_output_streams=2,num_input_streams=3",
            true,
            StreamSourceBackend::NULL,
            1,
            1,
            2,
            3,
        );
        check_success(
            "capture=true,num_output_devices=3,num_input_devices=2",
            true,
            StreamSourceBackend::NULL,
            3,
            2,
            1,
            1,
        );
        check_success(
            "capture=true,num_output_devices=2,num_input_devices=3,\
            num_output_streams=3,num_input_streams=2",
            true,
            StreamSourceBackend::NULL,
            2,
            3,
            3,
            2,
        );
        check_success(
            "capture=true,backend=null,num_output_devices=2,num_input_devices=3,\
            num_output_streams=3,num_input_streams=2",
            true,
            StreamSourceBackend::NULL,
            2,
            3,
            3,
            2,
        );
    }

    #[test]
    #[cfg(all(unix, feature = "audio_cras"))]
    fn cras_parameters_fromstr() {
        fn cras_check_success(
            s: &str,
            backend: StreamSourceBackend,
            client_type: CrasClientType,
            socket_type: CrasSocketType,
        ) {
            let params = s.parse::<Parameters>().expect("parse should have succeded");
            assert_eq!(params.backend, backend);
            assert_eq!(params.client_type, client_type);
            assert_eq!(params.socket_type, socket_type);
        }

        cras_check_success(
            "backend=cras",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
        cras_check_success(
            "backend=cras,client_type=crosvm",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
        cras_check_success(
            "backend=cras,client_type=arcvm",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_ARCVM,
            CrasSocketType::Unified,
        );
        check_failure("backend=cras,client_type=none");
        cras_check_success(
            "backend=cras,socket_type=legacy",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Legacy,
        );
        cras_check_success(
            "backend=cras,socket_type=unified",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
    }
}
<commit_msg>devices: snd: Fix cras_parameters_from_str test<commit_after>// Copyright 2022 The ChromiumOS Authors.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::num::ParseIntError;
use std::str::ParseBoolError;

#[cfg(all(unix, feature = "audio_cras"))]
use libcras::CrasClientType;
#[cfg(all(unix, feature = "audio_cras"))]
use libcras::CrasSocketType;
use serde::Deserialize;
use serde_keyvalue::FromKeyValues;
use thiserror::Error as ThisError;

use crate::virtio::snd::sys::StreamSourceBackend as SysStreamSourceBackend;

#[derive(ThisError, Debug)]
pub enum Error {
    /// Unknown snd parameter value.
    #[error("Invalid snd parameter value ({0}): {1}")]
    InvalidParameterValue(String, String),
    /// Failed to parse bool value.
    #[error("Invalid bool value: {0}")]
    InvalidBoolValue(ParseBoolError),
    /// Failed to parse int value.
    #[error("Invalid int value: {0}")]
    InvalidIntValue(ParseIntError),
    // Invalid backend.
    #[error("Backend is not implemented")]
    InvalidBackend,
    /// Failed to parse parameters.
    #[error("Invalid snd parameter: {0}")]
    UnknownParameter(String),
}

#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(try_from = "&str")]
pub enum StreamSourceBackend {
    NULL,
    Sys(SysStreamSourceBackend),
}

impl TryFrom<&str> for StreamSourceBackend {
    type Error = Error;

    fn try_from(s: &str) -> Result<Self, Self::Error> {
        match s {
            "null" => Ok(StreamSourceBackend::NULL),
            _ => SysStreamSourceBackend::try_from(s).map(StreamSourceBackend::Sys),
        }
    }
}

/// Holds the parameters for a cras sound device
#[derive(Debug, Clone, Deserialize, FromKeyValues)]
#[serde(deny_unknown_fields, default)]
pub struct Parameters {
    pub capture: bool,
    pub num_output_devices: u32,
    pub num_input_devices: u32,
    pub backend: StreamSourceBackend,
    pub num_output_streams: u32,
    pub num_input_streams: u32,
    #[cfg(all(unix, feature = "audio_cras"))]
    #[serde(deserialize_with = "libcras::deserialize_cras_client_type")]
    pub client_type: CrasClientType,
    #[cfg(all(unix, feature = "audio_cras"))]
    pub socket_type: CrasSocketType,
}

impl Default for Parameters {
    fn default() -> Self {
        Parameters {
            capture: false,
            num_output_devices: 1,
            num_input_devices: 1,
            backend: StreamSourceBackend::NULL,
            num_output_streams: 1,
            num_input_streams: 1,
            #[cfg(all(unix, feature = "audio_cras"))]
            client_type: CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            #[cfg(all(unix, feature = "audio_cras"))]
            socket_type: CrasSocketType::Unified,
        }
    }
}

impl Parameters {
    pub(crate) fn get_total_output_streams(&self) -> u32 {
        self.num_output_devices * self.num_output_streams
    }

    pub(crate) fn get_total_input_streams(&self) -> u32 {
        self.num_input_devices * self.num_input_streams
    }

    pub(crate) fn get_total_streams(&self) -> u32 {
        self.get_total_output_streams() + self.get_total_input_streams()
    }
}

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

    fn check_failure(s: &str) {
        serde_keyvalue::from_key_values::<Parameters>(s).expect_err("parse should have failed");
    }

    fn check_success(
        s: &str,
        capture: bool,
        backend: StreamSourceBackend,
        num_output_devices: u32,
        num_input_devices: u32,
        num_output_streams: u32,
        num_input_streams: u32,
    ) {
        let params: Parameters =
            serde_keyvalue::from_key_values(s).expect("parse should have succeded");
        assert_eq!(params.capture, capture);
        assert_eq!(params.backend, backend);
        assert_eq!(params.num_output_devices, num_output_devices);
        assert_eq!(params.num_input_devices, num_input_devices);
        assert_eq!(params.num_output_streams, num_output_streams);
        assert_eq!(params.num_input_streams, num_input_streams);
    }

    #[test]
    fn parameters_fromstr() {
        check_failure("capture=none");
        check_success(
            "capture=false",
            false,
            StreamSourceBackend::NULL,
            1,
            1,
            1,
            1,
        );
        check_success(
            "capture=true,num_output_streams=2,num_input_streams=3",
            true,
            StreamSourceBackend::NULL,
            1,
            1,
            2,
            3,
        );
        check_success(
            "capture=true,num_output_devices=3,num_input_devices=2",
            true,
            StreamSourceBackend::NULL,
            3,
            2,
            1,
            1,
        );
        check_success(
            "capture=true,num_output_devices=2,num_input_devices=3,\
            num_output_streams=3,num_input_streams=2",
            true,
            StreamSourceBackend::NULL,
            2,
            3,
            3,
            2,
        );
        check_success(
            "capture=true,backend=null,num_output_devices=2,num_input_devices=3,\
            num_output_streams=3,num_input_streams=2",
            true,
            StreamSourceBackend::NULL,
            2,
            3,
            3,
            2,
        );
    }

    #[test]
    #[cfg(all(unix, feature = "audio_cras"))]
    fn cras_parameters_fromstr() {
        fn cras_check_success(
            s: &str,
            backend: StreamSourceBackend,
            client_type: CrasClientType,
            socket_type: CrasSocketType,
        ) {
            let params: Parameters =
                serde_keyvalue::from_key_values(s).expect("parse should have succeded");
            assert_eq!(params.backend, backend);
            assert_eq!(params.client_type, client_type);
            assert_eq!(params.socket_type, socket_type);
        }

        cras_check_success(
            "backend=cras",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
        cras_check_success(
            "backend=cras,client_type=crosvm",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
        cras_check_success(
            "backend=cras,client_type=arcvm",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_ARCVM,
            CrasSocketType::Unified,
        );
        check_failure("backend=cras,client_type=none");
        cras_check_success(
            "backend=cras,socket_type=legacy",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Legacy,
        );
        cras_check_success(
            "backend=cras,socket_type=unified",
            StreamSourceBackend::Sys(SysStreamSourceBackend::CRAS),
            CrasClientType::CRAS_CLIENT_TYPE_CROSVM,
            CrasSocketType::Unified,
        );
    }
}
<|endoftext|>


<commit_before>//
//  0 1 0  Mnemosyne: a functional systems programming language.
//  0 0 1  (c) 2015 Hawk Weisman
//  1 1 1  hi@hawkweisman.me
//
//  Mnemosyne is released under the MIT License. Please refer to
//  the LICENSE file at the top-level directory of this distribution
//  or at https://github.com/hawkw/mnemosyne/.
//
//! # Mnemosyne core
//!
//! This crate contains the core Mnemosyne programming language components.
//! This includes the mnemosyne abstract syntax tree (`semantic::ast`),
//! functions for performing semantic analysis (`semantic`), functions
//! for compiling abstract syntax trees to LLVM bytecode (`compile`), and
//! assorted utility code such as a positional reference type and a
//! `ForkTable` data structure for use as a symbol table.
//!
//! The Mnemosyne parser is contained in a separate crate in order to improve
//! compile times.
#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(static_recursion)]
#![feature(box_syntax, box_patterns)]

extern crate rustc;
extern crate libc;
extern crate combine;
// extern crate iron_llvm;
// extern crate llvm_sys;
#[macro_use] extern crate itertools;

use rustc::lib::llvm::{LLVMVersionMajor, LLVMVersionMinor};

use std::fmt::Debug;

// include!(concat!(env!("OUT_DIR"), "/gen.rs"));

/// Returns the Mnemosyne version as a String
pub fn mnemosyne_version() -> String {
    format!("Mnemosyne {}", env!("CARGO_PKG_VERSION"))
}

/// Returns the current LLVM version as a String
pub fn llvm_version() -> String {
    format!("LLVM v{}.{}", LLVMVersionMajor, LLVMVersionMinor)
}

/// Macro for formatting an internal compiler error panic.
///
/// This should be used instead of the Rust standard library's `panic!()`
/// macro in the event of an unrecoverable internal compiler error.
#[macro_export]
macro_rules! ice {
    ($msg:expr) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , $msg
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          );
    ($fmt:expr, $($arg:tt)+) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , format_args!($fmt, $($arg)+)
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          )
}

pub mod position;
pub mod semantic;
pub mod compile;
pub mod forktable;
pub mod chars;
pub mod errors;

pub use semantic::ast;
pub use self::errors::*;
<commit_msg>Fix new llvm_version() function<commit_after>//
//  0 1 0  Mnemosyne: a functional systems programming language.
//  0 0 1  (c) 2015 Hawk Weisman
//  1 1 1  hi@hawkweisman.me
//
//  Mnemosyne is released under the MIT License. Please refer to
//  the LICENSE file at the top-level directory of this distribution
//  or at https://github.com/hawkw/mnemosyne/.
//
//! # Mnemosyne core
//!
//! This crate contains the core Mnemosyne programming language components.
//! This includes the mnemosyne abstract syntax tree (`semantic::ast`),
//! functions for performing semantic analysis (`semantic`), functions
//! for compiling abstract syntax trees to LLVM bytecode (`compile`), and
//! assorted utility code such as a positional reference type and a
//! `ForkTable` data structure for use as a symbol table.
//!
//! The Mnemosyne parser is contained in a separate crate in order to improve
//! compile times.
#![crate_name = "mnemosyne"]
#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(static_recursion)]
#![feature(box_syntax, box_patterns)]

extern crate rustc;
extern crate libc;
extern crate combine;
// extern crate iron_llvm;
// extern crate llvm_sys;
#[macro_use] extern crate itertools;

use rustc::lib::llvm::{LLVMVersionMajor, LLVMVersionMinor};

// include!(concat!(env!("OUT_DIR"), "/gen.rs"));

/// Returns the Mnemosyne version as a String
pub fn mnemosyne_version() -> String {
    format!("Mnemosyne {}", env!("CARGO_PKG_VERSION"))
}

/// Returns the current LLVM version as a String
pub fn llvm_version() -> String {
    unsafe {
        format!("LLVM v{}.{}", LLVMVersionMajor(), LLVMVersionMinor())
    }
}

/// Macro for formatting an internal compiler error panic.
///
/// This should be used instead of the Rust standard library's `panic!()`
/// macro in the event of an unrecoverable internal compiler error.
#[macro_export]
macro_rules! ice {
    ($msg:expr) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , $msg
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          );
    ($fmt:expr, $($arg:tt)+) => (
        panic!( "[internal error] {}\n \
                 [internal error] Something has gone horribly wrong.\n \
                 [internal error] Please contact the Mnemosyne implementors.\n\
                 {}, {}"
              , format_args!($fmt, $($arg)+)
              , $crate::mnemosyne_version(), $crate::llvm_version()
              )
          )
}

pub mod position;
pub mod semantic;
pub mod compile;
pub mod forktable;
pub mod chars;
pub mod errors;

pub use semantic::ast;
pub use self::errors::*;
<|endoftext|>


<commit_before>//xfail-stage0
//xfail-stage1
//xfail-stage2
use std;

fn main() {

    obj inner() {
        fn a() -> int {
            ret 2;
        }
        fn m() -> uint {
            ret 3u;
        }
        fn z() -> uint {
            ret self.m();
        }
    }

    auto my_inner = inner();

    auto my_outer = obj() {
        fn b() -> uint {
            ret 5u;
        }
        fn n() -> str {
            ret "world!";
        }
        with my_inner
    };

    log_err my_inner.z();
    assert (my_inner.z() == 3u);
    log_err my_outer.z();
    assert (my_outer.z() == 3u);
}

/*
   Here, when we make the self-call to self.m() in inner, we're going
   back through the outer "self".  That outer "self" has 5 methods in
   its vtable: a, b, m, n, z.  But the method z has already been
   compiled, and at the time it was compiled, it expected "self" to
   only have three methods in its vtable: a, m, and z.  So, the method
   z thinks that "self.m()" means "look up method #1 (indexing from 0)
   in my vtable and call it".  That means that it'll call method #1 on
   the larger vtable that it thinks is "self", and method #1 at that
   point is b.

   So, when we call my_inner.z(), we get 3, which is what we'd
   expect.  When we call my_outer.z(), we should also get 3, because
   at no point is z being overridden.

   To fix this bug, we need to add a second level of forwarding
   functions (let's call them "backwarding functions") on the inner
   object.  Every time an object is extended with another object, we
   have to rewrite the inner object's vtable to account for the fact
   that future self-calls will get a larger object.  The inner
   object's vtable will need to have five slots, too.  The ones for b
   and n will point right back at the outer object.  (These are the
   "backwarding" ones.)  And the ones for a, m, and z will point at
   the original, real vtable for inner.

   Adding support for this is issue #702.

*/
<commit_msg>Revised plan of attack for backwarding functions.<commit_after>//xfail-stage0
//xfail-stage1
//xfail-stage2
use std;

fn main() {

    obj inner() {
        fn a() -> int {
            ret 2;
        }
        fn m() -> uint {
            ret 3u;
        }
        fn z() -> uint {
            ret self.m();
        }
    }

    auto my_inner = inner();

    auto my_outer = obj() {
        fn b() -> uint {
            ret 5u;
        }
        fn n() -> str {
            ret "world!";
        }
        with my_inner
    };

    log_err my_inner.z();
    assert (my_inner.z() == 3u);
    log_err my_outer.z();
    assert (my_outer.z() == 3u);
}

/*
   Here, when we make the self-call to self.m() in inner, we're going
   back through the outer "self".  That outer "self" has 5 methods in
   its vtable: a, b, m, n, z.  But the method z has already been
   compiled, and at the time it was compiled, it expected "self" to
   only have three methods in its vtable: a, m, and z.  So, the method
   z thinks that "self.m()" means "look up method #1 (indexing from 0)
   in my vtable and call it".  That means that it'll call method #1 on
   the larger vtable that it thinks is "self", and method #1 at that
   point is b.

   So, when we call my_inner.z(), we get 3, which is what we'd
   expect.  When we call my_outer.z(), we should also get 3, because
   at no point is z being overridden.

   To fix this bug, we need to make the vtable slots on the inner
   object match whatever the object being passed in at runtime has.
   My first instinct was to change the vtable to match the runtime
   object, but vtables are already baked into RO memory.  So, instead,
   we're going to tweak the object being passed in at runtime to match
   the vtable that inner already has.  That is, it needs to only have
   a, m, and z slots in its vtable, and each one of those slots will
   forward to the *outer* object's a, m, and z slots, respectively.
   From there they will either head right back to inner, or they'll be
   overridden.

   Adding support for this is issue #702.

*/
<|endoftext|>


<commit_before>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;
    const N_CURSORS: i32 = 9;

    let mut unicode_offset = 0x80;
    let mut blink_state = true;

    let mut cursor_state_1 = 2;
    let mut cursor_state_2 = 3;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        let cursor_state_text = [
            "Invisible (click to change) ",
            "Underscore (click to change)",
            "Block (click to change)     ",
            "Outline (click to change)   ",
            "Caret (click to change)     ",
            "Half-block (click to change)",
            "Central (click to change)   ",
            "Cross (click to change)     ",
            "Heavy box (click to change) ",
        ];

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(Attribute::Dim);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(Attribute::Dim);

            window.attron(Attribute::Blink);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(Attribute::Bold);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(Attribute::Italic);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(ColorPair(3));
            window.attron(Attribute::Underline);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(ColorPair(1));
            window.attron(Attribute::Underline | Attribute::Italic);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(ColorPair(2));
            window.attron(Attribute::Blink);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(ColorPair(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(ColorPair(6));
            window.attron(Attribute::Strikeout);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(ColorPair(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 {
                // Show extended characters
                window.mvaddstr(
                    5 + i % 16,
                    (i / 16) * 5,
                    &format!("{:02X} ", i + unicode_offset),
                );
                if i + unicode_offset > ' ' as i32 {
                    window.addch((i + unicode_offset) as chtype);
                } else {
                    window.addch(' ');
                }
                window.addch(' ');
            }

            // Skipped the full RGB example, I don't think there's a suitable feature in ncurses

            redraw = false;
            window.attrset(COLOR_PAIR(1));

            for i in 0..6 {
                let line = 24 + i / 3;
                let col = 5 + 25 * (i % 3);

                let spanish = "Español";
                let russian = "Русский язык";
                let greek = "Ελληνικά";
                let georgian = "ქართული ენა";
                let fullwidth = " Ｆｕｌｌｗｉｄｔｈ";
                let combining_marks = "Cmin 	r";

                let texts = [
                    spanish,
                    russian,
                    greek,
                    georgian,
                    fullwidth,
                    combining_marks,
                ];

                if line < y_max && col < x_max {
                    window.mvaddnstr(line, 5 + 25 * (i % 3), texts[i as usize], x_max - col);
                }
            }

            window.mvaddstr(1, COL3, "Click on cursor descriptions to");
            window.mvaddstr(2, COL3, "cycle through possible cursors");
            window.mvaddstr(3, COL3, "Click on colors at left to change");
            window.mvaddstr(4, COL3, "colors used for under/over/outlining");
            window.mvaddstr(5, COL3, "Click 'Blink' at bottom to toggle");
            window.mvaddstr(6, COL3, "'real' blinking vs. 'highlit' blink");
        }

        window.mvaddnstr(
            19,
            color_block_start,
            cursor_state_text[cursor_state_1],
            x_max - color_block_start,
        );
        window.mvaddnstr(
            20,
            color_block_start,
            cursor_state_text[cursor_state_2],
            x_max - color_block_start,
        );

        for i in 0..color_block_cols * color_block_lines {
            let n_color_blocks = 256;

            window.attrset(COLOR_PAIR(
                if i >= n_color_blocks { 2 } else { i as chtype },
            ));
            if i > 2 && i < n_color_blocks {
                init_pair(i as i16, i as i16, COLOR_BLACK);
            }
            if i % color_block_cols == 0 {
                window.mv(i / color_block_cols, color_block_start);
            }
            window.attron(A_REVERSE);
            window.addstr(" ");
        }

        window.mv(19, color_block_start - 3);
        window.refresh();
        let c = window.getch();
        window.attrset(COLOR_PAIR(1));

        match c {
            Some(Input::KeyF1) => quit = true,
            Some(Input::Character(x)) if x == 27 as char => quit = true,
            Some(Input::KeyF2) => {
                blink_state = !blink_state;
                set_blink(blink_state);
            }
            Some(x) if x != Input::KeyMouse => {
                window.mvaddstr(0, COL1, &format!("Key {:?} hit          ", x));
            }
            Some(Input::KeyMouse) => {
                if let Ok(mouse_event) = getmouse() {
                    window.mvaddstr(
                        0,
                        COL1,
                        &format!(
                            "Mouse at {} x {}: {}",
                            mouse_event.x,
                            mouse_event.y,
                            mouse_event.bstate
                        ),
                    );
                    if mouse_event.x >= color_block_start {
                        if mouse_event.y == 19 {
                            // blink/non-blink toggle
                            cursor_state_1 = (cursor_state_1 + 1) % N_CURSORS as usize;
                        } else if mouse_event.y == 20 {
                            // cycle cursor state
                            cursor_state_2 = (cursor_state_2 + 1) % N_CURSORS as usize;
                        }
                    } else if mouse_event.x >= 40 && mouse_event.x < 40 + 10 {
                        if mouse_event.y == 11 {
                            redraw = true;
                            unicode_offset += 0x80;
                        } else if mouse_event.y == 12 && unicode_offset != 0 {
                            redraw = true;
                            unicode_offset -= 0x80;
                        }
                    }
                }
            }
            _ => (),
        }
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<commit_msg>Handle window resizes in newtest.rs<commit_after>extern crate pancurses;

use pancurses::*;

fn main() {
    let window = initscr();

    start_color();
    use_default_colors();
    set_blink(true);

    cbreak();
    noecho();

    window.clear();
    window.refresh();

    window.keypad(true);

    init_pair(1, 15, COLOR_BLACK);
    init_pair(2, COLOR_BLACK, COLOR_YELLOW);

    set_title("NewTest: tests various pancurses features");

    mousemask(ALL_MOUSE_EVENTS, std::ptr::null_mut());

    window.attrset(COLOR_PAIR(1));

    let mut quit = false;
    let mut redraw = true;

    const COL1: i32 = 2;
    const COL2: i32 = COL1 + 30;
    const COL3: i32 = 72;
    const N_CURSORS: i32 = 9;

    let mut unicode_offset = 0x80;
    let mut blink_state = true;

    let mut cursor_state_1 = 2;
    let mut cursor_state_2 = 3;

    while !quit {
        let (y_max, x_max) = window.get_max_yx();
        let color_block_start = 54;
        let mut color_block_cols = (x_max - color_block_start) / 2;
        let color_block_lines = 19;

        let cursor_state_text = [
            "Invisible (click to change) ",
            "Underscore (click to change)",
            "Block (click to change)     ",
            "Outline (click to change)   ",
            "Caret (click to change)     ",
            "Half-block (click to change)",
            "Central (click to change)   ",
            "Cross (click to change)     ",
            "Heavy box (click to change) ",
        ];

        if color_block_cols < 0 {
            color_block_cols = 0;
        }

        if redraw {
            window.mvaddstr(1, COL1, "'Normal' white-on-black");

            window.attron(Attribute::Dim);
            window.mvaddstr(2, COL1, "Dimmed text");
            window.attroff(Attribute::Dim);

            window.attron(Attribute::Blink);
            window.mvaddstr(6, 40, "Blinking");
            window.attron(Attribute::Bold);
            window.mvaddstr(8, 40, "BlinkBold");
            window.attron(Attribute::Italic);
            window.mvaddstr(0, COL2, "BlinkBoldItalic");
            window.attrset(ColorPair(3));
            window.attron(Attribute::Underline);

            window.mvaddstr(1, COL2, "Underlined");

            window.attrset(ColorPair(1));
            window.attron(Attribute::Underline | Attribute::Italic);
            window.mvaddstr(2, COL2, "UnderlinedItalic");
            window.attrset(ColorPair(2));
            window.attron(Attribute::Blink);
            window.mvaddstr(4, COL1, "Black-on-yellow blinking");

            window.attrset(ColorPair(1));
            window.mv(4, COL2);
            text_in_a_box("Text in a box", &window);

            window.attrset(ColorPair(6));
            window.attron(Attribute::Strikeout);
            window.mvaddstr(10, 40, "Strikeout");
            window.attrset(ColorPair(1));

            window.mv(11, 40);
            text_in_a_box("Next Ucode pg", &window);
            if unicode_offset != 0 {
                window.mv(12, 40);
                text_in_a_box("Prev Ucode pg", &window);
            }
            window.mvprintw(13, 40, &format!("U+{:04X} ", unicode_offset));

            for i in 0..128 {
                // Show extended characters
                window.mvaddstr(
                    5 + i % 16,
                    (i / 16) * 5,
                    &format!("{:02X} ", i + unicode_offset),
                );
                if i + unicode_offset > ' ' as i32 {
                    window.addch((i + unicode_offset) as chtype);
                } else {
                    window.addch(' ');
                }
                window.addch(' ');
            }

            // Skipped the full RGB example, I don't think there's a suitable feature in ncurses

            redraw = false;
            window.attrset(COLOR_PAIR(1));

            for i in 0..6 {
                let line = 24 + i / 3;
                let col = 5 + 25 * (i % 3);

                let spanish = "Español";
                let russian = "Русский язык";
                let greek = "Ελληνικά";
                let georgian = "ქართული ენა";
                let fullwidth = " Ｆｕｌｌｗｉｄｔｈ";
                let combining_marks = "Cmin 	r";

                let texts = [
                    spanish,
                    russian,
                    greek,
                    georgian,
                    fullwidth,
                    combining_marks,
                ];

                if line < y_max && col < x_max {
                    window.mvaddnstr(line, 5 + 25 * (i % 3), texts[i as usize], x_max - col);
                }
            }

            window.mvaddstr(1, COL3, "Click on cursor descriptions to");
            window.mvaddstr(2, COL3, "cycle through possible cursors");
            window.mvaddstr(3, COL3, "Click on colors at left to change");
            window.mvaddstr(4, COL3, "colors used for under/over/outlining");
            window.mvaddstr(5, COL3, "Click 'Blink' at bottom to toggle");
            window.mvaddstr(6, COL3, "'real' blinking vs. 'highlit' blink");
        }

        window.mvaddnstr(
            19,
            color_block_start,
            cursor_state_text[cursor_state_1],
            x_max - color_block_start,
        );
        window.mvaddnstr(
            20,
            color_block_start,
            cursor_state_text[cursor_state_2],
            x_max - color_block_start,
        );

        for i in 0..color_block_cols * color_block_lines {
            let n_color_blocks = 256;

            window.attrset(COLOR_PAIR(
                if i >= n_color_blocks { 2 } else { i as chtype },
            ));
            if i > 2 && i < n_color_blocks {
                init_pair(i as i16, i as i16, COLOR_BLACK);
            }
            if i % color_block_cols == 0 {
                window.mv(i / color_block_cols, color_block_start);
            }
            window.attron(A_REVERSE);
            window.addstr(" ");
        }

        window.mv(19, color_block_start - 3);
        window.refresh();
        let c = window.getch();
        window.attrset(COLOR_PAIR(1));

        match c {
            Some(Input::KeyResize) => {
                redraw = true;
                resize_term(0, 0);
            }
            Some(Input::KeyF1) => quit = true,
            Some(Input::Character(x)) if x == 27 as char => quit = true,
            Some(Input::KeyF2) => {
                blink_state = !blink_state;
                set_blink(blink_state);
            }
            Some(x) if x != Input::KeyMouse => {
                window.mvaddstr(0, COL1, &format!("Key {:?} hit          ", x));
            }
            Some(Input::KeyMouse) => {
                if let Ok(mouse_event) = getmouse() {
                    window.mvaddstr(
                        0,
                        COL1,
                        &format!(
                            "Mouse at {} x {}: {}",
                            mouse_event.x,
                            mouse_event.y,
                            mouse_event.bstate
                        ),
                    );
                    if mouse_event.x >= color_block_start {
                        if mouse_event.y == 19 {
                            // blink/non-blink toggle
                            cursor_state_1 = (cursor_state_1 + 1) % N_CURSORS as usize;
                        } else if mouse_event.y == 20 {
                            // cycle cursor state
                            cursor_state_2 = (cursor_state_2 + 1) % N_CURSORS as usize;
                        }
                    } else if mouse_event.x >= 40 && mouse_event.x < 40 + 10 {
                        if mouse_event.y == 11 {
                            redraw = true;
                            unicode_offset += 0x80;
                        } else if mouse_event.y == 12 && unicode_offset != 0 {
                            redraw = true;
                            unicode_offset -= 0x80;
                        }
                    }
                }
            }
            _ => (),
        }
    }

    endwin();
}

fn text_in_a_box(text: &str, window: &Window) {
    let len = text.len();

    window.attron(A_OVERLINE | A_UNDERLINE | A_LEFTLINE);
    if len == 1 {
        window.attron(A_RIGHTLINE);
    }

    window.addnstr(text, 1);
    if len > 1 {
        window.attroff(A_LEFTLINE);
        if len > 2 {
            window.addnstr(&text[1..], len - 2);
        }
        window.attron(A_RIGHTLINE);
        window.addnstr(&text[len - 1..], 1);
    }

    window.attroff(A_OVERLINE | A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE);
}
<|endoftext|>


<commit_before>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.+)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str,
    is_pure_assertion_line: bool,
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);

        if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
            let mut sst = captures.get(3).unwrap().as_str(); // get the scope selector text
            let mut only_whitespace_after_token_end = true;

            if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                if let Some(end_token_pos) = sst.find(token) { // and there is an end token in the line
                    let (ss, after_token_end) = sst.split_at(end_token_pos); // the scope selector text ends at the end token
                    sst = &ss;
                    only_whitespace_after_token_end = after_token_end.trim_right().is_empty();
                }
            }
            return Some(AssertionRange {
                begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                scope_selector_text: sst,
                is_pure_assertion_line: before_token_start.trim_left().is_empty() && only_whitespace_after_token_end, // if only whitespace surrounds the test tokens on the line, then it is a pure assertion line
            });
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    // format the scope selector to include a space at the beginning, because, currently, ScopeSelector expects excludes to begin with " -"
    // and they are sometimes in the syntax test as ^^^-comment, for example
    let selector = ScopeSelectors::from_str(&format!(" {}", &assertion.scope_selector_text)).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

/// If `parse_test_lines` is `false` then lines that only contain assertions are not parsed
fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    let header_line = line.clone();
    let search_result = SYNTAX_TEST_HEADER_PATTERN.captures(&header_line);
    let captures = try!(search_result.ok_or(SyntaxTestHeaderError::MalformedHeader));

    let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
    let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
    let syntax_file = captures.name("syntax_file").unwrap().as_str();

    // find the relevant syntax definition to parse the file with - case is important!
    println!("The test file references syntax definition file: {}", syntax_file);
    let syntax = try!(ss.find_syntax_by_path(syntax_file).ok_or(SyntaxTestHeaderError::SyntaxDefinitionNotFound));

    // iterate over the lines of the file, testing them
    let mut state = ParseState::new(syntax);
    let mut stack = ScopeStack::new();

    let mut current_line_number = 1;
    let mut test_against_line_number = 1;
    let mut scopes_on_line_being_tested = Vec::new();
    let mut previous_non_assertion_line = line.to_string();

    let mut assertion_failures: usize = 0;
    let mut total_assertions: usize = 0;

    loop { // over lines of file, starting with the header line
        let mut line_only_has_assertion = false;
        let mut line_has_assertion = false;
        if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
            let result = process_assertions(&assertion, &scopes_on_line_being_tested);
            total_assertions += &assertion.end_char - &assertion.begin_char;
            for failure in result.iter().filter(|r|!r.success) {
                let chars = &previous_non_assertion_line[failure.column_begin..failure.column_end];
                println!("  Assertion selector {:?} \
                    from line {:?} failed against line {:?}, column range {:?}-{:?} \
                    (with text {:?}) \
                    has scope {:?}",
                    assertion.scope_selector_text.trim(),
                    current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                    chars,
                    scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                );
                assertion_failures += failure.column_end - failure.column_begin;
            }
            line_only_has_assertion = assertion.is_pure_assertion_line;
            line_has_assertion = true;
        }
        if !line_only_has_assertion || parse_test_lines {
            if !line_has_assertion { // ST seems to ignore lines that have assertions when calculating which line the assertion tests against
                scopes_on_line_being_tested.clear();
                test_against_line_number = current_line_number;
                previous_non_assertion_line = line.to_string();
            }
            let ops = state.parse_line(&line);
            let mut col: usize = 0;
            for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                stack.apply(op);
                if s.is_empty() { // in this case we don't care about blank tokens
                    continue;
                }
                if !line_has_assertion {
                    // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                    let len = s.chars().count();
                    scopes_on_line_being_tested.push(
                        ScopedText {
                            char_start: col,
                            text_len: len,
                            scope: stack.as_slice().to_vec()
                        }
                    );
                    // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                    col += len;
                }
            }
        }

        line.clear();
        current_line_number += 1;
        if reader.read_line(&mut line).unwrap() == 0 {
            break;
        }
        line = line.replace("\r", &"");
    }
    if assertion_failures > 0 {
        Ok(SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions))
    } else {
        Ok(SyntaxTestFileResult::Success(total_assertions))
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path);
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<commit_msg>syntax tests example: account for test assertion lines without selectors<commit_after>//! An example of using syntect for testing syntax definitions.
//! Basically exactly the same as what Sublime Text can do,
//! but without needing ST installed
extern crate syntect;
extern crate walkdir;
#[macro_use]
extern crate lazy_static;
extern crate regex;
//extern crate onig;
use syntect::parsing::{SyntaxSet, ParseState, ScopeStack, Scope};
use syntect::highlighting::ScopeSelectors;
use syntect::easy::{ScopeRegionIterator};

use std::path::Path;
use std::io::{BufRead, BufReader};
use std::fs::File;
use std::cmp::{min, max};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};
use std::str::FromStr;
use regex::Regex;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestHeaderError {
    MalformedHeader,
    SyntaxDefinitionNotFound,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxTestFileResult {
    FailedAssertions(usize, usize),
    Success(usize),
}

lazy_static! {
    pub static ref SYNTAX_TEST_HEADER_PATTERN: Regex = Regex::new(r#"(?xm)
            ^(?P<testtoken_start>\s*\S+)
            \s+SYNTAX\sTEST\s+
            "(?P<syntax_file>[^"]+)"
            \s*(?P<testtoken_end>\S+)?$
        "#).unwrap();
    pub static ref SYNTAX_TEST_ASSERTION_PATTERN: Regex = Regex::new(r#"(?xm)
        \s*(?:
            (?P<begin_of_token><-)|(?P<range>\^+)
        )(.*)$"#).unwrap();
}

#[derive(Debug)]
struct AssertionRange<'a> {
    begin_char: usize,
    end_char: usize,
    scope_selector_text: &'a str,
    is_pure_assertion_line: bool,
}

#[derive(Debug)]
struct ScopedText {
    scope: Vec<Scope>,
    char_start: usize,
    text_len: usize,
}

#[derive(Debug)]
struct RangeTestResult {
    column_begin: usize,
    column_end: usize,
    success: bool,
}

fn get_line_assertion_details<'a>(testtoken_start: &str, testtoken_end: Option<&str>, line: &'a str) -> Option<AssertionRange<'a>> {
    // if the test start token specified in the test file's header is on the line
    if let Some(index) = line.find(testtoken_start) {
        let (before_token_start, token_and_rest_of_line) = line.split_at(index);

        if let Some(captures) = SYNTAX_TEST_ASSERTION_PATTERN.captures(&token_and_rest_of_line[testtoken_start.len()..]) {
            let mut sst = captures.get(3).unwrap().as_str(); // get the scope selector text
            let mut only_whitespace_after_token_end = true;

            if let Some(token) = testtoken_end { // if there is an end token defined in the test file header
                if let Some(end_token_pos) = sst.find(token) { // and there is an end token in the line
                    let (ss, after_token_end) = sst.split_at(end_token_pos); // the scope selector text ends at the end token
                    sst = &ss;
                    only_whitespace_after_token_end = after_token_end.trim_right().is_empty();
                }
            }
            return Some(AssertionRange {
                begin_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().start() } else { 0 },
                end_char: index + if captures.get(2).is_some() { testtoken_start.len() + captures.get(2).unwrap().end() } else { 1 },
                scope_selector_text: sst,
                is_pure_assertion_line: before_token_start.trim_left().is_empty() && only_whitespace_after_token_end, // if only whitespace surrounds the test tokens on the line, then it is a pure assertion line
            });
        }
    }
    None
}

fn process_assertions(assertion: &AssertionRange, test_against_line_scopes: &Vec<ScopedText>) -> Vec<RangeTestResult> {
    // format the scope selector to include a space at the beginning, because, currently, ScopeSelector expects excludes to begin with " -"
    // and they are sometimes in the syntax test as ^^^-comment, for example
    let selector = ScopeSelectors::from_str(&format!(" {}", &assertion.scope_selector_text)).unwrap();
    // find the scope at the specified start column, and start matching the selector through the rest of the tokens on the line from there until the end column is reached
    let mut results = Vec::new();
    for scoped_text in test_against_line_scopes.iter().skip_while(|s|s.char_start + s.text_len <= assertion.begin_char).take_while(|s|s.char_start < assertion.end_char) {
        let match_value = selector.does_match(scoped_text.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(scoped_text.char_start, assertion.begin_char),
            column_end: min(scoped_text.char_start + scoped_text.text_len, assertion.end_char),
            success: match_value.is_some()
        };
        results.push(result);
    }
    // don't ignore assertions after the newline, they should be treated as though they are asserting against the newline
    let last = test_against_line_scopes.last().unwrap();
    if last.char_start + last.text_len < assertion.end_char {
        let match_value = selector.does_match(last.scope.as_slice());
        let result = RangeTestResult {
            column_begin: max(last.char_start + last.text_len, assertion.begin_char),
            column_end: assertion.end_char,
            success: match_value.is_some()
        };
        results.push(result);
    }
    results
}

/// If `parse_test_lines` is `false` then lines that only contain assertions are not parsed
fn test_file(ss: &SyntaxSet, path: &Path, parse_test_lines: bool) -> Result<SyntaxTestFileResult, SyntaxTestHeaderError> {
    let f = File::open(path).unwrap();
    let mut reader = BufReader::new(f);
    let mut line = String::new();

    // read the first line from the file - if we have reached EOF already, it's an invalid file
    if reader.read_line(&mut line).unwrap() == 0 {
        return Err(SyntaxTestHeaderError::MalformedHeader);
    }

    line = line.replace("\r", &"");

    // parse the syntax test header in the first line of the file
    let header_line = line.clone();
    let search_result = SYNTAX_TEST_HEADER_PATTERN.captures(&header_line);
    let captures = try!(search_result.ok_or(SyntaxTestHeaderError::MalformedHeader));

    let testtoken_start = captures.name("testtoken_start").unwrap().as_str();
    let testtoken_end = captures.name("testtoken_end").map_or(None, |c|Some(c.as_str()));
    let syntax_file = captures.name("syntax_file").unwrap().as_str();

    // find the relevant syntax definition to parse the file with - case is important!
    println!("The test file references syntax definition file: {}", syntax_file);
    let syntax = try!(ss.find_syntax_by_path(syntax_file).ok_or(SyntaxTestHeaderError::SyntaxDefinitionNotFound));

    // iterate over the lines of the file, testing them
    let mut state = ParseState::new(syntax);
    let mut stack = ScopeStack::new();

    let mut current_line_number = 1;
    let mut test_against_line_number = 1;
    let mut scopes_on_line_being_tested = Vec::new();
    let mut previous_non_assertion_line = line.to_string();

    let mut assertion_failures: usize = 0;
    let mut total_assertions: usize = 0;

    loop { // over lines of file, starting with the header line
        let mut line_only_has_assertion = false;
        let mut line_has_assertion = false;
        if let Some(assertion) = get_line_assertion_details(testtoken_start, testtoken_end, &line) {
            let result = process_assertions(&assertion, &scopes_on_line_being_tested);
            total_assertions += &assertion.end_char - &assertion.begin_char;
            for failure in result.iter().filter(|r|!r.success) {
                let chars = &previous_non_assertion_line[failure.column_begin..failure.column_end];
                println!("  Assertion selector {:?} \
                    from line {:?} failed against line {:?}, column range {:?}-{:?} \
                    (with text {:?}) \
                    has scope {:?}",
                    assertion.scope_selector_text.trim(),
                    current_line_number, test_against_line_number, failure.column_begin, failure.column_end,
                    chars,
                    scopes_on_line_being_tested.iter().skip_while(|s|s.char_start + s.text_len <= failure.column_begin).next().unwrap_or(scopes_on_line_being_tested.last().unwrap()).scope
                );
                assertion_failures += failure.column_end - failure.column_begin;
            }
            line_only_has_assertion = assertion.is_pure_assertion_line;
            line_has_assertion = true;
        }
        if !line_only_has_assertion || parse_test_lines {
            if !line_has_assertion { // ST seems to ignore lines that have assertions when calculating which line the assertion tests against
                scopes_on_line_being_tested.clear();
                test_against_line_number = current_line_number;
                previous_non_assertion_line = line.to_string();
            }
            let ops = state.parse_line(&line);
            let mut col: usize = 0;
            for (s, op) in ScopeRegionIterator::new(&ops, &line) {
                stack.apply(op);
                if s.is_empty() { // in this case we don't care about blank tokens
                    continue;
                }
                if !line_has_assertion {
                    // if the line has no assertions on it, remember the scopes on the line so we can test against them later
                    let len = s.chars().count();
                    scopes_on_line_being_tested.push(
                        ScopedText {
                            char_start: col,
                            text_len: len,
                            scope: stack.as_slice().to_vec()
                        }
                    );
                    // TODO: warn when there are duplicate adjacent (non-meta?) scopes, as it is almost always undesired
                    col += len;
                }
            }
        }

        line.clear();
        current_line_number += 1;
        if reader.read_line(&mut line).unwrap() == 0 {
            break;
        }
        line = line.replace("\r", &"");
    }
    if assertion_failures > 0 {
        Ok(SyntaxTestFileResult::FailedAssertions(assertion_failures, total_assertions))
    } else {
        Ok(SyntaxTestFileResult::Success(total_assertions))
    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();
    let tests_path = if args.len() < 2 {
        "."
    } else {
        &args[1]
    };
    let syntaxes_path = if args.len() == 3 {
        &args[2]
    } else {
        ""
    };

    // load the syntaxes from disk if told to
    // (as opposed to from the binary dumps)
    // this helps to ensure that a recompile isn't needed
    // when using this for syntax development
    let mut ss = if syntaxes_path.is_empty() {
        SyntaxSet::load_defaults_newlines() // note we load the version with newlines
    } else {
        SyntaxSet::new()
    };
    if !syntaxes_path.is_empty() {
        println!("loading syntax definitions from {}", syntaxes_path);
        ss.load_syntaxes(&syntaxes_path, true).unwrap(); // note that we load the version with newlines
        ss.link_syntaxes();
    }

    let exit_code = recursive_walk(&ss, &tests_path);
    println!("exiting with code {}", exit_code);
    std::process::exit(exit_code);

}

fn recursive_walk(ss: &SyntaxSet, path: &str) -> i32 {
    let mut exit_code: i32 = 0; // exit with code 0 by default, if all tests pass
    let walker = WalkDir::new(path).into_iter();
    for entry in walker.filter_entry(|e|e.file_type().is_dir() || is_a_syntax_test_file(e)) {
        let entry = entry.unwrap();
        if entry.file_type().is_file() {
            println!("Testing file {}", entry.path().display());
            let result = test_file(&ss, entry.path(), true);
            println!("{:?}", result);
            if exit_code != 2 { // leave exit code 2 if there was an error
                if let Err(_) = result { // set exit code 2 if there was an error
                    exit_code = 2;
                } else if let Ok(ok) = result {
                    if let SyntaxTestFileResult::FailedAssertions(_, _) = ok {
                        exit_code = 1; // otherwise, if there were failures, exit with code 1
                    }
                }
            }
        }
    }
    exit_code
}

fn is_a_syntax_test_file(entry: &DirEntry) -> bool {
    entry.file_name()
         .to_str()
         .map(|s| s.starts_with("syntax_test_"))
         .unwrap_or(false)
}
<|endoftext|>