<commit_before>use core::mem::*;

#[test]
fn size_of_basic() {
    assert_eq!(size_of::<u8>(), 1);
    assert_eq!(size_of::<u16>(), 2);
    assert_eq!(size_of::<u32>(), 4);
    assert_eq!(size_of::<u64>(), 8);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn size_of_16() {
    assert_eq!(size_of::<usize>(), 2);
    assert_eq!(size_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn size_of_32() {
    assert_eq!(size_of::<usize>(), 4);
    assert_eq!(size_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_64() {
    assert_eq!(size_of::<usize>(), 8);
    assert_eq!(size_of::<*const usize>(), 8);
}

#[test]
fn size_of_val_basic() {
    assert_eq!(size_of_val(&1u8), 1);
    assert_eq!(size_of_val(&1u16), 2);
    assert_eq!(size_of_val(&1u32), 4);
    assert_eq!(size_of_val(&1u64), 8);
}

#[test]
fn align_of_basic() {
    assert_eq!(align_of::<u8>(), 1);
    assert_eq!(align_of::<u16>(), 2);
    assert_eq!(align_of::<u32>(), 4);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn align_of_16() {
    assert_eq!(align_of::<usize>(), 2);
    assert_eq!(align_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn align_of_32() {
    assert_eq!(align_of::<usize>(), 4);
    assert_eq!(align_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn align_of_64() {
    assert_eq!(align_of::<usize>(), 8);
    assert_eq!(align_of::<*const usize>(), 8);
}

#[test]
fn align_of_val_basic() {
    assert_eq!(align_of_val(&1u8), 1);
    assert_eq!(align_of_val(&1u16), 2);
    assert_eq!(align_of_val(&1u32), 4);
}

#[test]
fn test_swap() {
    let mut x = 31337;
    let mut y = 42;
    swap(&mut x, &mut y);
    assert_eq!(x, 42);
    assert_eq!(y, 31337);
}

#[test]
fn test_replace() {
    let mut x = Some("test".to_string());
    let y = replace(&mut x, None);
    assert!(x.is_none());
    assert!(y.is_some());
}

#[test]
fn test_transmute_copy() {
    assert_eq!(1, unsafe { transmute_copy(&1) });
}

#[test]
fn test_transmute() {
    trait Foo {
        fn dummy(&self) {}
    }
    impl Foo for isize {}

    let a = box 100isize as Box<dyn Foo>;
    unsafe {
        let x: ::core::raw::TraitObject = transmute(a);
        assert!(*(x.data as *const isize) == 100);
        let _x: Box<dyn Foo> = transmute(x);
    }

    unsafe {
        assert_eq!(transmute::<_, Vec<u8>>("L".to_string()), [76]);
    }
}

#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
    enum Regular {
        A,
        B(i32),
    }
    enum NotSendSync {
        A(*const i32),
    }

    fn is_send_sync<T: Send + Sync>() {}

    is_send_sync::<Discriminant<Regular>>();
    is_send_sync::<Discriminant<NotSendSync>>();
}

#[test]
fn test_const_forget() {
    const _: () = forget(0i32);
    const _: () = forget(Vec::<Vec<Box<i32>>>::new());
}
<commit_msg>rather than removing const_forget_box, stick an attribute on it and explain it cant be called in ctfe yet<commit_after>use core::mem::*;

#[test]
fn size_of_basic() {
    assert_eq!(size_of::<u8>(), 1);
    assert_eq!(size_of::<u16>(), 2);
    assert_eq!(size_of::<u32>(), 4);
    assert_eq!(size_of::<u64>(), 8);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn size_of_16() {
    assert_eq!(size_of::<usize>(), 2);
    assert_eq!(size_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn size_of_32() {
    assert_eq!(size_of::<usize>(), 4);
    assert_eq!(size_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_64() {
    assert_eq!(size_of::<usize>(), 8);
    assert_eq!(size_of::<*const usize>(), 8);
}

#[test]
fn size_of_val_basic() {
    assert_eq!(size_of_val(&1u8), 1);
    assert_eq!(size_of_val(&1u16), 2);
    assert_eq!(size_of_val(&1u32), 4);
    assert_eq!(size_of_val(&1u64), 8);
}

#[test]
fn align_of_basic() {
    assert_eq!(align_of::<u8>(), 1);
    assert_eq!(align_of::<u16>(), 2);
    assert_eq!(align_of::<u32>(), 4);
}

#[test]
#[cfg(target_pointer_width = "16")]
fn align_of_16() {
    assert_eq!(align_of::<usize>(), 2);
    assert_eq!(align_of::<*const usize>(), 2);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn align_of_32() {
    assert_eq!(align_of::<usize>(), 4);
    assert_eq!(align_of::<*const usize>(), 4);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn align_of_64() {
    assert_eq!(align_of::<usize>(), 8);
    assert_eq!(align_of::<*const usize>(), 8);
}

#[test]
fn align_of_val_basic() {
    assert_eq!(align_of_val(&1u8), 1);
    assert_eq!(align_of_val(&1u16), 2);
    assert_eq!(align_of_val(&1u32), 4);
}

#[test]
fn test_swap() {
    let mut x = 31337;
    let mut y = 42;
    swap(&mut x, &mut y);
    assert_eq!(x, 42);
    assert_eq!(y, 31337);
}

#[test]
fn test_replace() {
    let mut x = Some("test".to_string());
    let y = replace(&mut x, None);
    assert!(x.is_none());
    assert!(y.is_some());
}

#[test]
fn test_transmute_copy() {
    assert_eq!(1, unsafe { transmute_copy(&1) });
}

#[test]
fn test_transmute() {
    trait Foo {
        fn dummy(&self) {}
    }
    impl Foo for isize {}

    let a = box 100isize as Box<dyn Foo>;
    unsafe {
        let x: ::core::raw::TraitObject = transmute(a);
        assert!(*(x.data as *const isize) == 100);
        let _x: Box<dyn Foo> = transmute(x);
    }

    unsafe {
        assert_eq!(transmute::<_, Vec<u8>>("L".to_string()), [76]);
    }
}

#[test]
#[allow(dead_code)]
fn test_discriminant_send_sync() {
    enum Regular {
        A,
        B(i32),
    }
    enum NotSendSync {
        A(*const i32),
    }

    fn is_send_sync<T: Send + Sync>() {}

    is_send_sync::<Discriminant<Regular>>();
    is_send_sync::<Discriminant<NotSendSync>>();
}

#[test]
fn test_const_forget() {
    const _: () = forget(0i32);
    const _: () = forget(Vec::<Vec<Box<i32>>>::new());

    // Writing this function signature without const-forget
    // triggers compiler errors:
    // 1) That we use a non-const fn inside a const fn
    // 2) without the forget, it complains about the destructor of Box
    //
    // FIXME: this method cannot be called in const-eval yet, as Box isn't
    // const constructable
    #[allow(unused)]
    const fn const_forget_box<T: ?Sized>(x: Box<T>) {
        forget(x);
    }
}
<|endoftext|>


<commit_before>use bindings::{
	jit_nint,

	jit_type_void_ptr,
	jit_type_ubyte,
	jit_type_sbyte,
	jit_type_ushort,
	jit_type_short,
	jit_type_uint,
	jit_type_int,
	jit_type_nuint,
	jit_type_nint,
	jit_type_float32,
	jit_type_float64,
	jit_type_sys_bool,
	jit_type_sys_char,

	jit_value_create_long_constant,
	jit_value_create_nint_constant,
	jit_value_create_float64_constant,
	jit_value_create_float32_constant
};
use function::{
	CDECL,
	Function
};
use value::Value;
use types::{
	Type,
	TypeKind,
	Void,
	Float32,
	Float64,
	NInt,
	NUInt,
	Int,
	UInt,
	Short,
	UShort,
	SByte,
	UByte,
	SysBool,
	SysChar
};
use util::NativeRef;
use std::default::Default;
/// A type that can be compiled into a LibJIT representation
pub trait Compilable {
	/// Get a JIT representation of this value
	fn compile(&self, func:&Function) -> Value;
	/// Get the JIT type repr of the value
	fn jit_type(_:Option<Self>) -> Type;
}
#[cfg(test)]
fn test_compile<T:Compilable+Default>(kind:TypeKind) {
	::with_empty_func(|_, func| {
		let pval:T = Default::default();
		let val = pval.compile(func);
		assert!(val.get_type().get_kind().contains(kind));
	})
}
impl Compilable for () {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_void_ptr, 0))
		}
	}
	#[inline]
	fn jit_type(_:Option<()>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_void_ptr)
		}
	}
}
#[test]
fn test_compile_unit() {
	test_compile::<()>(Void)
}
impl Compilable for f64 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_float64_constant(func.as_ptr(), jit_type_float64, *self) )
		}
	}
	#[inline]
	fn jit_type(_:Option<f64>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_float64)
		}
	}
}
#[test]
fn test_compile_f64() {
	test_compile::<f64>(Float64)
}
impl Compilable for f32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_float32_constant(func.as_ptr(), jit_type_float32, *self) )
		}
	}
	#[inline]
	fn jit_type(_:Option<f32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_float32)
		}
	}
}
#[test]
fn test_compile_f32() {
	test_compile::<f32>(Float32)
}
impl Compilable for int {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_long_constant(func.as_ptr(), jit_type_nint, *self as i64) )
		}
	}
	#[inline]
	fn jit_type(_:Option<int>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_nint)
		}
	}
}
#[test]
fn test_compile_int() {
	test_compile::<int>(NInt)
}
impl Compilable for uint {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_nuint, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<uint>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_nuint)
		}
	}
}
#[test]
fn test_compile_uint() {
	test_compile::<uint>(NUInt)
}
impl Compilable for i32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_int, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_int)
		}
	}
}
#[test]
fn test_compile_i32() {
	test_compile::<i32>(Int)
}
impl Compilable for u32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_uint, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_uint)
		}
	}
}
#[test]
fn test_compile_u32() {
	test_compile::<u32>(UInt)
}
impl Compilable for i16 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_short, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i16>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_short)
		}
	}
}
#[test]
fn test_compile_i16() {
	test_compile::<i16>(Short)
}
impl Compilable for u16 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_ushort, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u16>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_ushort)
		}
	}
}
#[test]
fn test_compile_u16() {
	test_compile::<u16>(UShort)
}
impl Compilable for i8 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sbyte, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i8>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sbyte)
		}
	}
}
#[test]
fn test_compile_i8() {
	test_compile::<i8>(SByte)
}
impl Compilable for u8 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_ubyte, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u8>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_ubyte)
		}
	}
}
#[test]
fn test_compile_u8() {
	test_compile::<u8>(UByte)
}
impl Compilable for bool {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sys_bool, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<bool>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sys_bool)
		}
	}
}
#[test]
fn test_compile_bool() {
	test_compile::<bool>(SysBool)
}
impl Compilable for char {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sys_char, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<char>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sys_char)
		}
	}
}
#[test]
fn test_compile_char() {
	test_compile::<char>(SysChar)
}
impl<'t> Compilable for &'t str {
	fn compile(&self, func:&Function) -> Value {
		let cstring_t = Compilable::jit_type(None::<&'t str>);
		let strlen_i = (self.len() as i32).compile(func);
		let bufptr = Value::new(func, &cstring_t);
		func.insn_store(&bufptr, &func.insn_alloca(&strlen_i));
		for i in range(0, self.len()) {
			let char_i = self.char_at(i).compile(func);
			func.insn_store_relative(&bufptr, i as int, &char_i);
		}
		let null_term = '\0'.compile(func);
		func.insn_store_relative(&bufptr, self.len() as int, &null_term);
		bufptr
	}
	#[inline]
	fn jit_type(_:Option<&'t str>) -> Type {
		unsafe {
			Type::create_pointer(&NativeRef::from_ptr(jit_type_sys_char))
		}
	}
}
impl Compilable for String {
	fn compile(&self, func:&Function) -> Value {
		self.as_slice().compile(func)
	}
	#[inline]
	fn jit_type(_:Option<String>) -> Type {
		unsafe {
			Type::create_pointer(&NativeRef::from_ptr(jit_type_sys_char))
		}
	}
}
impl<T:Compilable> Compilable for *T {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&self.to_uint().compile(func), &::get_type::<T>(), false)
	}
	#[inline]
	fn jit_type(_:Option<*T>) -> Type {
		unsafe {
			Type::create_pointer(&::get_type::<T>())
		}
	}
}
impl<R:Compilable> Compilable for fn() -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn() -> R).to_uint().compile(func), &::get_type::<fn() -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn() -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [])
	}
}
impl<A:Compilable, R:Compilable> Compilable for fn(A) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A) -> R).to_uint().compile(func), &::get_type::<fn(A) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>()])
	}
}
impl<A:Compilable, B:Compilable, R:Compilable> Compilable for fn(A, B) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A, B) -> R).to_uint().compile(func), &::get_type::<fn(A, B) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A, B) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>(), &::get_type::<B>()])
	}
}
impl<A:Compilable, B:Compilable, C:Compilable, R:Compilable> Compilable for fn(A, B, C) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A, B, C) -> R).to_uint().compile(func), &::get_type::<fn(A, B, C) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A, B, C) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>(), &::get_type::<B>(), &::get_type::<C>()])
	}
}<commit_msg>Remove unnecessary unsafe block<commit_after>use bindings::{
	jit_nint,

	jit_type_void_ptr,
	jit_type_ubyte,
	jit_type_sbyte,
	jit_type_ushort,
	jit_type_short,
	jit_type_uint,
	jit_type_int,
	jit_type_nuint,
	jit_type_nint,
	jit_type_float32,
	jit_type_float64,
	jit_type_sys_bool,
	jit_type_sys_char,

	jit_value_create_long_constant,
	jit_value_create_nint_constant,
	jit_value_create_float64_constant,
	jit_value_create_float32_constant
};
use function::{
	CDECL,
	Function
};
use value::Value;
use types::{
	Type,
	TypeKind,
	Void,
	Float32,
	Float64,
	NInt,
	NUInt,
	Int,
	UInt,
	Short,
	UShort,
	SByte,
	UByte,
	SysBool,
	SysChar
};
use util::NativeRef;
use std::default::Default;
/// A type that can be compiled into a LibJIT representation
pub trait Compilable {
	/// Get a JIT representation of this value
	fn compile(&self, func:&Function) -> Value;
	/// Get the JIT type repr of the value
	fn jit_type(_:Option<Self>) -> Type;
}
#[cfg(test)]
fn test_compile<T:Compilable+Default>(kind:TypeKind) {
	::with_empty_func(|_, func| {
		let pval:T = Default::default();
		let val = pval.compile(func);
		assert!(val.get_type().get_kind().contains(kind));
	})
}
impl Compilable for () {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_void_ptr, 0))
		}
	}
	#[inline]
	fn jit_type(_:Option<()>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_void_ptr)
		}
	}
}
#[test]
fn test_compile_unit() {
	test_compile::<()>(Void)
}
impl Compilable for f64 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_float64_constant(func.as_ptr(), jit_type_float64, *self) )
		}
	}
	#[inline]
	fn jit_type(_:Option<f64>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_float64)
		}
	}
}
#[test]
fn test_compile_f64() {
	test_compile::<f64>(Float64)
}
impl Compilable for f32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_float32_constant(func.as_ptr(), jit_type_float32, *self) )
		}
	}
	#[inline]
	fn jit_type(_:Option<f32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_float32)
		}
	}
}
#[test]
fn test_compile_f32() {
	test_compile::<f32>(Float32)
}
impl Compilable for int {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_long_constant(func.as_ptr(), jit_type_nint, *self as i64) )
		}
	}
	#[inline]
	fn jit_type(_:Option<int>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_nint)
		}
	}
}
#[test]
fn test_compile_int() {
	test_compile::<int>(NInt)
}
impl Compilable for uint {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_nuint, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<uint>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_nuint)
		}
	}
}
#[test]
fn test_compile_uint() {
	test_compile::<uint>(NUInt)
}
impl Compilable for i32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_int, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_int)
		}
	}
}
#[test]
fn test_compile_i32() {
	test_compile::<i32>(Int)
}
impl Compilable for u32 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_uint, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u32>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_uint)
		}
	}
}
#[test]
fn test_compile_u32() {
	test_compile::<u32>(UInt)
}
impl Compilable for i16 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_short, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i16>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_short)
		}
	}
}
#[test]
fn test_compile_i16() {
	test_compile::<i16>(Short)
}
impl Compilable for u16 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_ushort, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u16>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_ushort)
		}
	}
}
#[test]
fn test_compile_u16() {
	test_compile::<u16>(UShort)
}
impl Compilable for i8 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sbyte, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<i8>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sbyte)
		}
	}
}
#[test]
fn test_compile_i8() {
	test_compile::<i8>(SByte)
}
impl Compilable for u8 {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_ubyte, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<u8>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_ubyte)
		}
	}
}
#[test]
fn test_compile_u8() {
	test_compile::<u8>(UByte)
}
impl Compilable for bool {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sys_bool, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<bool>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sys_bool)
		}
	}
}
#[test]
fn test_compile_bool() {
	test_compile::<bool>(SysBool)
}
impl Compilable for char {
	fn compile(&self, func:&Function) -> Value {
		unsafe {
			NativeRef::from_ptr(jit_value_create_nint_constant(func.as_ptr(), jit_type_sys_char, *self as jit_nint) )
		}
	}
	#[inline]
	fn jit_type(_:Option<char>) -> Type {
		unsafe {
			NativeRef::from_ptr(jit_type_sys_char)
		}
	}
}
#[test]
fn test_compile_char() {
	test_compile::<char>(SysChar)
}
impl<'t> Compilable for &'t str {
	fn compile(&self, func:&Function) -> Value {
		let cstring_t = Compilable::jit_type(None::<&'t str>);
		let strlen_i = (self.len() as i32).compile(func);
		let bufptr = Value::new(func, &cstring_t);
		func.insn_store(&bufptr, &func.insn_alloca(&strlen_i));
		for i in range(0, self.len()) {
			let char_i = self.char_at(i).compile(func);
			func.insn_store_relative(&bufptr, i as int, &char_i);
		}
		let null_term = '\0'.compile(func);
		func.insn_store_relative(&bufptr, self.len() as int, &null_term);
		bufptr
	}
	#[inline]
	fn jit_type(_:Option<&'t str>) -> Type {
		unsafe {
			Type::create_pointer(&NativeRef::from_ptr(jit_type_sys_char))
		}
	}
}
impl Compilable for String {
	fn compile(&self, func:&Function) -> Value {
		self.as_slice().compile(func)
	}
	#[inline]
	fn jit_type(_:Option<String>) -> Type {
		unsafe {
			Type::create_pointer(&NativeRef::from_ptr(jit_type_sys_char))
		}
	}
}
impl<T:Compilable> Compilable for *T {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&self.to_uint().compile(func), &::get_type::<T>(), false)
	}
	#[inline]
	fn jit_type(_:Option<*T>) -> Type {
		Type::create_pointer(&::get_type::<T>())
	}
}
impl<R:Compilable> Compilable for fn() -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn() -> R).to_uint().compile(func), &::get_type::<fn() -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn() -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [])
	}
}
impl<A:Compilable, R:Compilable> Compilable for fn(A) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A) -> R).to_uint().compile(func), &::get_type::<fn(A) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>()])
	}
}
impl<A:Compilable, B:Compilable, R:Compilable> Compilable for fn(A, B) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A, B) -> R).to_uint().compile(func), &::get_type::<fn(A, B) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A, B) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>(), &::get_type::<B>()])
	}
}
impl<A:Compilable, B:Compilable, C:Compilable, R:Compilable> Compilable for fn(A, B, C) -> R {
	fn compile(&self, func:&Function) -> Value {
		func.insn_convert(&(self as *fn(A, B, C) -> R).to_uint().compile(func), &::get_type::<fn(A, B, C) -> R>(), false)
	}
	#[inline]
	fn jit_type(_:Option<fn(A, B, C) -> R>) -> Type {
		Type::create_signature(CDECL, &::get_type::<R>(), &mut [&::get_type::<A>(), &::get_type::<B>(), &::get_type::<C>()])
	}
}<|endoftext|>


