<commit_before>use std::kinds::marker::ContravariantLifetime;
use std::mem;

use runtime::Object;
use {class, Id, IdVector, IntoIdVector, Owned, Ownership, Shared, ShareId};
use super::{INSCopying, INSObject};

pub struct NSRange {
	pub location: uint,
	pub length: uint,
}

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
	marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
	pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject] as *mut T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}
}

pub trait INSArray<T: INSObject, O: Ownership> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at(&self, index: uint) -> &T {
		unsafe {
			let obj = msg_send![self objectAtIndex:index] as *const T;
			&*obj
		}
	}

	fn first_object(&self) -> Option<&T> {
		unsafe {
			let obj = msg_send![self firstObject] as *const T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}

	fn last_object(&self) -> Option<&T> {
		unsafe {
			let obj = msg_send![self lastObject] as *const T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}

	fn object_enumerator(&self) -> NSEnumerator<T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *mut Self)
	}

	fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
		let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
		let range = NSRange { location: start, length: len };
		unsafe {
			msg_send![self getObjects:vec.as_ptr() range:range];
			mem::transmute(vec)
		}
	}

	fn to_vec(&self) -> Vec<&T> {
		self.objects_in_range(0, self.count())
	}

	fn into_vec(array: Id<Self>) -> Vec<Id<T, O>> {
		let vec = array.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

pub trait INSOwnedArray<T: INSObject> : INSArray<T, Owned> {
	fn mut_object_at(&mut self, index: uint) -> &mut T {
		unsafe {
			let result = msg_send![self objectAtIndex:index] as *mut T;
			&mut *result
		}
	}
}

pub trait INSSharedArray<T: INSObject> : INSArray<T, Shared> {
	fn shared_object_at(&self, index: uint) -> ShareId<T> {
		let obj = self.object_at(index);
		unsafe {
			Id::from_ptr(obj as *const _ as *mut T)
		}
	}

	fn from_slice(slice: &[ShareId<T>]) -> Id<Self> {
		let refs = slice.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn to_shared_vec(&self) -> Vec<ShareId<T>> {
		let vec = self.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T, Owned> for NSArray<T> { }

impl<T: INSObject> INSOwnedArray<T> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

impl<T: INSObject> Index<uint, T> for NSArray<T> {
	fn index(&self, index: &uint) -> &T {
		self.object_at(*index)
	}
}

pub trait INSMutableArray<T: INSObject, O: Ownership> : INSArray<T, O> {
	fn add_object(&mut self, obj: Id<T, O>) {
		unsafe {
			msg_send![self addObject:obj];
		}
	}

	fn insert_object_at(&mut self, index: uint, obj: Id<T, O>) {
		unsafe {
			msg_send![self insertObject:obj atIndex:index];
		}
	}

	fn remove_object_at(&mut self, index: uint) {
		unsafe {
			msg_send![self removeObjectAtIndex:index];
		}
	}
}

object_struct!(NSMutableArray<T>)

impl<T: INSObject> INSArray<T, Owned> for NSMutableArray<T> { }

impl<T: INSObject> INSOwnedArray<T> for NSMutableArray<T> { }

impl<T: INSObject> INSMutableArray<T, Owned> for NSMutableArray<T> { }

impl<T: INSObject> Collection for NSMutableArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

#[cfg(test)]
mod tests {
	use {Id};
	use foundation::{INSObject, NSObject};
	use super::{INSArray, INSMutableArray, NSArray, NSMutableArray};

	fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
		let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
		INSArray::from_vec(vec)
	}

	#[test]
	fn test_count() {
		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.count() == 0);

		let array = sample_array(4);
		assert!(array.count() == 4);
	}

	#[test]
	fn test_object_at() {
		let array = sample_array(4);
		assert!(array.object_at(0) != array.object_at(3));
		assert!(array.first_object().unwrap() == array.object_at(0));
		assert!(array.last_object().unwrap() == array.object_at(3));

		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.first_object().is_none());
		assert!(empty_array.last_object().is_none());
	}

	#[test]
	fn test_object_enumerator() {
		let array = sample_array(4);

		assert!(array.object_enumerator().count() == 4);
		assert!(array.object_enumerator()
		             .enumerate()
		             .all(|(i, obj)| obj == array.object_at(i)));
	}

	#[test]
	fn test_objects_in_range() {
		let array = sample_array(4);

		let middle_objs = array.objects_in_range(1, 2);
		assert!(middle_objs.len() == 2);
		assert!(*middle_objs.get(0) == array.object_at(1));
		assert!(*middle_objs.get(1) == array.object_at(2));

		let empty_objs = array.objects_in_range(1, 0);
		assert!(empty_objs.len() == 0);

		let all_objs = array.objects_in_range(0, 4);
		assert!(all_objs.len() == 4);
	}

	#[test]
	fn test_into_vec() {
		let array = sample_array(4);

		let vec = INSArray::into_vec(array);
		assert!(vec.len() == 4);
	}

	#[test]
	fn test_add_object() {
		let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
		let obj: Id<NSObject> = INSObject::new();
		array.deref_mut().add_object(obj);

		assert!(array.len() == 1);
		assert!(array.object_at(0) == array.object_at(0));
	}
}
<commit_msg>Added replace and remove methods for NSArray.<commit_after>use std::kinds::marker::ContravariantLifetime;
use std::mem;

use runtime::Object;
use {class, Id, IdVector, IntoIdVector, Owned, Ownership, Shared, ShareId};
use super::{INSCopying, INSObject};

pub struct NSRange {
	pub location: uint,
	pub length: uint,
}

pub struct NSEnumerator<'a, T> {
	id: Id<Object>,
	marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
	pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
		NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
	}
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
	fn next(&mut self) -> Option<&'a T> {
		unsafe {
			let obj = msg_send![self.id nextObject] as *mut T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}
}

pub trait INSArray<T: INSObject, O: Ownership> : INSObject {
	fn count(&self) -> uint {
		let result = unsafe {
			msg_send![self count]
		};
		result as uint
	}

	fn object_at(&self, index: uint) -> &T {
		unsafe {
			let obj = msg_send![self objectAtIndex:index] as *const T;
			&*obj
		}
	}

	fn first_object(&self) -> Option<&T> {
		unsafe {
			let obj = msg_send![self firstObject] as *const T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}

	fn last_object(&self) -> Option<&T> {
		unsafe {
			let obj = msg_send![self lastObject] as *const T;
			if obj.is_null() {
				None
			} else {
				Some(&*obj)
			}
		}
	}

	fn object_enumerator(&self) -> NSEnumerator<T> {
		unsafe {
			let result = msg_send![self objectEnumerator];
			NSEnumerator::from_ptr(result)
		}
	}

	unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
		let cls = class::<Self>();
		let obj = msg_send![cls alloc];
		let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
		Id::from_retained_ptr(obj as *mut Self)
	}

	fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self> {
		let refs = vec.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
		let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
		let range = NSRange { location: start, length: len };
		unsafe {
			msg_send![self getObjects:vec.as_ptr() range:range];
			mem::transmute(vec)
		}
	}

	fn to_vec(&self) -> Vec<&T> {
		self.objects_in_range(0, self.count())
	}

	fn into_vec(array: Id<Self>) -> Vec<Id<T, O>> {
		let vec = array.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

pub trait INSOwnedArray<T: INSObject> : INSArray<T, Owned> {
	fn mut_object_at(&mut self, index: uint) -> &mut T {
		unsafe {
			let result = msg_send![self objectAtIndex:index] as *mut T;
			&mut *result
		}
	}
}

pub trait INSSharedArray<T: INSObject> : INSArray<T, Shared> {
	fn shared_object_at(&self, index: uint) -> ShareId<T> {
		let obj = self.object_at(index);
		unsafe {
			Id::from_ptr(obj as *const _ as *mut T)
		}
	}

	fn from_slice(slice: &[ShareId<T>]) -> Id<Self> {
		let refs = slice.as_refs_slice();
		unsafe {
			INSArray::from_refs(refs)
		}
	}

	fn to_shared_vec(&self) -> Vec<ShareId<T>> {
		let vec = self.to_vec();
		unsafe {
			vec.into_id_vec()
		}
	}
}

object_struct!(NSArray<T>)

impl<T: INSObject> INSArray<T, Owned> for NSArray<T> { }

impl<T: INSObject> INSOwnedArray<T> for NSArray<T> { }

impl<T> INSCopying<NSArray<T>> for NSArray<T> { }

impl<T: INSObject> Collection for NSArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

impl<T: INSObject> Index<uint, T> for NSArray<T> {
	fn index(&self, index: &uint) -> &T {
		self.object_at(*index)
	}
}

pub trait INSMutableArray<T: INSObject, O: Ownership> : INSArray<T, O> {
	fn add_object(&mut self, obj: Id<T, O>) {
		unsafe {
			msg_send![self addObject:obj];
		}
	}

	fn insert_object_at(&mut self, index: uint, obj: Id<T, O>) {
		unsafe {
			msg_send![self insertObject:obj atIndex:index];
		}
	}

	fn replace_object_at(&mut self, index: uint, obj: Id<T, O>) -> Id<T, O> {
		let old_obj = unsafe {
			let obj = self.object_at(index);
			Id::from_ptr(obj as *const _ as *mut T)
		};
		unsafe {
			msg_send![self replaceObjectAtIndex:index withObject:obj];
		}
		old_obj
	}

	fn remove_object_at(&mut self, index: uint) -> Id<T, O> {
		let obj = unsafe {
			let obj = self.object_at(index);
			Id::from_ptr(obj as *const _ as *mut T)
		};
		unsafe {
			msg_send![self removeObjectAtIndex:index];
		}
		obj
	}

	fn remove_last_object(&mut self) -> Id<T, O> {
		let obj = self.last_object().map(|obj| unsafe {
			Id::from_ptr(obj as *const _ as *mut T)
		});
		unsafe {
			msg_send![self removeLastObject];
		}
		// removeLastObject would have failed if the array is empty,
		// so we know this won't be None
		obj.unwrap()
	}

	fn remove_all_objects(&mut self) {
		unsafe {
			msg_send![self removeAllObjects];
		}
	}
}

object_struct!(NSMutableArray<T>)

impl<T: INSObject> INSArray<T, Owned> for NSMutableArray<T> { }

impl<T: INSObject> INSOwnedArray<T> for NSMutableArray<T> { }

impl<T: INSObject> INSMutableArray<T, Owned> for NSMutableArray<T> { }

impl<T: INSObject> Collection for NSMutableArray<T> {
	fn len(&self) -> uint {
		self.count()
	}
}

impl<T: INSObject> Mutable for NSMutableArray<T> {
	fn clear(&mut self) {
		self.remove_all_objects();
	}
}

impl<T: INSObject> MutableSeq<Id<T>> for NSMutableArray<T> {
	fn push(&mut self, value: Id<T>) {
		self.add_object(value);
	}

	fn pop(&mut self) -> Option<Id<T>> {
		if self.is_empty() {
			None
		} else {
			Some(self.remove_last_object())
		}
	}
}

#[cfg(test)]
mod tests {
	use {Id};
	use foundation::{INSObject, NSObject};
	use super::{INSArray, INSMutableArray, NSArray, NSMutableArray};

	fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
		let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
		INSArray::from_vec(vec)
	}

	#[test]
	fn test_count() {
		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.count() == 0);

		let array = sample_array(4);
		assert!(array.count() == 4);
	}