<commit_before>use result::Result;
use std::getopts;
use std::cell::Cell;

/// The type of document to output
pub enum OutputFormat {
    /// Markdown
    pub Markdown,
    /// HTML, via markdown and pandoc
    pub PandocHtml
}

impl OutputFormat : cmp::Eq {
    #[cfg(stage0)]
    pure fn eq(other: &OutputFormat) -> bool {
        (self as uint) == ((*other) as uint)
    }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn eq(&self, other: &OutputFormat) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    #[cfg(stage0)]
    pure fn ne(other: &OutputFormat) -> bool { !self.eq(other) }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn ne(&self, other: &OutputFormat) -> bool { !(*self).eq(other) }
}

/// How to organize the output
pub enum OutputStyle {
    /// All in a single document
    pub DocPerCrate,
    /// Each module in its own document
    pub DocPerMod
}

impl OutputStyle : cmp::Eq {
    #[cfg(stage0)]
    pure fn eq(other: &OutputStyle) -> bool {
        (self as uint) == ((*other) as uint)
    }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn eq(&self, other: &OutputStyle) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    #[cfg(stage0)]
    pure fn ne(other: &OutputStyle) -> bool { !self.eq(other) }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn ne(&self, other: &OutputStyle) -> bool { !(*self).eq(other) }
}

/// The configuration for a rustdoc session
pub type Config = {
    input_crate: Path,
    output_dir: Path,
    output_format: OutputFormat,
    output_style: OutputStyle,
    pandoc_cmd: Option<~str>
};

fn opt_output_dir() -> ~str { ~"output-dir" }
fn opt_output_format() -> ~str { ~"output-format" }
fn opt_output_style() -> ~str { ~"output-style" }
fn opt_pandoc_cmd() -> ~str { ~"pandoc-cmd" }
fn opt_help() -> ~str { ~"h" }

fn opts() -> ~[(getopts::Opt, ~str)] {
    ~[
        (getopts::optopt(opt_output_dir()),
         ~"--output-dir <val>     put documents here"),
        (getopts::optopt(opt_output_format()),
         ~"--output-format <val>  either 'markdown' or 'html'"),
        (getopts::optopt(opt_output_style()),
         ~"--output-style <val>   either 'doc-per-crate' or 'doc-per-mod'"),
        (getopts::optopt(opt_pandoc_cmd()),
         ~"--pandoc-cmd <val>     the command for running pandoc"),
        (getopts::optflag(opt_help()),
         ~"-h                     print help")
    ]
}

pub fn usage() {
    use io::println;

    println(~"Usage: rustdoc [options] <cratefile>\n");
    println(~"Options:\n");
    for opts().each |opt| {
        println(fmt!("    %s", opt.second()));
    }
    println(~"");
}

pub fn default_config(input_crate: &Path) -> Config {
    {
        input_crate: *input_crate,
        output_dir: Path("."),
        output_format: PandocHtml,
        output_style: DocPerMod,
        pandoc_cmd: None
    }
}

type ProgramOutput = fn~((&str), (&[~str])) ->
    {status: int, out: ~str, err: ~str};

fn mock_program_output(_prog: &str, _args: &[~str]) -> {
    status: int, out: ~str, err: ~str
} {
    {
        status: 0,
        out: ~"",
        err: ~""
    }
}

fn parse_config(args: &[~str]) -> Result<Config, ~str> {
    parse_config_(args, run::program_output)
}

fn parse_config_(
    args: &[~str],
    +program_output: ProgramOutput
) -> Result<Config, ~str> {
    let args = vec::tail(args);
    let opts = vec::unzip(opts()).first();
    match getopts::getopts(args, opts) {
        result::Ok(matches) => {
            if vec::len(matches.free) == 1u {
                let input_crate = Path(vec::head(matches.free));
                config_from_opts(&input_crate, matches, move program_output)
            } else if vec::is_empty(matches.free) {
                result::Err(~"no crates specified")
            } else {
                result::Err(~"multiple crates specified")
            }
        }
        result::Err(f) => {
            result::Err(getopts::fail_str(f))
        }
    }
}

fn config_from_opts(
    input_crate: &Path,
    +matches: getopts::Matches,
    +program_output: ProgramOutput
) -> Result<Config, ~str> {

    let config = default_config(input_crate);
    let result = result::Ok(config);
    let result = do result::chain(result) |config| {
        let output_dir = getopts::opt_maybe_str(matches, opt_output_dir());
        let output_dir = output_dir.map(|s| Path(*s));
        result::Ok({
            output_dir: output_dir.get_default(config.output_dir),
            .. config
        })
    };
    let result = do result::chain(result) |config| {
        let output_format = getopts::opt_maybe_str(
            matches, opt_output_format());
        do output_format.map_default(result::Ok(config))
            |output_format| {
            do result::chain(parse_output_format(*output_format))
                |output_format| {

                result::Ok({
                    output_format: output_format,
                    .. config
                })
            }
        }
    };
    let result = do result::chain(result) |config| {
        let output_style =
            getopts::opt_maybe_str(matches, opt_output_style());
        do output_style.map_default(result::Ok(config))
            |output_style| {
            do result::chain(parse_output_style(*output_style))
                |output_style| {
                result::Ok({
                    output_style: output_style,
                    .. config
                })
            }
        }
    };
    let program_output = Cell(move program_output);
    let result = do result::chain(result) |config| {
        let pandoc_cmd = getopts::opt_maybe_str(matches, opt_pandoc_cmd());
        let pandoc_cmd = maybe_find_pandoc(
            &config, pandoc_cmd, move program_output.take());
        do result::chain(pandoc_cmd) |pandoc_cmd| {
            result::Ok({
                pandoc_cmd: pandoc_cmd,
                .. config
            })
        }
    };
    return result;
}

fn parse_output_format(output_format: &str) -> Result<OutputFormat, ~str> {
    match output_format.to_str() {
      ~"markdown" => result::Ok(Markdown),
      ~"html" => result::Ok(PandocHtml),
      _ => result::Err(fmt!("unknown output format '%s'", output_format))
    }
}

fn parse_output_style(output_style: &str) -> Result<OutputStyle, ~str> {
    match output_style.to_str() {
      ~"doc-per-crate" => result::Ok(DocPerCrate),
      ~"doc-per-mod" => result::Ok(DocPerMod),
      _ => result::Err(fmt!("unknown output style '%s'", output_style))
    }
}