	#[test]
	fn test_object_at() {
		let array = sample_array(4);
		assert!(array.object_at(0) != array.object_at(3));
		assert!(array.first_object().unwrap() == array.object_at(0));
		assert!(array.last_object().unwrap() == array.object_at(3));

		let empty_array: Id<NSArray<NSObject>> = INSObject::new();
		assert!(empty_array.first_object().is_none());
		assert!(empty_array.last_object().is_none());
	}

	#[test]
	fn test_object_enumerator() {
		let array = sample_array(4);

		assert!(array.object_enumerator().count() == 4);
		assert!(array.object_enumerator()
		             .enumerate()
		             .all(|(i, obj)| obj == array.object_at(i)));
	}

	#[test]
	fn test_objects_in_range() {
		let array = sample_array(4);

		let middle_objs = array.objects_in_range(1, 2);
		assert!(middle_objs.len() == 2);
		assert!(*middle_objs.get(0) == array.object_at(1));
		assert!(*middle_objs.get(1) == array.object_at(2));

		let empty_objs = array.objects_in_range(1, 0);
		assert!(empty_objs.len() == 0);

		let all_objs = array.objects_in_range(0, 4);
		assert!(all_objs.len() == 4);
	}

	#[test]
	fn test_into_vec() {
		let array = sample_array(4);

		let vec = INSArray::into_vec(array);
		assert!(vec.len() == 4);
	}

	#[test]
	fn test_add_object() {
		let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
		let obj: Id<NSObject> = INSObject::new();
		array.deref_mut().add_object(obj);

		assert!(array.len() == 1);
		assert!(array.object_at(0) == array.object_at(0));

		let obj: Id<NSObject> = INSObject::new();
		array.deref_mut().insert_object_at(0, obj);
		assert!(array.len() == 2);
	}

	#[test]
	fn test_replace_object() {
		let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
		let obj: Id<NSObject> = INSObject::new();
		array.deref_mut().add_object(obj);

		let obj: Id<NSObject> = INSObject::new();
		let old_obj = array.deref_mut().replace_object_at(0, obj);
		assert!(&*old_obj != array.object_at(0));
	}

	#[test]
	fn test_remove_object() {
		let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
		for _ in range(0u, 4) {
			let obj: Id<NSObject> = INSObject::new();
			array.deref_mut().add_object(obj);
		}

		array.deref_mut().remove_object_at(1);
		assert!(array.len() == 3);

		array.deref_mut().remove_last_object();
		assert!(array.len() == 2);

		array.deref_mut().remove_all_objects();
		assert!(array.is_empty());
	}
}
<|endoftext|>


<commit_before>use std::iter::FromIterator;

use mesh::{HashIndexer, IndexPrimitives, IntoPoints, Primitive};
use super::Index;
use super::pipeline::Vertex;

pub struct MeshBuffer {
    vertices: Vec<Vertex>,
    indices: Vec<Index>,
}

impl MeshBuffer {
    pub fn new() -> Self {
        MeshBuffer::default()
    }

    pub fn extend<V, I>(&mut self, vertices: V, indices: I)
    where
        V: IntoIterator<Item = Vertex>,
        I: IntoIterator<Item = Index>,
    {
        self.vertices.extend(vertices);
        self.indices.extend(indices);
    }

    pub fn append(&mut self, buffer: &mut Self) {
        let offset = self.vertices.len();
        self.vertices.append(&mut buffer.vertices);
        self.indices.extend(
            buffer
                .indices
                .drain(..)
                .map(|index| index + offset as Index),
        );
    }

    pub fn vertices(&self) -> &[Vertex] {
        self.vertices.as_slice()
    }

    pub fn indices(&self) -> &[Index] {
        self.indices.as_slice()
    }
}

impl Default for MeshBuffer {
    fn default() -> Self {
        MeshBuffer {
            vertices: vec![],
            indices: vec![],
        }
    }
}

// This allows for streams of polygons containing `Vertex`s to be `collect`ed
// into a `MeshBuffer`. This is a bit dubious; the high cost and complexity is
// hidden behind an innocuous `collect` invocation.
impl<T> FromIterator<T> for MeshBuffer
where
    T: IntoPoints + Primitive<Point = Vertex>,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut buffer = MeshBuffer::new();
        let (indeces, points) = input.into_iter().index_primitives(HashIndexer::default());
        buffer.extend(points, indeces.into_iter().map(|index| index as Index));
        buffer
    }
}

pub trait ToMeshBuffer {
    fn to_mesh_buffer(&self) -> MeshBuffer;
}
<commit_msg>Allow `collect` for points convertible to `Vertex`.<commit_after>use std::hash::Hash;
use std::iter::FromIterator;

use mesh::{HashIndexer, IndexPrimitives, IntoPoints, Primitive};
use super::Index;
use super::pipeline::Vertex;

pub struct MeshBuffer {
    vertices: Vec<Vertex>,
    indices: Vec<Index>,
}

impl MeshBuffer {
    pub fn new() -> Self {
        MeshBuffer::default()
    }

    pub fn extend<V, I>(&mut self, vertices: V, indices: I)
    where
        V: IntoIterator<Item = Vertex>,
        I: IntoIterator<Item = Index>,
    {
        self.vertices.extend(vertices);
        self.indices.extend(indices);
    }

    pub fn append(&mut self, buffer: &mut Self) {
        let offset = self.vertices.len();
        self.vertices.append(&mut buffer.vertices);
        self.indices.extend(
            buffer
                .indices
                .drain(..)
                .map(|index| index + offset as Index),
        );
    }

    pub fn vertices(&self) -> &[Vertex] {
        self.vertices.as_slice()
    }

    pub fn indices(&self) -> &[Index] {
        self.indices.as_slice()
    }
}

impl Default for MeshBuffer {
    fn default() -> Self {
        MeshBuffer {
            vertices: vec![],
            indices: vec![],
        }
    }
}

// This allows for streams of polygons containing `Vertex`s to be `collect`ed
// into a `MeshBuffer`. This is a bit dubious; the high cost and complexity is
// hidden behind an innocuous `collect` invocation.
impl<T> FromIterator<T> for MeshBuffer
where
    T: IntoPoints + Primitive,
    T::Point: Eq + Hash + Into<Vertex>,
{
    fn from_iter<I>(input: I) -> Self
    where
        I: IntoIterator<Item = T>,
    {
        let mut buffer = MeshBuffer::new();
        let (indeces, points) = input.into_iter().index_primitives(HashIndexer::default());
        buffer.extend(
            points.into_iter().map(|point| point.into()),
            indeces.into_iter().map(|index| index as Index),
        );
        buffer
    }
}

pub trait ToMeshBuffer {
    fn to_mesh_buffer(&self) -> MeshBuffer;
}
<|endoftext|>


<commit_before>use futures::executor::block_on;
use futures::future::{Future, FutureExt};
use futures::stream::{self, StreamExt, TryStreamExt};
use futures::io::{AsyncBufReadExt, Cursor};
use futures::task::Poll;
use futures_test::io::AsyncReadTestExt;
use futures_test::task::noop_context;

macro_rules! block_on_next {
    ($expr:expr) => {
        block_on($expr.next()).unwrap().unwrap()
    };
}