fn maybe_find_pandoc(
    config: &Config,
    +maybe_pandoc_cmd: Option<~str>,
    +program_output: ProgramOutput
) -> Result<Option<~str>, ~str> {
    if config.output_format != PandocHtml {
        return result::Ok(maybe_pandoc_cmd);
    }

    let possible_pandocs = match maybe_pandoc_cmd {
      Some(pandoc_cmd) => ~[pandoc_cmd],
      None => {
        ~[~"pandoc"] + match os::homedir() {
          Some(dir) => {
            ~[dir.push_rel(&Path(".cabal/bin/pandoc")).to_str()]
          }
          None => ~[]
        }
      }
    };

    let pandoc = do vec::find(possible_pandocs) |pandoc| {
        let output = program_output(*pandoc, ~[~"--version"]);
        debug!("testing pandoc cmd %s: %?", *pandoc, output);
        output.status == 0
    };

    if pandoc.is_some() {
        result::Ok(pandoc)
    } else {
        result::Err(~"couldn't find pandoc")
    }
}

#[test]
fn should_find_pandoc() {
    let config = {
        output_format: PandocHtml,
        .. default_config(&Path("test"))
    };
    let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
        status: int, out: ~str, err: ~str
    } {
        {
            status: 0, out: ~"pandoc 1.8.2.1", err: ~""
        }
    };
    let result = maybe_find_pandoc(&config, None, move mock_program_output);
    assert result == result::Ok(Some(~"pandoc"));
}

#[test]
fn should_error_with_no_pandoc() {
    let config = {
        output_format: PandocHtml,
        .. default_config(&Path("test"))
    };
    let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
        status: int, out: ~str, err: ~str
    } {
        {
            status: 1, out: ~"", err: ~""
        }
    };
    let result = maybe_find_pandoc(&config, None, move mock_program_output);
    assert result == result::Err(~"couldn't find pandoc");
}

#[cfg(test)]
mod test {
    #[legacy_exports];
    fn parse_config(args: &[~str]) -> Result<Config, ~str> {
        parse_config_(args, mock_program_output)
    }
}

#[test]
fn should_error_with_no_crates() {
    let config = test::parse_config(~[~"rustdoc"]);
    assert config.get_err() == ~"no crates specified";
}

#[test]
fn should_error_with_multiple_crates() {
    let config =
        test::parse_config(~[~"rustdoc", ~"crate1.rc", ~"crate2.rc"]);
    assert config.get_err() == ~"multiple crates specified";
}

#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_dir == Path(".");
}

#[test]
fn should_set_output_dir_if_provided() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-dir", ~"snuggles"
    ]);
    assert config.get().output_dir == Path("snuggles");
}

#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_format == PandocHtml;
}

#[test]
fn should_set_output_format_to_markdown_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"markdown"
    ]);
    assert config.get().output_format == Markdown;
}

#[test]
fn should_set_output_format_to_pandoc_html_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"html"
    ]);
    assert config.get().output_format == PandocHtml;
}

#[test]
fn should_error_on_bogus_format() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"bogus"
    ]);
    assert config.get_err() == ~"unknown output format 'bogus'";
}

#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_style == DocPerMod;
}

#[test]
fn should_set_output_style_to_one_doc_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-crate"
    ]);
    assert config.get().output_style == DocPerCrate;
}

#[test]
fn should_set_output_style_to_doc_per_mod_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-mod"
    ]);
    assert config.get().output_style == DocPerMod;
}

#[test]
fn should_error_on_bogus_output_style() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"bogus"
    ]);
    assert config.get_err() == ~"unknown output style 'bogus'";
}

#[test]
fn should_set_pandoc_command_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--pandoc-cmd", ~"panda-bear-doc"
    ]);
    assert config.get().pandoc_cmd == Some(~"panda-bear-doc");
}

#[test]
fn should_set_pandoc_command_when_using_pandoc() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().pandoc_cmd == Some(~"pandoc");
}
<commit_msg>rustdoc: Fix build error<commit_after>use result::Result;
use std::getopts;
use std::cell::Cell;

/// The type of document to output
pub enum OutputFormat {
    /// Markdown
    pub Markdown,
    /// HTML, via markdown and pandoc
    pub PandocHtml
}

impl OutputFormat : cmp::Eq {
    #[cfg(stage0)]
    pure fn eq(other: &OutputFormat) -> bool {
        (self as uint) == ((*other) as uint)
    }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn eq(&self, other: &OutputFormat) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    #[cfg(stage0)]
    pure fn ne(other: &OutputFormat) -> bool { !self.eq(other) }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn ne(&self, other: &OutputFormat) -> bool { !(*self).eq(other) }
}

/// How to organize the output
pub enum OutputStyle {
    /// All in a single document
    pub DocPerCrate,
    /// Each module in its own document
    pub DocPerMod
}

impl OutputStyle : cmp::Eq {
    #[cfg(stage0)]
    pure fn eq(other: &OutputStyle) -> bool {
        (self as uint) == ((*other) as uint)
    }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn eq(&self, other: &OutputStyle) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    #[cfg(stage0)]
    pure fn ne(other: &OutputStyle) -> bool { !self.eq(other) }
    #[cfg(stage1)]
    #[cfg(stage2)]
    pure fn ne(&self, other: &OutputStyle) -> bool { !(*self).eq(other) }
}

/// The configuration for a rustdoc session
pub type Config = {
    input_crate: Path,
    output_dir: Path,
    output_format: OutputFormat,
    output_style: OutputStyle,
    pandoc_cmd: Option<~str>
};

fn opt_output_dir() -> ~str { ~"output-dir" }
fn opt_output_format() -> ~str { ~"output-format" }
fn opt_output_style() -> ~str { ~"output-style" }
fn opt_pandoc_cmd() -> ~str { ~"pandoc-cmd" }
fn opt_help() -> ~str { ~"h" }

fn opts() -> ~[(getopts::Opt, ~str)] {
    ~[
        (getopts::optopt(opt_output_dir()),
         ~"--output-dir <val>     put documents here"),
        (getopts::optopt(opt_output_format()),
         ~"--output-format <val>  either 'markdown' or 'html'"),
        (getopts::optopt(opt_output_style()),
         ~"--output-style <val>   either 'doc-per-crate' or 'doc-per-mod'"),
        (getopts::optopt(opt_pandoc_cmd()),
         ~"--pandoc-cmd <val>     the command for running pandoc"),
        (getopts::optflag(opt_help()),
         ~"-h                     print help")
    ]
}

pub fn usage() {
    use io::println;

    println(~"Usage: rustdoc [options] <cratefile>\n");
    println(~"Options:\n");
    for opts().each |opt| {
        println(fmt!("    %s", opt.second()));
    }
    println(~"");
}

pub fn default_config(input_crate: &Path) -> Config {
    {
        input_crate: *input_crate,
        output_dir: Path("."),
        output_format: PandocHtml,
        output_style: DocPerMod,
        pandoc_cmd: None
    }
}

type ProgramOutput = fn~((&str), (&[~str])) ->
    {status: int, out: ~str, err: ~str};

fn mock_program_output(_prog: &str, _args: &[~str]) -> {
    status: int, out: ~str, err: ~str
} {
    {
        status: 0,
        out: ~"",
        err: ~""
    }
}

pub fn parse_config(args: &[~str]) -> Result<Config, ~str> {
    parse_config_(args, run::program_output)
}

fn parse_config_(
    args: &[~str],
    +program_output: ProgramOutput
) -> Result<Config, ~str> {
    let args = vec::tail(args);
    let opts = vec::unzip(opts()).first();
    match getopts::getopts(args, opts) {
        result::Ok(matches) => {
            if vec::len(matches.free) == 1u {
                let input_crate = Path(vec::head(matches.free));
                config_from_opts(&input_crate, matches, move program_output)
            } else if vec::is_empty(matches.free) {
                result::Err(~"no crates specified")
            } else {
                result::Err(~"multiple crates specified")
            }
        }
        result::Err(f) => {
            result::Err(getopts::fail_str(f))
        }
    }
}

fn config_from_opts(
    input_crate: &Path,
    +matches: getopts::Matches,
    +program_output: ProgramOutput
) -> Result<Config, ~str> {

    let config = default_config(input_crate);
    let result = result::Ok(config);
    let result = do result::chain(result) |config| {
        let output_dir = getopts::opt_maybe_str(matches, opt_output_dir());
        let output_dir = output_dir.map(|s| Path(*s));
        result::Ok({
            output_dir: output_dir.get_default(config.output_dir),
            .. config
        })
    };
    let result = do result::chain(result) |config| {
        let output_format = getopts::opt_maybe_str(
            matches, opt_output_format());
        do output_format.map_default(result::Ok(config))
            |output_format| {
            do result::chain(parse_output_format(*output_format))
                |output_format| {

                result::Ok({
                    output_format: output_format,
                    .. config
                })
            }
        }
    };
    let result = do result::chain(result) |config| {
        let output_style =
            getopts::opt_maybe_str(matches, opt_output_style());
        do output_style.map_default(result::Ok(config))
            |output_style| {
            do result::chain(parse_output_style(*output_style))
                |output_style| {
                result::Ok({
                    output_style: output_style,
                    .. config
                })
            }
        }
    };
    let program_output = Cell(move program_output);
    let result = do result::chain(result) |config| {
        let pandoc_cmd = getopts::opt_maybe_str(matches, opt_pandoc_cmd());
        let pandoc_cmd = maybe_find_pandoc(
            &config, pandoc_cmd, move program_output.take());
        do result::chain(pandoc_cmd) |pandoc_cmd| {
            result::Ok({
                pandoc_cmd: pandoc_cmd,
                .. config
            })
        }
    };
    return result;
}

fn parse_output_format(output_format: &str) -> Result<OutputFormat, ~str> {
    match output_format.to_str() {
      ~"markdown" => result::Ok(Markdown),
      ~"html" => result::Ok(PandocHtml),
      _ => result::Err(fmt!("unknown output format '%s'", output_format))
    }
}

fn parse_output_style(output_style: &str) -> Result<OutputStyle, ~str> {
    match output_style.to_str() {
      ~"doc-per-crate" => result::Ok(DocPerCrate),
      ~"doc-per-mod" => result::Ok(DocPerMod),
      _ => result::Err(fmt!("unknown output style '%s'", output_style))
    }
}

fn maybe_find_pandoc(
    config: &Config,
    +maybe_pandoc_cmd: Option<~str>,
    +program_output: ProgramOutput
) -> Result<Option<~str>, ~str> {
    if config.output_format != PandocHtml {
        return result::Ok(maybe_pandoc_cmd);
    }

    let possible_pandocs = match maybe_pandoc_cmd {
      Some(pandoc_cmd) => ~[pandoc_cmd],
      None => {
        ~[~"pandoc"] + match os::homedir() {
          Some(dir) => {
            ~[dir.push_rel(&Path(".cabal/bin/pandoc")).to_str()]
          }
          None => ~[]
        }
      }
    };

    let pandoc = do vec::find(possible_pandocs) |pandoc| {
        let output = program_output(*pandoc, ~[~"--version"]);
        debug!("testing pandoc cmd %s: %?", *pandoc, output);
        output.status == 0
    };

    if pandoc.is_some() {
        result::Ok(pandoc)
    } else {
        result::Err(~"couldn't find pandoc")
    }
}

#[test]
fn should_find_pandoc() {
    let config = {
        output_format: PandocHtml,
        .. default_config(&Path("test"))
    };
    let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
        status: int, out: ~str, err: ~str
    } {
        {
            status: 0, out: ~"pandoc 1.8.2.1", err: ~""
        }
    };
    let result = maybe_find_pandoc(&config, None, move mock_program_output);
    assert result == result::Ok(Some(~"pandoc"));
}

#[test]
fn should_error_with_no_pandoc() {
    let config = {
        output_format: PandocHtml,
        .. default_config(&Path("test"))
    };
    let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
        status: int, out: ~str, err: ~str
    } {
        {
            status: 1, out: ~"", err: ~""
        }
    };
    let result = maybe_find_pandoc(&config, None, move mock_program_output);
    assert result == result::Err(~"couldn't find pandoc");
}

#[cfg(test)]
mod test {
    #[legacy_exports];
    fn parse_config(args: &[~str]) -> Result<Config, ~str> {
        parse_config_(args, mock_program_output)
    }
}

#[test]
fn should_error_with_no_crates() {
    let config = test::parse_config(~[~"rustdoc"]);
    assert config.get_err() == ~"no crates specified";
}

#[test]
fn should_error_with_multiple_crates() {
    let config =
        test::parse_config(~[~"rustdoc", ~"crate1.rc", ~"crate2.rc"]);
    assert config.get_err() == ~"multiple crates specified";
}

#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_dir == Path(".");
}

#[test]
fn should_set_output_dir_if_provided() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-dir", ~"snuggles"
    ]);
    assert config.get().output_dir == Path("snuggles");
}

#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_format == PandocHtml;
}

#[test]
fn should_set_output_format_to_markdown_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"markdown"
    ]);
    assert config.get().output_format == Markdown;
}

#[test]
fn should_set_output_format_to_pandoc_html_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"html"
    ]);
    assert config.get().output_format == PandocHtml;
}

#[test]
fn should_error_on_bogus_format() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-format", ~"bogus"
    ]);
    assert config.get_err() == ~"unknown output format 'bogus'";
}

#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().output_style == DocPerMod;
}

#[test]
fn should_set_output_style_to_one_doc_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-crate"
    ]);
    assert config.get().output_style == DocPerCrate;
}

#[test]
fn should_set_output_style_to_doc_per_mod_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-mod"
    ]);
    assert config.get().output_style == DocPerMod;
}

#[test]
fn should_error_on_bogus_output_style() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--output-style", ~"bogus"
    ]);
    assert config.get_err() == ~"unknown output style 'bogus'";
}

#[test]
fn should_set_pandoc_command_if_requested() {
    let config = test::parse_config(~[
        ~"rustdoc", ~"crate.rc", ~"--pandoc-cmd", ~"panda-bear-doc"
    ]);
    assert config.get().pandoc_cmd == Some(~"panda-bear-doc");
}

#[test]
fn should_set_pandoc_command_when_using_pandoc() {
    let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
    assert config.get().pandoc_cmd == Some(~"pandoc");
}
<|endoftext|>


<commit_before>use byteorder::{ReadBytesExt, LittleEndian};
use encoding::all::UTF_16LE;
use encoding::{Encoding, DecoderTrap};
use lnkpkg::errors::{LnkError};
use serde::{ser};
use std::io::Read;
use std::io::Error;
use std::slice;
use std::fmt;

pub struct ByteArray(pub Vec<u8>);
impl fmt::Debug for ByteArray {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{:?}",
            to_hex_string(&self.0),
        )
    }
}
impl ser::Serialize for ByteArray {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: ser::Serializer
    {
        serializer.serialize_str(&format!("{}", to_hex_string(&self.0)))
    }
}

pub fn to_hex_string(bytes: &Vec<u8>) -> String {
    let strs: Vec<String> = bytes.iter()
        .map(|b| format!("{:02X}", b))
        .collect();
    strs.join("")
}

pub fn read_string_u8_w_size<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string with the size preceeding the string
    let str_size = reader.read_u8()?;

    let mut utf8_buffer = vec![0;str_size as usize];
    reader.read_exact(utf8_buffer.as_mut_slice())?;

    Ok(
        String::from_utf8(utf8_buffer)?
    )
}

pub fn read_string_u16_w_size<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string with the size preceeding the string
    // multiply size by two (wchar = 2 bytes)
    let str_size = reader.read_u16::<LittleEndian>()? * 2;

    let mut utf16_buffer = vec![0;str_size as usize];
    reader.read_exact(utf16_buffer.as_mut_slice())?;

    let utf16_string = match UTF_16LE.decode(&utf16_buffer.as_slice(),DecoderTrap::Ignore) {
        Ok(utf16) => utf16,
        Err(error) => return Err(
            LnkError::utf16_decode_error(
                format!("Error decoding name in volume_label_unicode field. [{}]",error)
            )
        )
    };

    Ok(utf16_string)
}

pub fn read_string_u8_till_null<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string till a null char is reached
    let utf8_buffer = get_u8_vec(&mut reader)?;
    Ok(
        String::from_utf8(utf8_buffer)?
    )
}

pub fn read_string_u16_till_null<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string till a null char is reached
    let utf16_buffer = get_u8_vec_utf16(&mut reader)?;
    let utf16_string = match UTF_16LE.decode(&utf16_buffer.as_slice(),DecoderTrap::Ignore) {
        Ok(utf16) => utf16,
        Err(error) => return Err(
            LnkError::utf16_decode_error(
                format!("Error decoding name in volume_label_unicode field. [{}]",error)
            )
        )
    };
    Ok(utf16_string)
}

pub fn get_u8_vec<R: Read>(mut reader: R) -> Result<Vec<u8>,Error> {
    let mut string_vec: Vec<u8> = Vec::new();

    loop {
        let u8_char = reader.read_u8()?;
        if u8_char == 0x00 {
            break
        } else {
            string_vec.push(u8_char);
        }
    }

    Ok(string_vec)
}

pub fn get_u8_vec_utf16<R: Read>(mut reader: R) -> Result<Vec<u8>,Error>{
    let mut u16_vec: Vec<u16> = Vec::new();

    loop {
        let u16_char = reader.read_u16::<LittleEndian>()?;
        if u16_char == 0x0000 {
            break
        } else {
            u16_vec.push(u16_char);
        }
    }

    let u8_slice: &[u8] = unsafe {
        slice::from_raw_parts(u16_vec.as_ptr() as *const u8, u16_vec.len() * 2)
    };

    Ok(u8_slice.to_vec())
}