#[test]
fn lines() {
    let buf = Cursor::new(&b"12\r"[..]);
    let mut s = buf.lines();
    assert_eq!(block_on_next!(s), "12\r".to_string());
    assert!(block_on(s.next()).is_none());

    let buf = Cursor::new(&b"12\r\n\n"[..]);
    let mut s = buf.lines();
    assert_eq!(block_on_next!(s), "12".to_string());
    assert_eq!(block_on_next!(s), "".to_string());
    assert!(block_on(s.next()).is_none());
}

fn run<F: Future + Unpin>(mut f: F) -> F::Output {
    let mut cx = noop_context();
    loop {
        if let Poll::Ready(x) = f.poll_unpin(&mut cx) {
            return x;
        }
    }
}

macro_rules! run_next {
    ($expr:expr) => {
        run($expr.next()).unwrap().unwrap()
    };
}

#[test]
fn maybe_pending() {
    let buf = stream::iter(vec![&b"12"[..], &b"\r"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut s = buf.lines();
    assert_eq!(run_next!(s), "12\r".to_string());
    assert!(run(s.next()).is_none());

    let buf = stream::iter(vec![&b"12"[..], &b"\r\n"[..], &b"\n"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut s = buf.lines();
    assert_eq!(run_next!(s), "12".to_string());
    assert_eq!(run_next!(s), "".to_string());
    assert!(run(s.next()).is_none());
}
<commit_msg>futures: tests/io_lines.rs : Avoid compile failures w/o features<commit_after>#[cfg(any(feature = "std", feature = "executor"))]
mod util {
    use futures::future::Future;

    pub fn run<F: Future + Unpin>(mut f: F) -> F::Output {
        use futures_test::task::noop_context;
        use futures::task::Poll;
        use futures::future::FutureExt;

        let mut cx = noop_context();
        loop {
            if let Poll::Ready(x) = f.poll_unpin(&mut cx) {
                return x;
            }
        }
    }
}

#[cfg(feature = "executor")]
#[test]
fn lines() {
    use futures::executor::block_on;
    use futures::stream::StreamExt;
    use futures::io::{AsyncBufReadExt, Cursor};

    macro_rules! block_on_next {
        ($expr:expr) => {
            block_on($expr.next()).unwrap().unwrap()
        };
    }

    let buf = Cursor::new(&b"12\r"[..]);
    let mut s = buf.lines();
    assert_eq!(block_on_next!(s), "12\r".to_string());
    assert!(block_on(s.next()).is_none());

    let buf = Cursor::new(&b"12\r\n\n"[..]);
    let mut s = buf.lines();
    assert_eq!(block_on_next!(s), "12".to_string());
    assert_eq!(block_on_next!(s), "".to_string());
    assert!(block_on(s.next()).is_none());
}

#[cfg(feature = "std")]
#[test]
fn maybe_pending() {
    use futures::stream::{self, StreamExt, TryStreamExt};
    use futures::io::AsyncBufReadExt;
    use futures_test::io::AsyncReadTestExt;

    use util::run;

    macro_rules! run_next {
        ($expr:expr) => {
            run($expr.next()).unwrap().unwrap()
        };
    }

    let buf = stream::iter(vec![&b"12"[..], &b"\r"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut s = buf.lines();
    assert_eq!(run_next!(s), "12\r".to_string());
    assert!(run(s.next()).is_none());

    let buf = stream::iter(vec![&b"12"[..], &b"\r\n"[..], &b"\n"[..]])
        .map(Ok)
        .into_async_read()
        .interleave_pending();
    let mut s = buf.lines();
    assert_eq!(run_next!(s), "12".to_string());
    assert_eq!(run_next!(s), "".to_string());
    assert!(run(s.next()).is_none());
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use about_loader;
use mime_classifier::MimeClassifier;
use mime_guess::guess_mime_type;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use net_traits::{LoadConsumer, LoadData, LoadOrigin, Metadata, NetworkError};
use net_traits::ProgressMsg::{Done, Payload};
use resource_thread::{CancellationListener, ProgressSender};
use resource_thread::{send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::sync::Arc;
use url::Url;
use util::thread::spawn_named;

static READ_SIZE: usize = 8192;

enum ReadStatus {
    Partial(Vec<u8>),
    EOF,
}

enum LoadResult {
    Cancelled,
    Finished,
}

struct FileLoadOrigin;
impl LoadOrigin for FileLoadOrigin {
    fn referrer_url(&self) -> Option<Url> {
        None
    }
    fn referrer_policy(&self) -> Option<ReferrerPolicy> {
        None
    }
    fn pipeline_id(&self) -> Option<PipelineId> {
        None
    }
}

fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
    let mut buf = vec![0; READ_SIZE];
    match reader.read(&mut buf) {
        Ok(0) => Ok(ReadStatus::EOF),
        Ok(n) => {
            buf.truncate(n);
            Ok(ReadStatus::Partial(buf))
        }
        Err(e) => Err(e.description().to_owned()),
    }
}

fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener)
            -> Result<LoadResult, String> {
    while !cancel_listener.is_cancelled() {
        match try!(read_block(reader)) {
            ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(),
            ReadStatus::EOF => return Ok(LoadResult::Finished),
        }
    }
    let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
    Ok(LoadResult::Cancelled)
}

fn get_progress_chan(load_data: LoadData, file_path: PathBuf,
                     senders: LoadConsumer, classifier: Arc<MimeClassifier>, buf: &[u8])
                     -> Result<ProgressSender, ()> {
    let mut metadata = Metadata::default(load_data.url);
    let mime_type = guess_mime_type(file_path.as_path());
    metadata.set_content_type(Some(&mime_type));
    return start_sending_sniffed_opt(senders, metadata, classifier, buf, load_data.context);
}

pub fn factory(load_data: LoadData,
               senders: LoadConsumer,
               classifier: Arc<MimeClassifier>,
               cancel_listener: CancellationListener) {
    assert!(load_data.url.scheme() == "file");
    spawn_named("file_loader".to_owned(), move || {
        let file_path = match load_data.url.to_file_path() {
            Ok(file_path) => file_path,
            Err(_) => {
                send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders);
                return;
            },
        };
        let mut file = File::open(&file_path);
        let reader = match file {
            Ok(ref mut reader) => reader,
            Err(_) => {
                // this should be one of the three errors listed in
                // http://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
                // but, we'll go for a "file not found!"
                let url = Url::parse("about:not-found").unwrap();
                let load_data_404 = LoadData::new(load_data.context, url, &FileLoadOrigin);
                about_loader::factory(load_data_404, senders, classifier, cancel_listener);
                return;
            }
        };

        if cancel_listener.is_cancelled() {
            if let Ok(progress_chan) = get_progress_chan(load_data, file_path,
                                                         senders, classifier, &[]) {
                let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
            }
            return;
        }

        match read_block(reader) {
            Ok(ReadStatus::Partial(buf)) => {
                let progress_chan = get_progress_chan(load_data, file_path,
                                                      senders, classifier, &buf).ok().unwrap();
                progress_chan.send(Payload(buf)).unwrap();
                let read_result = read_all(reader, &progress_chan, &cancel_listener);
                if let Ok(load_result) = read_result {
                    match load_result {
                        LoadResult::Cancelled => return,
                        LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(),
                    }
                }
            }
            Ok(ReadStatus::EOF) => {
                if let Ok(chan) = get_progress_chan(load_data, file_path,
                                                    senders, classifier, &[]) {
                    let _ = chan.send(Done(Ok(())));
                }
            }
            Err(e) => {
                send_error(load_data.url, NetworkError::Internal(e), senders);
            }
        }
    });
}
<commit_msg>Auto merge of #13304 - frewsxcv:ownership, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use about_loader;
use mime_classifier::MimeClassifier;
use mime_guess::guess_mime_type;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use net_traits::{LoadConsumer, LoadData, LoadOrigin, Metadata, NetworkError};
use net_traits::ProgressMsg::{Done, Payload};
use resource_thread::{CancellationListener, ProgressSender};
use resource_thread::{send_error, start_sending_sniffed_opt};
use std::borrow::ToOwned;
use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use url::Url;
use util::thread::spawn_named;

static READ_SIZE: usize = 8192;

enum ReadStatus {
    Partial(Vec<u8>),
    EOF,
}

enum LoadResult {
    Cancelled,
    Finished,
}

struct FileLoadOrigin;
impl LoadOrigin for FileLoadOrigin {
    fn referrer_url(&self) -> Option<Url> {
        None
    }
    fn referrer_policy(&self) -> Option<ReferrerPolicy> {
        None
    }
    fn pipeline_id(&self) -> Option<PipelineId> {
        None
    }
}

fn read_block(reader: &mut File) -> Result<ReadStatus, String> {
    let mut buf = vec![0; READ_SIZE];
    match reader.read(&mut buf) {
        Ok(0) => Ok(ReadStatus::EOF),
        Ok(n) => {
            buf.truncate(n);
            Ok(ReadStatus::Partial(buf))
        }
        Err(e) => Err(e.description().to_owned()),
    }
}

fn read_all(reader: &mut File, progress_chan: &ProgressSender, cancel_listener: &CancellationListener)
            -> Result<LoadResult, String> {
    while !cancel_listener.is_cancelled() {
        match try!(read_block(reader)) {
            ReadStatus::Partial(buf) => progress_chan.send(Payload(buf)).unwrap(),
            ReadStatus::EOF => return Ok(LoadResult::Finished),
        }
    }
    let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
    Ok(LoadResult::Cancelled)
}

fn get_progress_chan(load_data: LoadData, file_path: &Path,
                     senders: LoadConsumer, classifier: Arc<MimeClassifier>, buf: &[u8])
                     -> Result<ProgressSender, ()> {
    let mut metadata = Metadata::default(load_data.url);
    let mime_type = guess_mime_type(file_path);
    metadata.set_content_type(Some(&mime_type));
    return start_sending_sniffed_opt(senders, metadata, classifier, buf, load_data.context);
}

pub fn factory(load_data: LoadData,
               senders: LoadConsumer,
               classifier: Arc<MimeClassifier>,
               cancel_listener: CancellationListener) {
    assert!(load_data.url.scheme() == "file");
    spawn_named("file_loader".to_owned(), move || {
        let file_path = match load_data.url.to_file_path() {
            Ok(file_path) => file_path,
            Err(_) => {
                send_error(load_data.url, NetworkError::Internal("Could not parse path".to_owned()), senders);
                return;
            },
        };
        let mut file = File::open(&file_path);
        let reader = match file {
            Ok(ref mut reader) => reader,
            Err(_) => {
                // this should be one of the three errors listed in
                // http://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
                // but, we'll go for a "file not found!"
                let url = Url::parse("about:not-found").unwrap();
                let load_data_404 = LoadData::new(load_data.context, url, &FileLoadOrigin);
                about_loader::factory(load_data_404, senders, classifier, cancel_listener);
                return;
            }
        };

        if cancel_listener.is_cancelled() {
            if let Ok(progress_chan) = get_progress_chan(load_data, &file_path,
                                                         senders, classifier, &[]) {
                let _ = progress_chan.send(Done(Err(NetworkError::LoadCancelled)));
            }
            return;
        }

        match read_block(reader) {
            Ok(ReadStatus::Partial(buf)) => {
                let progress_chan = get_progress_chan(load_data, &file_path,
                                                      senders, classifier, &buf).ok().unwrap();
                progress_chan.send(Payload(buf)).unwrap();
                let read_result = read_all(reader, &progress_chan, &cancel_listener);
                if let Ok(load_result) = read_result {
                    match load_result {
                        LoadResult::Cancelled => return,
                        LoadResult::Finished => progress_chan.send(Done(Ok(()))).unwrap(),
                    }
                }
            }
            Ok(ReadStatus::EOF) => {
                if let Ok(chan) = get_progress_chan(load_data, &file_path,
                                                    senders, classifier, &[]) {
                    let _ = chan.send(Done(Ok(())));
                }
            }
            Err(e) => {
                send_error(load_data.url, NetworkError::Internal(e), senders);
            }
        }
    });
}
<|endoftext|>


<commit_before>use input::Input;
use pdcurses::chtype;
use pdcurses::mmask_t;

pub fn COLOR_PAIR(n: chtype) -> chtype {
    (n << PDC_COLOR_SHIFT) & A_COLOR
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const PDC_CHARTEXT_BITS: chtype = 21;
pub const PDC_COLOR_SHIFT: chtype = PDC_CHARTEXT_BITS + 12;

pub const A_ALTCHARSET: chtype = 0x001 << PDC_CHARTEXT_BITS;
pub const A_BOLD: chtype = 0x080 << PDC_CHARTEXT_BITS;
pub const A_BLINK: chtype = 0x040 << PDC_CHARTEXT_BITS;
pub const A_COLOR: chtype = 0x7fffffff << PDC_COLOR_SHIFT;
pub const A_CHARTEXT: chtype = (0x1 << PDC_CHARTEXT_BITS) - 1;
pub const A_DIM: chtype = 0x400 << PDC_CHARTEXT_BITS;
pub const A_LEFTLINE: chtype = 0x004 << PDC_CHARTEXT_BITS;
pub const A_INVIS: chtype = 0x008 << PDC_CHARTEXT_BITS;
pub const A_ITALIC: chtype = A_INVIS;
pub const A_NORMAL: chtype = 0;
pub const A_OVERLINE: chtype = 0x100 << PDC_CHARTEXT_BITS;
pub const A_REVERSE: chtype = 0x020 << PDC_CHARTEXT_BITS;
pub const A_RIGHTLINE: chtype = 0x002 << PDC_CHARTEXT_BITS;
pub const A_STRIKEOUT: chtype = 0x200 << PDC_CHARTEXT_BITS;
pub const A_UNDERLINE: chtype = 0x010 << PDC_CHARTEXT_BITS;

pub const KEY_OFFSET: i32 = 0xec00;
pub const KEY_F15: i32 = (KEY_OFFSET + 0x17);
pub const KEY_UNDO: i32 = (KEY_OFFSET + 0x96);
pub const KEY_RESIZE: i32 = (KEY_OFFSET + 0x122);
pub const KEY_MOUSE: i32 = (KEY_OFFSET + 0x11b);

pub const SPECIAL_KEY_CODES: [Input; 102] = [
    Input::KeyCodeYes,
    Input::KeyBreak,
    Input::KeyDown,
    Input::KeyUp,
    Input::KeyLeft,
    Input::KeyRight,
    Input::KeyHome,
    Input::KeyBackspace,
    Input::KeyF0,
    Input::KeyF1,
    Input::KeyF2,
    Input::KeyF3,
    Input::KeyF4,
    Input::KeyF5,
    Input::KeyF6,
    Input::KeyF7,
    Input::KeyF8,
    Input::KeyF9,
    Input::KeyF10,
    Input::KeyF11,
    Input::KeyF12,
    Input::KeyF13,
    Input::KeyF14,
    Input::KeyF15,
    // PDcurses reserves space for 64 function keys, but we've
    // only implemented 15. This has to be taken into account
    // when converting the integer into an index of this array
    Input::KeyDL,
    Input::KeyIL,
    Input::KeyDC,
    Input::KeyIC,
    Input::KeyEIC,
    Input::KeyClear,
    Input::KeyEOS,
    Input::KeyEOL,
    Input::KeySF,
    Input::KeySR,
    Input::KeyNPage,
    Input::KeyPPage,
    Input::KeySTab,
    Input::KeyCTab,
    Input::KeyCATab,
    Input::KeyEnter,
    Input::KeySReset,
    Input::KeyReset,
    Input::KeyPrint,
    Input::KeyLL,
    Input::KeyAbort,
    Input::KeySHelp,
    Input::KeyLHelp,
    Input::KeyBTab,
    Input::KeyBeg,
    Input::KeyCancel,
    Input::KeyClose,
    Input::KeyCommand,
    Input::KeyCopy,
    Input::KeyCreate,
    Input::KeyEnd,
    Input::KeyExit,
    Input::KeyFind,
    Input::KeyHelp,
    Input::KeyMark,
    Input::KeyMessage,
    Input::KeyMove,
    Input::KeyNext,
    Input::KeyOpen,
    Input::KeyOptions,
    Input::KeyPrevious,
    Input::KeyRedo,
    Input::KeyReference,
    Input::KeyRefresh,
    Input::KeyReplace,
    Input::KeyRestart,
    Input::KeyResume,
    Input::KeySave,
    Input::KeySBeg,
    Input::KeySCancel,
    Input::KeySCommand,
    Input::KeySCopy,
    Input::KeySCreate,
    Input::KeySDC,
    Input::KeySDL,
    Input::KeySelect,
    Input::KeySEnd,
    Input::KeySEOL,
    Input::KeySExit,
    Input::KeySFind,
    Input::KeySHome,
    Input::KeySIC,
    Input::KeySLeft,
    Input::KeySMessage,
    Input::KeySMove,
    Input::KeySNext,
    Input::KeySOptions,
    Input::KeySPrevious,
    Input::KeySPrint,
    Input::KeySRedo,
    Input::KeySReplace,
    Input::KeySRight,
    Input::KeySResume,
    Input::KeySSave,
    Input::KeySSuspend,
    Input::KeySUndo,
    Input::KeySuspend,
    Input::KeyUndo,
];

pub const ALL_MOUSE_EVENTS: mmask_t = 0x1fffffff;

pub const BUTTON1_RELEASED: mmask_t = 0x00000001;
pub const BUTTON1_PRESSED: mmask_t = 0x00000002;
pub const BUTTON1_CLICKED: mmask_t = 0x00000004;
pub const BUTTON1_DOUBLE_CLICKED: mmask_t = 0x00000008;
pub const BUTTON1_TRIPLE_CLICKED: mmask_t = 0x00000010;

pub const BUTTON2_RELEASED: mmask_t = 0x00000020;
pub const BUTTON2_PRESSED: mmask_t = 0x00000040;
pub const BUTTON2_CLICKED: mmask_t = 0x00000080;
pub const BUTTON2_DOUBLE_CLICKED: mmask_t = 0x00000100;
pub const BUTTON2_TRIPLE_CLICKED: mmask_t = 0x00000200;

pub const BUTTON3_RELEASED: mmask_t = 0x00000400;
pub const BUTTON3_PRESSED: mmask_t = 0x00000800;
pub const BUTTON3_CLICKED: mmask_t = 0x00001000;
pub const BUTTON3_DOUBLE_CLICKED: mmask_t = 0x00002000;
pub const BUTTON3_TRIPLE_CLICKED: mmask_t = 0x00004000;

pub const BUTTON4_RELEASED: mmask_t = 0x00008000;
pub const BUTTON4_PRESSED: mmask_t = 0x00010000;
pub const BUTTON4_CLICKED: mmask_t = 0x00020000;
pub const BUTTON4_DOUBLE_CLICKED: mmask_t = 0x00040000;
pub const BUTTON4_TRIPLE_CLICKED: mmask_t = 0x00080000;

pub const BUTTON5_RELEASED: mmask_t = 0x00100000;
pub const BUTTON5_PRESSED: mmask_t = 0x00200000;
pub const BUTTON5_CLICKED: mmask_t = 0x00400000;
pub const BUTTON5_DOUBLE_CLICKED: mmask_t = 0x00800000;
pub const BUTTON5_TRIPLE_CLICKED: mmask_t = 0x01000000;

pub const REPORT_MOUSE_POSITION: mmask_t = 0x20000000;
pub const BUTTON_SHIFT: mmask_t = 0x04000000;
pub const BUTTON_CTRL: mmask_t = 0x08000000;
pub const BUTTON_ALT: mmask_t = 0x10000000;

pub fn ACS_LRCORNER() -> chtype {
    'V' as chtype | A_ALTCHARSET
}
pub fn ACS_URCORNER() -> chtype {
    'W' as chtype | A_ALTCHARSET
}
pub fn ACS_ULCORNER() -> chtype {
    'X' as chtype | A_ALTCHARSET
}
pub fn ACS_LLCORNER() -> chtype {
    'Y' as chtype | A_ALTCHARSET
}
pub fn ACS_PLUS() -> chtype {
    'Z' as chtype | A_ALTCHARSET
}
pub fn ACS_LTEE() -> chtype {
    '[' as chtype | A_ALTCHARSET
}
pub fn ACS_RTEE() -> chtype {
    '\\' as chtype | A_ALTCHARSET
}
pub fn ACS_BTEE() -> chtype {
    ']' as chtype | A_ALTCHARSET
}
pub fn ACS_TTEE() -> chtype {
    '^' as chtype | A_ALTCHARSET
}
pub fn ACS_HLINE() -> chtype {
    '_' as chtype | A_ALTCHARSET
}
pub fn ACS_VLINE() -> chtype {
    '`' as chtype | A_ALTCHARSET
}

pub fn ACS_S1() -> chtype {
    'l' as chtype | A_ALTCHARSET
}
pub fn ACS_S9() -> chtype {
    'o' as chtype | A_ALTCHARSET
}
pub fn ACS_DIAMOND() -> chtype {
    'j' as chtype | A_ALTCHARSET
}
pub fn ACS_CKBOARD() -> chtype {
    'k' as chtype | A_ALTCHARSET
}
pub fn ACS_DEGREE() -> chtype {
    'w' as chtype | A_ALTCHARSET
}
pub fn ACS_PLMINUS() -> chtype {
    'x' as chtype | A_ALTCHARSET
}
pub fn ACS_BULLET() -> chtype {
    'h' as chtype | A_ALTCHARSET
}

pub fn ACS_LARROW() -> chtype {
    '!' as chtype | A_ALTCHARSET
}
pub fn ACS_RARROW() -> chtype {
    ' ' as chtype | A_ALTCHARSET
}
pub fn ACS_DARROW() -> chtype {
    '#' as chtype | A_ALTCHARSET
}
pub fn ACS_UARROW() -> chtype {
    '"' as chtype | A_ALTCHARSET
}
pub fn ACS_BOARD() -> chtype {
    '+' as chtype | A_ALTCHARSET
}
pub fn ACS_LANTERN() -> chtype {
    'z' as chtype | A_ALTCHARSET
}
pub fn ACS_BLOCK() -> chtype {
    't' as chtype | A_ALTCHARSET
}

pub fn ACS_S3() -> chtype {
    'm' as chtype | A_ALTCHARSET
}
pub fn ACS_S7() -> chtype {
    'n' as chtype | A_ALTCHARSET
}
pub fn ACS_LEQUAL() -> chtype {
    'u' as chtype | A_ALTCHARSET
}
pub fn ACS_GEQUAL() -> chtype {
    'v' as chtype | A_ALTCHARSET
}
pub fn ACS_PI() -> chtype {
    '$' as chtype | A_ALTCHARSET
}
pub fn ACS_NEQUAL() -> chtype {
    '%' as chtype | A_ALTCHARSET
}
pub fn ACS_STERLING() -> chtype {
    '~' as chtype | A_ALTCHARSET
}

pub fn ACS_BSSB() -> chtype {
    ACS_ULCORNER()
}
pub fn ACS_SSBB() -> chtype {
    ACS_LLCORNER()
}
pub fn ACS_BBSS() -> chtype {
    ACS_URCORNER()
}
pub fn ACS_SBBS() -> chtype {
    ACS_LRCORNER()
}
pub fn ACS_SBSS() -> chtype {
    ACS_RTEE()
}
pub fn ACS_SSSB() -> chtype {
    ACS_LTEE()
}
pub fn ACS_SSBS() -> chtype {
    ACS_BTEE()
}
pub fn ACS_BSSS() -> chtype {
    ACS_TTEE()
}
pub fn ACS_BSBS() -> chtype {
    ACS_HLINE()
}
pub fn ACS_SBSB() -> chtype {
    ACS_VLINE()
}
pub fn ACS_SSSS() -> chtype {
    ACS_PLUS()
}
<commit_msg>Added separators to long literals<commit_after>use input::Input;
use pdcurses::chtype;
use pdcurses::mmask_t;

pub fn COLOR_PAIR(n: chtype) -> chtype {
    (n << PDC_COLOR_SHIFT) & A_COLOR
}

pub const COLOR_BLACK: i16 = 0;
pub const COLOR_RED: i16 = 1;
pub const COLOR_GREEN: i16 = 2;
pub const COLOR_YELLOW: i16 = 3;
pub const COLOR_BLUE: i16 = 4;
pub const COLOR_MAGENTA: i16 = 5;
pub const COLOR_CYAN: i16 = 6;
pub const COLOR_WHITE: i16 = 7;

pub const PDC_CHARTEXT_BITS: chtype = 21;
pub const PDC_COLOR_SHIFT: chtype = PDC_CHARTEXT_BITS + 12;

pub const A_ALTCHARSET: chtype = 0x001 << PDC_CHARTEXT_BITS;
pub const A_BOLD: chtype = 0x080 << PDC_CHARTEXT_BITS;
pub const A_BLINK: chtype = 0x040 << PDC_CHARTEXT_BITS;
pub const A_COLOR: chtype = 0x7fffffff << PDC_COLOR_SHIFT;
pub const A_CHARTEXT: chtype = (0x1 << PDC_CHARTEXT_BITS) - 1;
pub const A_DIM: chtype = 0x400 << PDC_CHARTEXT_BITS;
pub const A_LEFTLINE: chtype = 0x004 << PDC_CHARTEXT_BITS;
pub const A_INVIS: chtype = 0x008 << PDC_CHARTEXT_BITS;
pub const A_ITALIC: chtype = A_INVIS;
pub const A_NORMAL: chtype = 0;
pub const A_OVERLINE: chtype = 0x100 << PDC_CHARTEXT_BITS;
pub const A_REVERSE: chtype = 0x020 << PDC_CHARTEXT_BITS;
pub const A_RIGHTLINE: chtype = 0x002 << PDC_CHARTEXT_BITS;
pub const A_STRIKEOUT: chtype = 0x200 << PDC_CHARTEXT_BITS;
pub const A_UNDERLINE: chtype = 0x010 << PDC_CHARTEXT_BITS;

pub const KEY_OFFSET: i32 = 0xec00;
pub const KEY_F15: i32 = (KEY_OFFSET + 0x17);
pub const KEY_UNDO: i32 = (KEY_OFFSET + 0x96);
pub const KEY_RESIZE: i32 = (KEY_OFFSET + 0x122);
pub const KEY_MOUSE: i32 = (KEY_OFFSET + 0x11b);

pub const SPECIAL_KEY_CODES: [Input; 102] = [
    Input::KeyCodeYes,
    Input::KeyBreak,
    Input::KeyDown,
    Input::KeyUp,
    Input::KeyLeft,
    Input::KeyRight,
    Input::KeyHome,
    Input::KeyBackspace,
    Input::KeyF0,
    Input::KeyF1,
    Input::KeyF2,
    Input::KeyF3,
    Input::KeyF4,
    Input::KeyF5,
    Input::KeyF6,
    Input::KeyF7,
    Input::KeyF8,
    Input::KeyF9,
    Input::KeyF10,
    Input::KeyF11,
    Input::KeyF12,
    Input::KeyF13,
    Input::KeyF14,
    Input::KeyF15,
    // PDcurses reserves space for 64 function keys, but we've
    // only implemented 15. This has to be taken into account
    // when converting the integer into an index of this array
    Input::KeyDL,
    Input::KeyIL,
    Input::KeyDC,
    Input::KeyIC,
    Input::KeyEIC,
    Input::KeyClear,
    Input::KeyEOS,
    Input::KeyEOL,
    Input::KeySF,
    Input::KeySR,
    Input::KeyNPage,
    Input::KeyPPage,
    Input::KeySTab,
    Input::KeyCTab,
    Input::KeyCATab,
    Input::KeyEnter,
    Input::KeySReset,
    Input::KeyReset,
    Input::KeyPrint,
    Input::KeyLL,
    Input::KeyAbort,
    Input::KeySHelp,
    Input::KeyLHelp,
    Input::KeyBTab,
    Input::KeyBeg,
    Input::KeyCancel,
    Input::KeyClose,
    Input::KeyCommand,
    Input::KeyCopy,
    Input::KeyCreate,
    Input::KeyEnd,
    Input::KeyExit,
    Input::KeyFind,
    Input::KeyHelp,
    Input::KeyMark,
    Input::KeyMessage,
    Input::KeyMove,
    Input::KeyNext,
    Input::KeyOpen,
    Input::KeyOptions,
    Input::KeyPrevious,
    Input::KeyRedo,
    Input::KeyReference,
    Input::KeyRefresh,
    Input::KeyReplace,
    Input::KeyRestart,
    Input::KeyResume,
    Input::KeySave,
    Input::KeySBeg,
    Input::KeySCancel,
    Input::KeySCommand,
    Input::KeySCopy,
    Input::KeySCreate,
    Input::KeySDC,
    Input::KeySDL,
    Input::KeySelect,
    Input::KeySEnd,
    Input::KeySEOL,
    Input::KeySExit,
    Input::KeySFind,
    Input::KeySHome,
    Input::KeySIC,
    Input::KeySLeft,
    Input::KeySMessage,
    Input::KeySMove,
    Input::KeySNext,
    Input::KeySOptions,
    Input::KeySPrevious,
    Input::KeySPrint,
    Input::KeySRedo,
    Input::KeySReplace,
    Input::KeySRight,
    Input::KeySResume,
    Input::KeySSave,
    Input::KeySSuspend,
    Input::KeySUndo,
    Input::KeySuspend,
    Input::KeyUndo,
];

pub const ALL_MOUSE_EVENTS: mmask_t = 0x1fff_ffff;

pub const BUTTON1_RELEASED: mmask_t = 0x0000_0001;
pub const BUTTON1_PRESSED: mmask_t = 0x0000_0002;
pub const BUTTON1_CLICKED: mmask_t = 0x0000_0004;
pub const BUTTON1_DOUBLE_CLICKED: mmask_t = 0x0000_0008;
pub const BUTTON1_TRIPLE_CLICKED: mmask_t = 0x0000_0010;

pub const BUTTON2_RELEASED: mmask_t = 0x0000_0020;
pub const BUTTON2_PRESSED: mmask_t = 0x0000_0040;
pub const BUTTON2_CLICKED: mmask_t = 0x0000_0080;
pub const BUTTON2_DOUBLE_CLICKED: mmask_t = 0x0000_0100;
pub const BUTTON2_TRIPLE_CLICKED: mmask_t = 0x0000_0200;

pub const BUTTON3_RELEASED: mmask_t = 0x0000_0400;
pub const BUTTON3_PRESSED: mmask_t = 0x0000_0800;
pub const BUTTON3_CLICKED: mmask_t = 0x0000_1000;
pub const BUTTON3_DOUBLE_CLICKED: mmask_t = 0x0000_2000;
pub const BUTTON3_TRIPLE_CLICKED: mmask_t = 0x0000_4000;

pub const BUTTON4_RELEASED: mmask_t = 0x0000_8000;
pub const BUTTON4_PRESSED: mmask_t = 0x0001_0000;
pub const BUTTON4_CLICKED: mmask_t = 0x0002_0000;
pub const BUTTON4_DOUBLE_CLICKED: mmask_t = 0x0004_0000;
pub const BUTTON4_TRIPLE_CLICKED: mmask_t = 0x0008_0000;

pub const BUTTON5_RELEASED: mmask_t = 0x0010_0000;
pub const BUTTON5_PRESSED: mmask_t = 0x0020_0000;
pub const BUTTON5_CLICKED: mmask_t = 0x0040_0000;
pub const BUTTON5_DOUBLE_CLICKED: mmask_t = 0x0080_0000;
pub const BUTTON5_TRIPLE_CLICKED: mmask_t = 0x0100_0000;

pub const REPORT_MOUSE_POSITION: mmask_t = 0x2000_0000;
pub const BUTTON_SHIFT: mmask_t = 0x0400_0000;
pub const BUTTON_CTRL: mmask_t = 0x0800_0000;
pub const BUTTON_ALT: mmask_t = 0x1000_0000;

pub fn ACS_LRCORNER() -> chtype {
    'V' as chtype | A_ALTCHARSET
}
pub fn ACS_URCORNER() -> chtype {
    'W' as chtype | A_ALTCHARSET
}
pub fn ACS_ULCORNER() -> chtype {
    'X' as chtype | A_ALTCHARSET
}
pub fn ACS_LLCORNER() -> chtype {
    'Y' as chtype | A_ALTCHARSET
}
pub fn ACS_PLUS() -> chtype {
    'Z' as chtype | A_ALTCHARSET
}
pub fn ACS_LTEE() -> chtype {
    '[' as chtype | A_ALTCHARSET
}
pub fn ACS_RTEE() -> chtype {
    '\\' as chtype | A_ALTCHARSET
}
pub fn ACS_BTEE() -> chtype {
    ']' as chtype | A_ALTCHARSET
}
pub fn ACS_TTEE() -> chtype {
    '^' as chtype | A_ALTCHARSET
}
pub fn ACS_HLINE() -> chtype {
    '_' as chtype | A_ALTCHARSET
}
pub fn ACS_VLINE() -> chtype {
    '`' as chtype | A_ALTCHARSET
}

pub fn ACS_S1() -> chtype {
    'l' as chtype | A_ALTCHARSET
}
pub fn ACS_S9() -> chtype {
    'o' as chtype | A_ALTCHARSET
}
pub fn ACS_DIAMOND() -> chtype {
    'j' as chtype | A_ALTCHARSET
}
pub fn ACS_CKBOARD() -> chtype {
    'k' as chtype | A_ALTCHARSET
}
pub fn ACS_DEGREE() -> chtype {
    'w' as chtype | A_ALTCHARSET
}
pub fn ACS_PLMINUS() -> chtype {
    'x' as chtype | A_ALTCHARSET
}
pub fn ACS_BULLET() -> chtype {
    'h' as chtype | A_ALTCHARSET
}

pub fn ACS_LARROW() -> chtype {
    '!' as chtype | A_ALTCHARSET
}
pub fn ACS_RARROW() -> chtype {
    ' ' as chtype | A_ALTCHARSET
}
pub fn ACS_DARROW() -> chtype {
    '#' as chtype | A_ALTCHARSET
}
pub fn ACS_UARROW() -> chtype {
    '"' as chtype | A_ALTCHARSET
}
pub fn ACS_BOARD() -> chtype {
    '+' as chtype | A_ALTCHARSET
}
pub fn ACS_LANTERN() -> chtype {
    'z' as chtype | A_ALTCHARSET
}
pub fn ACS_BLOCK() -> chtype {
    't' as chtype | A_ALTCHARSET
}

pub fn ACS_S3() -> chtype {
    'm' as chtype | A_ALTCHARSET
}
pub fn ACS_S7() -> chtype {
    'n' as chtype | A_ALTCHARSET
}
pub fn ACS_LEQUAL() -> chtype {
    'u' as chtype | A_ALTCHARSET
}
pub fn ACS_GEQUAL() -> chtype {
    'v' as chtype | A_ALTCHARSET
}
pub fn ACS_PI() -> chtype {
    '$' as chtype | A_ALTCHARSET
}
pub fn ACS_NEQUAL() -> chtype {
    '%' as chtype | A_ALTCHARSET
}
pub fn ACS_STERLING() -> chtype {
    '~' as chtype | A_ALTCHARSET
}

pub fn ACS_BSSB() -> chtype {
    ACS_ULCORNER()
}
pub fn ACS_SSBB() -> chtype {
    ACS_LLCORNER()
}
pub fn ACS_BBSS() -> chtype {
    ACS_URCORNER()
}
pub fn ACS_SBBS() -> chtype {
    ACS_LRCORNER()
}
pub fn ACS_SBSS() -> chtype {
    ACS_RTEE()
}
pub fn ACS_SSSB() -> chtype {
    ACS_LTEE()
}
pub fn ACS_SSBS() -> chtype {
    ACS_BTEE()
}
pub fn ACS_BSSS() -> chtype {
    ACS_TTEE()
}
pub fn ACS_BSBS() -> chtype {
    ACS_HLINE()
}
pub fn ACS_SBSB() -> chtype {
    ACS_VLINE()
}
pub fn ACS_SSSS() -> chtype {
    ACS_PLUS()
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-face`][ff] at-rule.
//!
//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule

#![deny(missing_docs)]

#[cfg(feature = "gecko")]
use computed_values::{font_stretch, font_style, font_weight};
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{SourceLocation, CowRcStr};
use error_reporting::{ContextualParseError, ParseErrorReporter};
#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
#[cfg(feature = "gecko")] use cssparser::UnicodeRange;
use parser::{ParserContext, ParserErrorContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::font_language_override;
use selectors::parser::SelectorParseErrorKind;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
use style_traits::{StyleParseErrorKind, ToCss};
use values::computed::font::FamilyName;
#[cfg(feature = "gecko")]
use values::specified::font::{SpecifiedFontFeatureSettings, FontVariationSettings};
use values::specified::url::SpecifiedUrl;

/// A source for a font-face rule.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum Source {
    /// A `url()` source.
    Url(UrlSource),
    /// A `local()` source.
    #[css(function)]
    Local(FamilyName),
}

impl OneOrMoreSeparated for Source {
    type S = Comma;
}

/// A `UrlSource` represents a font-face source that has been specified with a
/// `url()` function.
///
/// <https://drafts.csswg.org/css-fonts/#src-desc>
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct UrlSource {
    /// The specified url.
    pub url: SpecifiedUrl,
    /// The format hints specified with the `format()` function.
    pub format_hints: Vec<String>,
}

impl ToCss for UrlSource {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.url.to_css(dest)
    }
}

/// A font-display value for a @font-face rule.
/// The font-display descriptor determines how a font face is displayed based
/// on whether and when it is downloaded and ready to use.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum FontDisplay {
    Auto,
    Block,
    Swap,
    Fallback,
    Optional,
}

/// A font-weight value for a @font-face rule.
/// The font-weight CSS property specifies the weight or boldness of the font.
#[cfg(feature = "gecko")]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum FontWeight {
    /// Numeric font weights for fonts that provide more than just normal and bold.
    Weight(font_weight::T),
    /// Normal font weight. Same as 400.
    Normal,
    /// Bold font weight. Same as 700.
    Bold,
}

#[cfg(feature = "gecko")]
impl Parse for FontWeight {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<FontWeight, ParseError<'i>> {
        let result = input.try(|input| {
            let ident = input.expect_ident().map_err(|_| ())?;
            match_ignore_ascii_case! { &ident,
                "normal" => Ok(FontWeight::Normal),
                "bold" => Ok(FontWeight::Bold),
                _ => Err(())
            }
        });
        result.or_else(|_| {
            font_weight::T::from_int(input.expect_integer()?)
                .map(FontWeight::Weight)
                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        })
    }
}

/// Parse the block inside a `@font-face` rule.
///
/// Note that the prelude parsing code lives in the `stylesheets` module.
pub fn parse_font_face_block<R>(context: &ParserContext,
                                error_context: &ParserErrorContext<R>,
                                input: &mut Parser,
                                location: SourceLocation)
                                -> FontFaceRuleData
    where R: ParseErrorReporter
{
    let mut rule = FontFaceRuleData::empty(location);
    {
        let parser = FontFaceRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err((error, slice)) = declaration {
                let location = error.location;
                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
                context.log_css_error(error_context, location, error)
            }
        }
    }
    rule
}

/// A @font-face rule that is known to have font-family and src declarations.
#[cfg(feature = "servo")]
pub struct FontFace<'a>(&'a FontFaceRuleData);

/// A list of effective sources that we send over through IPC to the font cache.
#[cfg(feature = "servo")]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct EffectiveSources(Vec<Source>);

#[cfg(feature = "servo")]
impl<'a> FontFace<'a> {
    /// Returns the list of effective sources for that font-face, that is the
    /// sources which don't list any format hint, or the ones which list at
    /// least "truetype" or "opentype".
    pub fn effective_sources(&self) -> EffectiveSources {
        EffectiveSources(self.sources().iter().rev().filter(|source| {
            if let Source::Url(ref url_source) = **source {
                let hints = &url_source.format_hints;
                // We support only opentype fonts and truetype is an alias for
                // that format. Sources without format hints need to be
                // downloaded in case we support them.
                hints.is_empty() || hints.iter().any(|hint| {
                    hint == "truetype" || hint == "opentype" || hint == "woff"
                })
            } else {
                true
            }
        }).cloned().collect())
    }
}

#[cfg(feature = "servo")]
impl Iterator for EffectiveSources {
    type Item = Source;
    fn next(&mut self) -> Option<Source> {
        self.0.pop()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.0.len(), Some(self.0.len()))
    }
}

struct FontFaceRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut FontFaceRuleData,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = ();
    type Error = StyleParseErrorKind<'i>;
}

impl Parse for Source {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Source, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("local")).is_ok() {
            return input.parse_nested_block(|input| {
                FamilyName::parse(context, input)
            }).map(Source::Local)
        }

        let url = SpecifiedUrl::parse(context, input)?;

        // Parsing optional format()
        let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
            input.parse_nested_block(|input| {
                input.parse_comma_separated(|input| {
                    Ok(input.expect_string()?.as_ref().to_owned())
                })
            })?
        } else {
            vec![]
        };

        Ok(Source::Url(UrlSource {
            url: url,
            format_hints: format_hints,
        }))
    }
}

macro_rules! is_descriptor_enabled {
    ("font-display") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontDisplayEnabled
        }
    };
    ("font-variation-settings") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontVariationsEnabled
        }
    };
    ($name: tt) => { true }
}

macro_rules! font_face_descriptors_common {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
    ) => {
        /// Data inside a `@font-face` rule.
        ///
        /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFaceRuleData {
            $(
                #[$doc]
                pub $ident: Option<$ty>,
            )*
            /// Line and column of the @font-face rule source code.
            pub source_location: SourceLocation,
        }

        impl FontFaceRuleData {
            fn empty(location: SourceLocation) -> Self {
                FontFaceRuleData {
                    $(
                        $ident: None,
                    )*
                    source_location: location,
                }
            }

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
                $(
                    if let Some(value) = self.$ident {
                        descriptors.$gecko_ident.set_from(value)
                    }
                )*
                // Leave unset descriptors to eCSSUnit_Null,
                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
                // to initial values.
            }
        }

        impl ToCssWithGuard for FontFaceRuleData {
            // Serialization of FontFaceRule is not specced.
            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
                dest.write_str("@font-face {\n")?;
                $(
                    if let Some(ref value) = self.$ident {
                        dest.write_str(concat!("  ", $name, ": "))?;
                        ToCss::to_css(value, &mut CssWriter::new(dest))?;
                        dest.write_str(";\n")?;
                    }
                )*
                dest.write_str("}")
            }
        }

       impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
           type Declaration = ();
           type Error = StyleParseErrorKind<'i>;

           fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                              -> Result<(), ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name if is_descriptor_enabled!($name) => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = Some(value)
                        }
                    )*
                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                }
                Ok(())
            }
        }
    }
}

macro_rules! font_face_descriptors {
    (
        mandatory descriptors = [
            $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
        ]
        optional descriptors = [
            $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty =
                $o_initial: expr, )*
        ]
    ) => {
        font_face_descriptors_common! {
            $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
            $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
        }

        impl FontFaceRuleData {
            /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
            /// is valid as far as the CSS parser is concerned even if it doesn’t have
            /// a font-family or src declaration.
            ///
            /// However both are required for the rule to represent an actual font face.
            #[cfg(feature = "servo")]
            pub fn font_face(&self) -> Option<FontFace> {
                if $( self.$m_ident.is_some() )&&* {
                    Some(FontFace(self))
                } else {
                    None
                }
            }
        }

        #[cfg(feature = "servo")]
        impl<'a> FontFace<'a> {
            $(
                #[$m_doc]
                pub fn $m_ident(&self) -> &$m_ty {
                    self.0 .$m_ident.as_ref().unwrap()
                }
            )*
            $(
                #[$o_doc]
                pub fn $o_ident(&self) -> $o_ty {
                    if let Some(ref value) = self.0 .$o_ident {
                        value.clone()
                    } else {
                        $o_initial
                    }
                }
            )*
        }
    }
}

/// css-name rust_identifier: Type = initial_value,
#[cfg(feature = "gecko")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
        /// The style of this font face
        "font-style" style / mStyle: font_style::T = font_style::T::normal,

        /// The weight of this font face
        "font-weight" weight / mWeight: FontWeight = FontWeight::Normal,

        /// The stretch of this font face
        "font-stretch" stretch / mStretch: font_stretch::T = font_stretch::T::normal,

        /// The display of this font face
        "font-display" display / mDisplay: FontDisplay = FontDisplay::Auto,

        /// The ranges of code points outside of which this font face should not be used.
        "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange> = vec![
            UnicodeRange { start: 0, end: 0x10FFFF }
        ],

        /// The feature settings of this font face.
        "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings = {
            font_feature_settings::SpecifiedValue::normal()
        },

        /// The variation settings of this font face.
        "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings = {
            font_variation_settings::SpecifiedValue::normal()
        },

        /// The language override of this font face.
        "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue = {
            font_language_override::SpecifiedValue::Normal
        },
    ]
}

#[cfg(feature = "servo")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
    ]
}
<commit_msg>Remove initial values from font-face descriptors<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The [`@font-face`][ff] at-rule.
//!
//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule

#![deny(missing_docs)]

#[cfg(feature = "gecko")]
use computed_values::{font_stretch, font_style, font_weight};
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
use cssparser::{SourceLocation, CowRcStr};
use error_reporting::{ContextualParseError, ParseErrorReporter};
#[cfg(feature = "gecko")] use gecko_bindings::structs::CSSFontFaceDescriptors;
#[cfg(feature = "gecko")] use cssparser::UnicodeRange;
use parser::{ParserContext, ParserErrorContext, Parse};
#[cfg(feature = "gecko")]
use properties::longhands::font_language_override;
use selectors::parser::SelectorParseErrorKind;
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::fmt::{self, Write};
use str::CssStringWriter;
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
use style_traits::{StyleParseErrorKind, ToCss};
use values::computed::font::FamilyName;
#[cfg(feature = "gecko")]
use values::specified::font::{SpecifiedFontFeatureSettings, FontVariationSettings};
use values::specified::url::SpecifiedUrl;

/// A source for a font-face rule.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum Source {
    /// A `url()` source.
    Url(UrlSource),
    /// A `local()` source.
    #[css(function)]
    Local(FamilyName),
}

impl OneOrMoreSeparated for Source {
    type S = Comma;
}

/// A `UrlSource` represents a font-face source that has been specified with a
/// `url()` function.
///
/// <https://drafts.csswg.org/css-fonts/#src-desc>
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct UrlSource {
    /// The specified url.
    pub url: SpecifiedUrl,
    /// The format hints specified with the `format()` function.
    pub format_hints: Vec<String>,
}

impl ToCss for UrlSource {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.url.to_css(dest)
    }
}

/// A font-display value for a @font-face rule.
/// The font-display descriptor determines how a font face is displayed based
/// on whether and when it is downloaded and ready to use.
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq)]
#[derive(ToComputedValue, ToCss)]
pub enum FontDisplay {
    Auto,
    Block,
    Swap,
    Fallback,
    Optional,
}

/// A font-weight value for a @font-face rule.
/// The font-weight CSS property specifies the weight or boldness of the font.
#[cfg(feature = "gecko")]
#[derive(Clone, Debug, Eq, PartialEq, ToCss)]
pub enum FontWeight {
    /// Numeric font weights for fonts that provide more than just normal and bold.
    Weight(font_weight::T),
    /// Normal font weight. Same as 400.
    Normal,
    /// Bold font weight. Same as 700.
    Bold,
}

#[cfg(feature = "gecko")]
impl Parse for FontWeight {
    fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>)
        -> Result<FontWeight, ParseError<'i>> {
        let result = input.try(|input| {
            let ident = input.expect_ident().map_err(|_| ())?;
            match_ignore_ascii_case! { &ident,
                "normal" => Ok(FontWeight::Normal),
                "bold" => Ok(FontWeight::Bold),
                _ => Err(())
            }
        });
        result.or_else(|_| {
            font_weight::T::from_int(input.expect_integer()?)
                .map(FontWeight::Weight)
                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        })
    }
}

/// Parse the block inside a `@font-face` rule.
///
/// Note that the prelude parsing code lives in the `stylesheets` module.
pub fn parse_font_face_block<R>(context: &ParserContext,
                                error_context: &ParserErrorContext<R>,
                                input: &mut Parser,
                                location: SourceLocation)
                                -> FontFaceRuleData
    where R: ParseErrorReporter
{
    let mut rule = FontFaceRuleData::empty(location);
    {
        let parser = FontFaceRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err((error, slice)) = declaration {
                let location = error.location;
                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
                context.log_css_error(error_context, location, error)
            }
        }
    }
    rule
}

/// A @font-face rule that is known to have font-family and src declarations.
#[cfg(feature = "servo")]
pub struct FontFace<'a>(&'a FontFaceRuleData);

/// A list of effective sources that we send over through IPC to the font cache.
#[cfg(feature = "servo")]
#[derive(Clone, Debug)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
pub struct EffectiveSources(Vec<Source>);

#[cfg(feature = "servo")]
impl<'a> FontFace<'a> {
    /// Returns the list of effective sources for that font-face, that is the
    /// sources which don't list any format hint, or the ones which list at
    /// least "truetype" or "opentype".
    pub fn effective_sources(&self) -> EffectiveSources {
        EffectiveSources(self.sources().iter().rev().filter(|source| {
            if let Source::Url(ref url_source) = **source {
                let hints = &url_source.format_hints;
                // We support only opentype fonts and truetype is an alias for
                // that format. Sources without format hints need to be
                // downloaded in case we support them.
                hints.is_empty() || hints.iter().any(|hint| {
                    hint == "truetype" || hint == "opentype" || hint == "woff"
                })
            } else {
                true
            }
        }).cloned().collect())
    }
}

#[cfg(feature = "servo")]
impl Iterator for EffectiveSources {
    type Item = Source;
    fn next(&mut self) -> Option<Source> {
        self.0.pop()
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.0.len(), Some(self.0.len()))
    }
}

struct FontFaceRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut FontFaceRuleData,
}

/// Default methods reject all at rules.
impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> {
    type PreludeNoBlock = ();
    type PreludeBlock = ();
    type AtRule = ();
    type Error = StyleParseErrorKind<'i>;
}

impl Parse for Source {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Source, ParseError<'i>> {
        if input.try(|input| input.expect_function_matching("local")).is_ok() {
            return input.parse_nested_block(|input| {
                FamilyName::parse(context, input)
            }).map(Source::Local)
        }

        let url = SpecifiedUrl::parse(context, input)?;

        // Parsing optional format()
        let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
            input.parse_nested_block(|input| {
                input.parse_comma_separated(|input| {
                    Ok(input.expect_string()?.as_ref().to_owned())
                })
            })?
        } else {
            vec![]
        };

        Ok(Source::Url(UrlSource {
            url: url,
            format_hints: format_hints,
        }))
    }
}

macro_rules! is_descriptor_enabled {
    ("font-display") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontDisplayEnabled
        }
    };
    ("font-variation-settings") => {
        unsafe {
            use gecko_bindings::structs::mozilla;
            mozilla::StylePrefs_sFontVariationsEnabled
        }
    };
    ($name: tt) => { true }
}

macro_rules! font_face_descriptors_common {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )*
    ) => {
        /// Data inside a `@font-face` rule.
        ///
        /// <https://drafts.csswg.org/css-fonts/#font-face-rule>
        #[derive(Clone, Debug, PartialEq)]
        pub struct FontFaceRuleData {
            $(
                #[$doc]
                pub $ident: Option<$ty>,
            )*
            /// Line and column of the @font-face rule source code.
            pub source_location: SourceLocation,
        }

        impl FontFaceRuleData {
            fn empty(location: SourceLocation) -> Self {
                FontFaceRuleData {
                    $(
                        $ident: None,
                    )*
                    source_location: location,
                }
            }

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(self, descriptors: &mut CSSFontFaceDescriptors) {
                $(
                    if let Some(value) = self.$ident {
                        descriptors.$gecko_ident.set_from(value)
                    }
                )*
                // Leave unset descriptors to eCSSUnit_Null,
                // FontFaceSet::FindOrCreateUserFontEntryFromFontFace does the defaulting
                // to initial values.
            }
        }

        impl ToCssWithGuard for FontFaceRuleData {
            // Serialization of FontFaceRule is not specced.
            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
                dest.write_str("@font-face {\n")?;
                $(
                    if let Some(ref value) = self.$ident {
                        dest.write_str(concat!("  ", $name, ": "))?;
                        ToCss::to_css(value, &mut CssWriter::new(dest))?;
                        dest.write_str(";\n")?;
                    }
                )*
                dest.write_str("}")
            }
        }

       impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> {
           type Declaration = ();
           type Error = StyleParseErrorKind<'i>;

           fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
                              -> Result<(), ParseError<'i>> {
                match_ignore_ascii_case! { &*name,
                    $(
                        $name if is_descriptor_enabled!($name) => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = Some(value)
                        }
                    )*
                    _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
                }
                Ok(())
            }
        }
    }
}

macro_rules! font_face_descriptors {
    (
        mandatory descriptors = [
            $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )*
        ]
        optional descriptors = [
            $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )*
        ]
    ) => {
        font_face_descriptors_common! {
            $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )*
            $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )*
        }

        impl FontFaceRuleData {
            /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
            /// is valid as far as the CSS parser is concerned even if it doesn’t have
            /// a font-family or src declaration.
            ///
            /// However both are required for the rule to represent an actual font face.
            #[cfg(feature = "servo")]
            pub fn font_face(&self) -> Option<FontFace> {
                if $( self.$m_ident.is_some() )&&* {
                    Some(FontFace(self))
                } else {
                    None
                }
            }
        }

        #[cfg(feature = "servo")]
        impl<'a> FontFace<'a> {
            $(
                #[$m_doc]
                pub fn $m_ident(&self) -> &$m_ty {
                    self.0 .$m_ident.as_ref().unwrap()
                }
            )*
        }
    }
}

/// css-name rust_identifier: Type,
#[cfg(feature = "gecko")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
        /// The style of this font face
        "font-style" style / mStyle: font_style::T,

        /// The weight of this font face
        "font-weight" weight / mWeight: FontWeight,

        /// The stretch of this font face
        "font-stretch" stretch / mStretch: font_stretch::T,

        /// The display of this font face
        "font-display" display / mDisplay: FontDisplay,

        /// The ranges of code points outside of which this font face should not be used.
        "unicode-range" unicode_range / mUnicodeRange: Vec<UnicodeRange>,

        /// The feature settings of this font face.
        "font-feature-settings" feature_settings / mFontFeatureSettings: SpecifiedFontFeatureSettings,

        /// The variation settings of this font face.
        "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings,

        /// The language override of this font face.
        "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue,
    ]
}

#[cfg(feature = "servo")]
font_face_descriptors! {
    mandatory descriptors = [
        /// The name of this font face
        "font-family" family / mFamily: FamilyName,

        /// The alternative sources for this font face.
        "src" sources / mSrc: Vec<Source>,
    ]
    optional descriptors = [
    ]
}
<|endoftext|>