pub fn get_u16_vec<R: Read>(mut reader: R) -> Result<Vec<u16>,Error> {
    let mut string_vec: Vec<u16> = Vec::new();

    loop {
        let u16_char = reader.read_u16::<LittleEndian>()?;
        if u16_char == 0x0000 {
            break
        } else {
            string_vec.push(u16_char);
        }
    }

    Ok(string_vec)
}
<commit_msg>Added method to get the size of a reader<commit_after>use byteorder::{ReadBytesExt, LittleEndian};
use encoding::all::UTF_16LE;
use encoding::{Encoding, DecoderTrap};
use lnkpkg::errors::{LnkError};
use serde::{ser};
use std::io::Read;
use std::io::{Seek,SeekFrom};
use std::io::Error;
use std::slice;
use std::fmt;

pub fn get_reader_size<Rs: Read+Seek>(mut reader: Rs) -> Result<u64,LnkError>{
    // Seek to end to get offset
    let end_offset = reader.seek(
        SeekFrom::End(0)
    )?;
    // Seek back to start of reader
    reader.seek(
        SeekFrom::Start(0)
    )?;

    Ok(end_offset)
}

pub struct ByteArray(pub Vec<u8>);
impl fmt::Debug for ByteArray {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{:?}",
            to_hex_string(&self.0),
        )
    }
}
impl ser::Serialize for ByteArray {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: ser::Serializer
    {
        serializer.serialize_str(&format!("{}", to_hex_string(&self.0)))
    }
}

pub fn to_hex_string(bytes: &Vec<u8>) -> String {
    let strs: Vec<String> = bytes.iter()
        .map(|b| format!("{:02X}", b))
        .collect();
    strs.join("")
}

pub fn read_string_u8_w_size<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string with the size preceeding the string
    let str_size = reader.read_u8()?;

    let mut utf8_buffer = vec![0;str_size as usize];
    reader.read_exact(utf8_buffer.as_mut_slice())?;

    Ok(
        String::from_utf8(utf8_buffer)?
    )
}

pub fn read_string_u16_w_size<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string with the size preceeding the string
    // multiply size by two (wchar = 2 bytes)
    let str_size = reader.read_u16::<LittleEndian>()? * 2;

    let mut utf16_buffer = vec![0;str_size as usize];
    reader.read_exact(utf16_buffer.as_mut_slice())?;

    let utf16_string = match UTF_16LE.decode(&utf16_buffer.as_slice(),DecoderTrap::Ignore) {
        Ok(utf16) => utf16,
        Err(error) => return Err(
            LnkError::utf16_decode_error(
                format!("Error decoding name in volume_label_unicode field. [{}]",error)
            )
        )
    };

    Ok(utf16_string)
}

pub fn read_string_u8_till_null<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string till a null char is reached
    let utf8_buffer = get_u8_vec(&mut reader)?;
    Ok(
        String::from_utf8(utf8_buffer)?
    )
}

pub fn read_string_u16_till_null<R: Read>(mut reader: R) -> Result<String,LnkError> {
    // Reads into a string till a null char is reached
    let utf16_buffer = get_u8_vec_utf16(&mut reader)?;
    let utf16_string = match UTF_16LE.decode(&utf16_buffer.as_slice(),DecoderTrap::Ignore) {
        Ok(utf16) => utf16,
        Err(error) => return Err(
            LnkError::utf16_decode_error(
                format!("Error decoding name in volume_label_unicode field. [{}]",error)
            )
        )
    };
    Ok(utf16_string)
}

pub fn get_u8_vec<R: Read>(mut reader: R) -> Result<Vec<u8>,Error> {
    let mut string_vec: Vec<u8> = Vec::new();

    loop {
        let u8_char = reader.read_u8()?;
        if u8_char == 0x00 {
            break
        } else {
            string_vec.push(u8_char);
        }
    }

    Ok(string_vec)
}

pub fn get_u8_vec_utf16<R: Read>(mut reader: R) -> Result<Vec<u8>,Error>{
    let mut u16_vec: Vec<u16> = Vec::new();

    loop {
        let u16_char = reader.read_u16::<LittleEndian>()?;
        if u16_char == 0x0000 {
            break
        } else {
            u16_vec.push(u16_char);
        }
    }

    let u8_slice: &[u8] = unsafe {
        slice::from_raw_parts(u16_vec.as_ptr() as *const u8, u16_vec.len() * 2)
    };

    Ok(u8_slice.to_vec())
}

pub fn get_u16_vec<R: Read>(mut reader: R) -> Result<Vec<u16>,Error> {
    let mut string_vec: Vec<u16> = Vec::new();

    loop {
        let u16_char = reader.read_u16::<LittleEndian>()?;
        if u16_char == 0x0000 {
            break
        } else {
            string_vec.push(u16_char);
        }
    }

    Ok(string_vec)
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Virgile Andreani <virgile.andreani@anbuco.fr>
// (c) kwantam <kwantam@gmail.com>
//     * 2015-04-28 ~ updated to work with both UTF-8 and non-UTF-8 encodings
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg, ArgMatches};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;

static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
 With no FILE, or when FILE is -, read standard input.";

pub mod options {
    pub static TABS: &str = "tabs";
    pub static INITIAL: &str = "initial";
    pub static NO_UTF8: &str = "no-utf8";
    pub static FILES: &str = "FILES";
}

static LONG_HELP: &str = "";

static DEFAULT_TABSTOP: usize = 8;

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

/// The mode to use when replacing tabs beyond the last one specified in
/// the `--tabs` argument.
enum RemainingMode {
    None,
    Slash,
    Plus,
}

/// Decide whether the character is either a space or a comma.
///
/// # Examples
///
/// ```rust,ignore
/// assert!(is_space_or_comma(' '))
/// assert!(is_space_or_comma(','))
/// assert!(!is_space_or_comma('a'))
/// ```
fn is_space_or_comma(c: char) -> bool {
    c == ' ' || c == ','
}

/// Parse a list of tabstops from a `--tabs` argument.
///
/// This function returns both the vector of numbers appearing in the
/// comma- or space-separated list, and also an optional mode, specified
/// by either a "/" or a "+" character appearing before the final number
/// in the list. This mode defines the strategy to use for computing the
/// number of spaces to use for columns beyond the end of the tab stop
/// list specified here.
fn tabstops_parse(s: String) -> (RemainingMode, Vec<usize>) {
    // Leading commas and spaces are ignored.
    let s = s.trim_start_matches(is_space_or_comma);

    // If there were only commas and spaces in the string, just use the
    // default tabstops.
    if s.is_empty() {
        return (RemainingMode::None, vec![DEFAULT_TABSTOP]);
    }

    let mut nums = vec![];
    let mut remaining_mode = RemainingMode::None;
    for word in s.split(is_space_or_comma) {
        let bytes = word.as_bytes();
        for i in 0..bytes.len() {
            match bytes[i] {
                b'+' => {
                    remaining_mode = RemainingMode::Plus;
                }
                b'/' => {
                    remaining_mode = RemainingMode::Slash;
                }
                _ => {
                    // Parse a number from the byte sequence.
                    let num = from_utf8(&bytes[i..]).unwrap().parse::<usize>().unwrap();

                    // Tab size must be positive.
                    if num == 0 {
                        crash!(1, "{}\n", "tab size cannot be 0");
                    }

                    // Tab sizes must be ascending.
                    if let Some(last_stop) = nums.last() {
                        if *last_stop >= num {
                            crash!(1, "tab sizes must be ascending");
                        }
                    }

                    // Append this tab stop to the list of all tabstops.
                    nums.push(num);
                    break;
                }
            }
        }
    }
    // If no numbers could be parsed (for example, if `s` were "+,+,+"),
    // then just use the default tabstops.
    if nums.is_empty() {
        nums = vec![DEFAULT_TABSTOP];
    }
    (remaining_mode, nums)
}

struct Options {
    files: Vec<String>,
    tabstops: Vec<usize>,
    tspaces: String,
    iflag: bool,
    uflag: bool,

    /// Strategy for expanding tabs for columns beyond those specified
    /// in `tabstops`.
    remaining_mode: RemainingMode,
}

impl Options {
    fn new(matches: &ArgMatches) -> Options {
        let (remaining_mode, tabstops) = match matches.value_of(options::TABS) {
            Some(s) => tabstops_parse(s.to_string()),
            None => (RemainingMode::None, vec![DEFAULT_TABSTOP]),
        };

        let iflag = matches.is_present(options::INITIAL);
        let uflag = !matches.is_present(options::NO_UTF8);

        // avoid allocations when dumping out long sequences of spaces
        // by precomputing the longest string of spaces we will ever need
        let nspaces = tabstops
            .iter()
            .scan(0, |pr, &it| {
                let ret = Some(it - *pr);
                *pr = it;
                ret
            })
            .max()
            .unwrap(); // length of tabstops is guaranteed >= 1
        let tspaces = " ".repeat(nspaces);

        let files: Vec<String> = match matches.values_of(options::FILES) {
            Some(s) => s.map(|v| v.to_string()).collect(),
            None => vec!["-".to_owned()],
        };

        Options {
            files,
            tabstops,
            tspaces,
            iflag,
            uflag,
            remaining_mode,
        }
    }
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = usage();
    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    expand(Options::new(&matches));
    0
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::INITIAL)
                .long(options::INITIAL)
                .short("i")
                .help("do not convert tabs after non blanks"),
        )
        .arg(
            Arg::with_name(options::TABS)
                .long(options::TABS)
                .short("t")
                .value_name("N, LIST")
                .takes_value(true)
                .help("have tabs N characters apart, not 8 or use comma separated list of explicit tab positions"),
        )
        .arg(
            Arg::with_name(options::NO_UTF8)
                .long(options::NO_UTF8)
                .short("U")
                .help("interpret input file as 8-bit ASCII rather than UTF-8"),
        ).arg(
            Arg::with_name(options::FILES)
                .multiple(true)
                .hidden(true)
                .takes_value(true)
        )
}

fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
    let file_buf;
    if path == "-" {
        BufReader::new(Box::new(stdin()) as Box<dyn Read>)
    } else {
        file_buf = match File::open(&path[..]) {
            Ok(a) => a,
            Err(e) => crash!(1, "{}: {}\n", path.maybe_quote(), e),
        };
        BufReader::new(Box::new(file_buf) as Box<dyn Read>)
    }
}

/// Compute the number of spaces to the next tabstop.
///
/// `tabstops` is the sequence of tabstop locations.
///
/// `col` is the index of the current cursor in the line being written.
///
/// If `remaining_mode` is [`RemainingMode::Plus`], then the last entry
/// in the `tabstops` slice is interpreted as a relative number of
/// spaces, which this function will return for every input value of
/// `col` beyond the end of the second-to-last element of `tabstops`.
///
/// If `remaining_mode` is [`RemainingMode::Plus`], then the last entry
/// in the `tabstops` slice is interpreted as a relative number of
/// spaces, which this function will return for every input value of
/// `col` beyond the end of the second-to-last element of `tabstops`.
fn next_tabstop(tabstops: &[usize], col: usize, remaining_mode: &RemainingMode) -> usize {
    let num_tabstops = tabstops.len();
    match remaining_mode {
        RemainingMode::Plus => match tabstops[0..num_tabstops - 1].iter().find(|&&t| t > col) {
            Some(t) => t - col,
            None => tabstops[num_tabstops - 1] - 1,
        },
        RemainingMode::Slash => match tabstops[0..num_tabstops - 1].iter().find(|&&t| t > col) {
            Some(t) => t - col,
            None => tabstops[num_tabstops - 1] - col % tabstops[num_tabstops - 1],
        },
        RemainingMode::None => {
            if num_tabstops == 1 {
                tabstops[0] - col % tabstops[0]
            } else {
                match tabstops.iter().find(|&&t| t > col) {
                    Some(t) => t - col,
                    None => 1,
                }
            }
        }
    }
}

#[derive(PartialEq, Eq, Debug)]
enum CharType {
    Backspace,
    Tab,
    Other,
}

fn expand(options: Options) {
    use self::CharType::*;

    let mut output = BufWriter::new(stdout());
    let ts = options.tabstops.as_ref();
    let mut buf = Vec::new();

    for file in options.files.into_iter() {
        let mut fh = open(file);

        while match fh.read_until(b'\n', &mut buf) {
            Ok(s) => s > 0,
            Err(_) => buf.is_empty(),
        } {
            let mut col = 0;
            let mut byte = 0;
            let mut init = true;

            while byte < buf.len() {
                let (ctype, cwidth, nbytes) = if options.uflag {
                    let nbytes = char::from(buf[byte]).len_utf8();

                    if byte + nbytes > buf.len() {
                        // don't overrun buffer because of invalid UTF-8
                        (Other, 1, 1)
                    } else if let Ok(t) = from_utf8(&buf[byte..byte + nbytes]) {
                        match t.chars().next() {
                            Some('\t') => (Tab, 0, nbytes),
                            Some('\x08') => (Backspace, 0, nbytes),
                            Some(c) => (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes),
                            None => {
                                // no valid char at start of t, so take 1 byte
                                (Other, 1, 1)
                            }
                        }
                    } else {
                        (Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide
                    }
                } else {
                    (
                        match buf[byte] {
                            // always take exactly 1 byte in strict ASCII mode
                            0x09 => Tab,
                            0x08 => Backspace,
                            _ => Other,
                        },
                        1,
                        1,
                    )
                };

                // figure out how many columns this char takes up
                match ctype {
                    Tab => {
                        // figure out how many spaces to the next tabstop
                        let nts = next_tabstop(ts, col, &options.remaining_mode);
                        col += nts;

                        // now dump out either spaces if we're expanding, or a literal tab if we're not
                        if init || !options.iflag {
                            if nts <= options.tspaces.len() {
                                crash_if_err!(
                                    1,
                                    output.write_all(options.tspaces[..nts].as_bytes())
                                );
                            } else {
                                crash_if_err!(1, output.write_all(" ".repeat(nts).as_bytes()));
                            };
                        } else {
                            crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
                        }
                    }
                    _ => {
                        col = if ctype == Other {
                            col + cwidth
                        } else if col > 0 {
                            col - 1
                        } else {
                            0
                        };

                        // if we're writing anything other than a space, then we're
                        // done with the line's leading spaces
                        if buf[byte] != 0x20 {
                            init = false;
                        }

                        crash_if_err!(1, output.write_all(&buf[byte..byte + nbytes]));
                    }
                }

                byte += nbytes; // advance the pointer
            }

            crash_if_err!(1, output.flush());
            buf.truncate(0); // clear the buffer
        }
    }
}

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

    #[test]
    fn test_next_tabstop_remaining_mode_none() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::None), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::None), 2);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::None), 1);
    }

    #[test]
    fn test_next_tabstop_remaining_mode_plus() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::Plus), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Plus), 4);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Plus), 4);
    }

    #[test]
    fn test_next_tabstop_remaining_mode_slash() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::Slash), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Slash), 2);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Slash), 4);
    }
}
<commit_msg>expand: return UResult from uumain() function<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Virgile Andreani <virgile.andreani@anbuco.fr>
// (c) kwantam <kwantam@gmail.com>
//     * 2015-04-28 ~ updated to work with both UTF-8 and non-UTF-8 encodings
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) ctype cwidth iflag nbytes nspaces nums tspaces uflag

#[macro_use]
extern crate uucore;

use clap::{crate_version, App, Arg, ArgMatches};
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, BufWriter, Read, Write};
use std::str::from_utf8;
use unicode_width::UnicodeWidthChar;
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult};

static ABOUT: &str = "Convert tabs in each FILE to spaces, writing to standard output.
 With no FILE, or when FILE is -, read standard input.";

pub mod options {
    pub static TABS: &str = "tabs";
    pub static INITIAL: &str = "initial";
    pub static NO_UTF8: &str = "no-utf8";
    pub static FILES: &str = "FILES";
}

static LONG_HELP: &str = "";

static DEFAULT_TABSTOP: usize = 8;

fn usage() -> String {
    format!("{0} [OPTION]... [FILE]...", uucore::execution_phrase())
}

/// The mode to use when replacing tabs beyond the last one specified in
/// the `--tabs` argument.
enum RemainingMode {
    None,
    Slash,
    Plus,
}

/// Decide whether the character is either a space or a comma.
///
/// # Examples
///
/// ```rust,ignore
/// assert!(is_space_or_comma(' '))
/// assert!(is_space_or_comma(','))
/// assert!(!is_space_or_comma('a'))
/// ```
fn is_space_or_comma(c: char) -> bool {
    c == ' ' || c == ','
}

/// Parse a list of tabstops from a `--tabs` argument.
///
/// This function returns both the vector of numbers appearing in the
/// comma- or space-separated list, and also an optional mode, specified
/// by either a "/" or a "+" character appearing before the final number
/// in the list. This mode defines the strategy to use for computing the
/// number of spaces to use for columns beyond the end of the tab stop
/// list specified here.
fn tabstops_parse(s: String) -> (RemainingMode, Vec<usize>) {
    // Leading commas and spaces are ignored.
    let s = s.trim_start_matches(is_space_or_comma);

    // If there were only commas and spaces in the string, just use the
    // default tabstops.
    if s.is_empty() {
        return (RemainingMode::None, vec![DEFAULT_TABSTOP]);
    }

    let mut nums = vec![];
    let mut remaining_mode = RemainingMode::None;
    for word in s.split(is_space_or_comma) {
        let bytes = word.as_bytes();
        for i in 0..bytes.len() {
            match bytes[i] {
                b'+' => {
                    remaining_mode = RemainingMode::Plus;
                }
                b'/' => {
                    remaining_mode = RemainingMode::Slash;
                }
                _ => {
                    // Parse a number from the byte sequence.
                    let num = from_utf8(&bytes[i..]).unwrap().parse::<usize>().unwrap();

                    // Tab size must be positive.
                    if num == 0 {
                        crash!(1, "{}\n", "tab size cannot be 0");
                    }

                    // Tab sizes must be ascending.
                    if let Some(last_stop) = nums.last() {
                        if *last_stop >= num {
                            crash!(1, "tab sizes must be ascending");
                        }
                    }

                    // Append this tab stop to the list of all tabstops.
                    nums.push(num);
                    break;
                }
            }
        }
    }
    // If no numbers could be parsed (for example, if `s` were "+,+,+"),
    // then just use the default tabstops.
    if nums.is_empty() {
        nums = vec![DEFAULT_TABSTOP];
    }
    (remaining_mode, nums)
}

struct Options {
    files: Vec<String>,
    tabstops: Vec<usize>,
    tspaces: String,
    iflag: bool,
    uflag: bool,

    /// Strategy for expanding tabs for columns beyond those specified
    /// in `tabstops`.
    remaining_mode: RemainingMode,
}

impl Options {
    fn new(matches: &ArgMatches) -> Options {
        let (remaining_mode, tabstops) = match matches.value_of(options::TABS) {
            Some(s) => tabstops_parse(s.to_string()),
            None => (RemainingMode::None, vec![DEFAULT_TABSTOP]),
        };

        let iflag = matches.is_present(options::INITIAL);
        let uflag = !matches.is_present(options::NO_UTF8);

        // avoid allocations when dumping out long sequences of spaces
        // by precomputing the longest string of spaces we will ever need
        let nspaces = tabstops
            .iter()
            .scan(0, |pr, &it| {
                let ret = Some(it - *pr);
                *pr = it;
                ret
            })
            .max()
            .unwrap(); // length of tabstops is guaranteed >= 1
        let tspaces = " ".repeat(nspaces);

        let files: Vec<String> = match matches.values_of(options::FILES) {
            Some(s) => s.map(|v| v.to_string()).collect(),
            None => vec!["-".to_owned()],
        };

        Options {
            files,
            tabstops,
            tspaces,
            iflag,
            uflag,
            remaining_mode,
        }
    }
}

#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let usage = usage();
    let matches = uu_app().usage(&usage[..]).get_matches_from(args);

    expand(Options::new(&matches)).map_err_context(|| "failed to write output".to_string())
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::INITIAL)
                .long(options::INITIAL)
                .short("i")
                .help("do not convert tabs after non blanks"),
        )
        .arg(
            Arg::with_name(options::TABS)
                .long(options::TABS)
                .short("t")
                .value_name("N, LIST")
                .takes_value(true)
                .help("have tabs N characters apart, not 8 or use comma separated list of explicit tab positions"),
        )
        .arg(
            Arg::with_name(options::NO_UTF8)
                .long(options::NO_UTF8)
                .short("U")
                .help("interpret input file as 8-bit ASCII rather than UTF-8"),
        ).arg(
            Arg::with_name(options::FILES)
                .multiple(true)
                .hidden(true)
                .takes_value(true)
        )
}

fn open(path: String) -> BufReader<Box<dyn Read + 'static>> {
    let file_buf;
    if path == "-" {
        BufReader::new(Box::new(stdin()) as Box<dyn Read>)
    } else {
        file_buf = match File::open(&path[..]) {
            Ok(a) => a,
            Err(e) => crash!(1, "{}: {}\n", path.maybe_quote(), e),
        };
        BufReader::new(Box::new(file_buf) as Box<dyn Read>)
    }
}

/// Compute the number of spaces to the next tabstop.
///
/// `tabstops` is the sequence of tabstop locations.
///
/// `col` is the index of the current cursor in the line being written.
///
/// If `remaining_mode` is [`RemainingMode::Plus`], then the last entry
/// in the `tabstops` slice is interpreted as a relative number of
/// spaces, which this function will return for every input value of
/// `col` beyond the end of the second-to-last element of `tabstops`.
///
/// If `remaining_mode` is [`RemainingMode::Plus`], then the last entry
/// in the `tabstops` slice is interpreted as a relative number of
/// spaces, which this function will return for every input value of
/// `col` beyond the end of the second-to-last element of `tabstops`.
fn next_tabstop(tabstops: &[usize], col: usize, remaining_mode: &RemainingMode) -> usize {
    let num_tabstops = tabstops.len();
    match remaining_mode {
        RemainingMode::Plus => match tabstops[0..num_tabstops - 1].iter().find(|&&t| t > col) {
            Some(t) => t - col,
            None => tabstops[num_tabstops - 1] - 1,
        },
        RemainingMode::Slash => match tabstops[0..num_tabstops - 1].iter().find(|&&t| t > col) {
            Some(t) => t - col,
            None => tabstops[num_tabstops - 1] - col % tabstops[num_tabstops - 1],
        },
        RemainingMode::None => {
            if num_tabstops == 1 {
                tabstops[0] - col % tabstops[0]
            } else {
                match tabstops.iter().find(|&&t| t > col) {
                    Some(t) => t - col,
                    None => 1,
                }
            }
        }
    }
}

#[derive(PartialEq, Eq, Debug)]
enum CharType {
    Backspace,
    Tab,
    Other,
}

fn expand(options: Options) -> std::io::Result<()> {
    use self::CharType::*;

    let mut output = BufWriter::new(stdout());
    let ts = options.tabstops.as_ref();
    let mut buf = Vec::new();

    for file in options.files.into_iter() {
        let mut fh = open(file);

        while match fh.read_until(b'\n', &mut buf) {
            Ok(s) => s > 0,
            Err(_) => buf.is_empty(),
        } {
            let mut col = 0;
            let mut byte = 0;
            let mut init = true;

            while byte < buf.len() {
                let (ctype, cwidth, nbytes) = if options.uflag {
                    let nbytes = char::from(buf[byte]).len_utf8();

                    if byte + nbytes > buf.len() {
                        // don't overrun buffer because of invalid UTF-8
                        (Other, 1, 1)
                    } else if let Ok(t) = from_utf8(&buf[byte..byte + nbytes]) {
                        match t.chars().next() {
                            Some('\t') => (Tab, 0, nbytes),
                            Some('\x08') => (Backspace, 0, nbytes),
                            Some(c) => (Other, UnicodeWidthChar::width(c).unwrap_or(0), nbytes),
                            None => {
                                // no valid char at start of t, so take 1 byte
                                (Other, 1, 1)
                            }
                        }
                    } else {
                        (Other, 1, 1) // implicit assumption: non-UTF-8 char is 1 col wide
                    }
                } else {
                    (
                        match buf[byte] {
                            // always take exactly 1 byte in strict ASCII mode
                            0x09 => Tab,
                            0x08 => Backspace,
                            _ => Other,
                        },
                        1,
                        1,
                    )
                };

                // figure out how many columns this char takes up
                match ctype {
                    Tab => {
                        // figure out how many spaces to the next tabstop
                        let nts = next_tabstop(ts, col, &options.remaining_mode);
                        col += nts;

                        // now dump out either spaces if we're expanding, or a literal tab if we're not
                        if init || !options.iflag {
                            if nts <= options.tspaces.len() {
                                output.write_all(options.tspaces[..nts].as_bytes())?
                            } else {
                                output.write_all(" ".repeat(nts).as_bytes())?;
                            };
                        } else {
                            output.write_all(&buf[byte..byte + nbytes])?;
                        }
                    }
                    _ => {
                        col = if ctype == Other {
                            col + cwidth
                        } else if col > 0 {
                            col - 1
                        } else {
                            0
                        };

                        // if we're writing anything other than a space, then we're
                        // done with the line's leading spaces
                        if buf[byte] != 0x20 {
                            init = false;
                        }

                        output.write_all(&buf[byte..byte + nbytes])?;
                    }
                }

                byte += nbytes; // advance the pointer
            }

            output.flush()?;
            buf.truncate(0); // clear the buffer
        }
    }
    Ok(())
}

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

    #[test]
    fn test_next_tabstop_remaining_mode_none() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::None), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::None), 2);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::None), 1);
    }

    #[test]
    fn test_next_tabstop_remaining_mode_plus() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::Plus), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Plus), 4);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Plus), 4);
    }

    #[test]
    fn test_next_tabstop_remaining_mode_slash() {
        assert_eq!(next_tabstop(&[1, 5], 0, &RemainingMode::Slash), 1);
        assert_eq!(next_tabstop(&[1, 5], 3, &RemainingMode::Slash), 2);
        assert_eq!(next_tabstop(&[1, 5], 6, &RemainingMode::Slash), 4);
    }
}
<|endoftext|>