<commit_before>/*!
This module provides both the `Scanner` trait, and the implementations for various basic types.

If you want to implement your own, the simplest way is to use the `scanner!` macro from the main `scan` package.  However, you can also implement a scanner by hand.
*/

use super::{ScanCursor, ScanError};

/**
This macro is a shortcut used in this module.  It implements a scanner for the type `T` given two constraints:

- The existance of a function `scan_fn` which takes a string and returns either `Some(uint)` with the length of the string slice to convert *or* a `None` indicating that there is no valid value to scan.
- That there exists an implementation of `std::str::FromStr` for `T` which can be used to convert the string slice denoted by `scan_fn` into a value of type `T`.

The `name` parameter is used in error messages to identify what sort of token was expected, when `scan_fn` returns `None`.
*/
#[macro_export]
macro_rules! from_str_slice_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = || Err(cursor.expected($name));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err()
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				from_str(s)
					.map(|i| Ok((i, cursor.clone())))
					.unwrap_or_else(err)
			}
		}
	};
}

/**
This trait provides the abstract interface for extracting strongly-typed values out of a string.
*/
pub trait Scanner<'a> {
	/**
The `scan` function's job is to, given a `ScanCursor`, either:

- return a value extracted from the input cursor's position *and* a successor cursor past the end of the input string which was consumed, or
- a `ScanError` describing why a value could not be extracted.

Another way of putting it: you should either parse a value and fast-forward the cursor over the parts of the input you used, or explain why you couldn't.
	*/
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(cursor.expected("`true` or `false`")))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(cursor.expected("character"))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for String {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(String, Cur), ScanError> {
		cursor.pop_token().map(|(s,c)| Ok((s.into_string(), c)))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for () {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<((), Cur), ScanError> {
		Ok(((), cursor.clone()))
	}
}

from_str_slice_scanner! { scan_float -> f32 as "real number" }
from_str_slice_scanner! { scan_float -> f64 as "real number" }
from_str_slice_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_slice_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_slice_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_slice_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_slice_scanner! { scan_int -> int as "integer" }
from_str_slice_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> uint as "unsigned integer" }

/**
This function is just a short-hand way of accessing the byte offset *after* the code point at a given position in a string.
*/
pub fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

/**
This function scans the length of a float literal from a string.

Note that this doesn't support various bits of normal Rust syntax; like embedded underscores or hex literals.
*/
pub fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'...'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'...'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'...'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'...'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'...'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

/**
This function scans the length of an unsigned integer literal from a string.

Note that this doesn't support embedded underscores, or non-decimal bases.
*/
pub fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

/**
This function scans the length of a (potentially) signed integer literal from a string.

Note that this doesn't support embedded underscores, or non-decimal bases.
*/
pub fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;
	use compare_strs::CaseInsensitive;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore, CaseInsensitive> {
		Cursor::new(s, WordsAndInts, Ignore, CaseInsensitive)
	}

	fn scan_a<'a, T: Scanner<'a>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore, CaseInsensitive>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::str::FromStr;
		use std::num::{Float, Zero};

		fn test<'a, F: Float + Scanner<'a> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Zero::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Zero::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::{Bounded, Int, Zero};

		fn test<'a, I: Int + Scanner<'a> + Show>(check_past: bool) {
			let zero: I = Zero::zero();
			let min: I = Bounded::min_value();
			let max: I = Bounded::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<commit_msg>Fixed fallout from numeric trait simplification.<commit_after>/*!
This module provides both the `Scanner` trait, and the implementations for various basic types.

If you want to implement your own, the simplest way is to use the `scanner!` macro from the main `scan` package.  However, you can also implement a scanner by hand.
*/

use super::{ScanCursor, ScanError};

/**
This macro is a shortcut used in this module.  It implements a scanner for the type `T` given two constraints:

- The existance of a function `scan_fn` which takes a string and returns either `Some(uint)` with the length of the string slice to convert *or* a `None` indicating that there is no valid value to scan.
- That there exists an implementation of `std::str::FromStr` for `T` which can be used to convert the string slice denoted by `scan_fn` into a value of type `T`.

The `name` parameter is used in error messages to identify what sort of token was expected, when `scan_fn` returns `None`.
*/
#[macro_export]
macro_rules! from_str_slice_scanner {
	($scan_fn:path -> $T:ty as $name:expr) => {
		impl<'a> Scanner<'a> for $T {
			fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<($T, Cur), ScanError> {
				let err = || Err(cursor.expected($name));

				let end = match $scan_fn(cursor.tail_str()) {
					Some(i) => i,
					None => return err()
				};

				let s = cursor.str_slice_to(end);
				let cursor = cursor.slice_from(end);

				from_str(s)
					.map(|i| Ok((i, cursor.clone())))
					.unwrap_or_else(err)
			}
		}
	};
}

/**
This trait provides the abstract interface for extracting strongly-typed values out of a string.
*/
pub trait Scanner<'a> {
	/**
The `scan` function's job is to, given a `ScanCursor`, either:

- return a value extracted from the input cursor's position *and* a successor cursor past the end of the input string which was consumed, or
- a `ScanError` describing why a value could not be extracted.

Another way of putting it: you should either parse a value and fast-forward the cursor over the parts of the input you used, or explain why you couldn't.
	*/
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(Self, Cur), ScanError>;
}

impl<'a> Scanner<'a> for bool {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(bool, Cur), ScanError> {
		cursor.expect_tok("true").map(|c| (true, c))
			.or_else(|_| cursor.expect_tok("false").map(|c| (false, c)))
			.or_else(|_| Err(cursor.expected("`true` or `false`")))
	}
}

impl<'a> Scanner<'a> for char {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(char, Cur), ScanError> {
		let s = cursor.tail_str();
		if s.len() == 0 {
			Err(cursor.expected("character"))
		} else {
			let ::std::str::CharRange { ch, next } = s.char_range_at(0);
			Ok((ch, cursor.slice_from(next)))
		}
	}
}

impl<'a> Scanner<'a> for &'a str {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(&'a str, Cur), ScanError> {
		cursor.pop_token().map(|sc| Ok(sc))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for String {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<(String, Cur), ScanError> {
		cursor.pop_token().map(|(s,c)| Ok((s.into_string(), c)))
			.unwrap_or_else(|| Err(cursor.expected("any token")))
	}
}

impl<'a> Scanner<'a> for () {
	fn scan<Cur: ScanCursor<'a>>(cursor: &Cur) -> Result<((), Cur), ScanError> {
		Ok(((), cursor.clone()))
	}
}

from_str_slice_scanner! { scan_float -> f32 as "real number" }
from_str_slice_scanner! { scan_float -> f64 as "real number" }
from_str_slice_scanner! { scan_int -> i8 as "8-bit integer" }
from_str_slice_scanner! { scan_int -> i16 as "16-bit integer" }
from_str_slice_scanner! { scan_int -> i32 as "32-bit integer" }
from_str_slice_scanner! { scan_int -> i64 as "64-bit integer" }
from_str_slice_scanner! { scan_int -> int as "integer" }
from_str_slice_scanner! { scan_uint -> u8 as "8-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u16 as "16-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u32 as "32-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> u64 as "64-bit unsigned integer" }
from_str_slice_scanner! { scan_uint -> uint as "unsigned integer" }

/**
This function is just a short-hand way of accessing the byte offset *after* the code point at a given position in a string.
*/
pub fn next_char_at(s: &str, at: uint) -> uint {
	let ::std::str::CharRange { ch: _, next } = s.char_range_at(at);
	next
}

/**
This function scans the length of a float literal from a string.

Note that this doesn't support various bits of normal Rust syntax; like embedded underscores or hex literals.
*/
pub fn scan_float(s: &str) -> Option<uint> {
	enum State {
		Start,
		Whole,
		Suffix,
		ExponentStart,
		Exponent,
	}

	let mut state = Start;

	for (i,c) in s.char_indices() {
		match state {
			Start => {
				assert!(i == 0);
				match c {
					'0'...'9' | '-' => state = Whole,
					_ => return None
				}
			},
			Whole => match c {
				'0'...'9' => (),
				'.' => state = Suffix,
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i)
			},
			Suffix => match c {
				'0'...'9' => (),
				'e' | 'E' => state = ExponentStart,
				_ => return Some(i),
			},
			ExponentStart => match c {
				'+' | '-' | '0'...'9' => state = Exponent,
				_ => return Some(i)
			},
			Exponent => match c {
				'0'...'9' => (),
				_ => return Some(i)
			}
		}
	}

	return Some(s.len())
}

/**
This function scans the length of an unsigned integer literal from a string.

Note that this doesn't support embedded underscores, or non-decimal bases.
*/
pub fn scan_uint<'a>(s: &'a str) -> Option<uint> {
	s.char_indices()
		.take_while(|&(_,c)| '0' <= c && c <= '9')
		.map(|(i,_)| Some(next_char_at(s, i)))
		.last().unwrap_or(None)
}

/**
This function scans the length of a (potentially) signed integer literal from a string.

Note that this doesn't support embedded underscores, or non-decimal bases.
*/
pub fn scan_int<'a>(s: &'a str) -> Option<uint> {
	if s.len() == 0 { return None }

	let (s, off) = if s.char_at(0) == '-' {
		(s.slice_from(1), 1)
	} else {
		(s, 0)
	};

	scan_uint(s).map(|end| end+off)
}

#[cfg(test)]
mod test {
	use Cursor;
	use ScanError;
	use super::Scanner;
	use tokenizer::WordsAndInts;
	use whitespace::Ignore;
	use compare_strs::CaseInsensitive;

	fn cur<'a>(s: &'a str) -> Cursor<'a, WordsAndInts, Ignore, CaseInsensitive> {
		Cursor::new(s, WordsAndInts, Ignore, CaseInsensitive)
	}

	fn scan_a<'a, T: Scanner<'a>>(s: &'a str) -> Result<(T, Cursor<'a, WordsAndInts, Ignore, CaseInsensitive>), ScanError> {
		Scanner::scan(&cur(s))
	}

	#[test]
	fn test_bool() {
		assert!(scan_a::<bool>("").err().is_some());
		assert!(scan_a::<bool>("true").unwrap().0 == true);
		assert!(scan_a::<bool>("false").unwrap().0 == false);
		assert!(scan_a::<bool>("yes").err().is_some());
		assert!(scan_a::<bool>("no").err().is_some());
		assert!(scan_a::<bool>("on").err().is_some());
		assert!(scan_a::<bool>("off").err().is_some());
		assert!(scan_a::<bool>("1").err().is_some());
		assert!(scan_a::<bool>("0").err().is_some());
	}

	#[test]
	fn test_char() {
		assert!(scan_a::<char>("").err().is_some());
		assert!(scan_a::<char>("x").unwrap().0 == 'x');
		assert!(scan_a::<char>("xy").unwrap().0 == 'x');
		assert!(scan_a::<char>("日本語").unwrap().0 == '日');
	}

	#[test]
	fn test_floats() {
		use std::str::FromStr;
		use std::num::Float;

		fn test<'a, F: Float + Scanner<'a> + FromStr>() {
			let f = |v:F| v;
			let fs = |s:&str| -> F from_str(s).unwrap();

			assert!(scan_a::<F>("").err().is_some());
			assert!(scan_a::<F>("0").ok().unwrap().0 == f(Float::zero()));
			assert!(scan_a::<F>("0.0").ok().unwrap().0 == f(Float::zero()));
			assert!(scan_a::<F>("-0").ok().unwrap().0 == -f(Float::zero()));
			assert!(scan_a::<F>("1.0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.00").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e0").ok().unwrap().0 == fs("1.0"));
			assert!(scan_a::<F>("1.0e1").ok().unwrap().0 == fs("10.0"));
		}

		test::<f32>();
		test::<f64>();
	}

	#[test]
	fn test_sized_ints() {
		use std::fmt::Show;
		use std::num::Int;

		fn test<'a, I: Int + Scanner<'a> + Show>(check_past: bool) {
			let zero: I = Int::zero();
			let min: I = Int::min_value();
			let max: I = Int::max_value();

			assert!(scan_a::<I>("").err().is_some());
			assert!(scan_a::<I>("0").ok().unwrap().0 == zero);
			assert!(scan_a::<I>(format!("{}", min).as_slice()).ok().unwrap().0 == min);
			assert!(scan_a::<I>(format!("{}", max).as_slice()).ok().unwrap().0 == max);

			if check_past {
				let past_min: i64 = (min.to_i64()).unwrap() - 1;
				let past_max: u64 = (max.to_u64()).unwrap() + 1;
				assert!(scan_a::<I>(format!("{}", past_min).as_slice()).err().is_some());
				assert!(scan_a::<I>(format!("{}", past_max).as_slice()).err().is_some());
			}
		}

		test::<i8>(true);
		test::<i16>(true);
		test::<i32>(true);
		test::<i64>(false);
		test::<u8>(true);
		test::<u16>(true);
		test::<u32>(true);
		test::<u64>(false);
	}

	#[test]
	fn test_int() {
		assert!(scan_a::<int>("").err().is_some());
		assert!(scan_a::<int>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<int>("x").err().is_some());
		assert!(scan_a::<int>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<int>("-").err().is_some());
		assert!(scan_a::<int>("-0").ok().unwrap().0 == 0);
		assert!(scan_a::<int>("-42").ok().unwrap().0 == -42);
		assert!(scan_a::<int>("-1_234").ok().unwrap().0 == -1);
	}

	#[test]
	fn test_uint() {
		assert!(scan_a::<uint>("").err().is_some());
		assert!(scan_a::<uint>("0").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("1_234").ok().unwrap().0 == 1);
		assert!(scan_a::<uint>("x").err().is_some());
		assert!(scan_a::<uint>("0x").ok().unwrap().0 == 0);
		assert!(scan_a::<uint>("42x").ok().unwrap().0 == 42);
		assert!(scan_a::<uint>("-").err().is_some());
		assert!(scan_a::<uint>("-0").err().is_some());
		assert!(scan_a::<uint>("-42").err().is_some());
		assert!(scan_a::<uint>("-1_234").err().is_some());
	}

	#[test]
	fn test_str() {
		assert!(scan_a::<&str>("").err().is_some());
		assert!(scan_a::<&str>("a").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("a b").ok().unwrap().0 == "a");
		assert!(scan_a::<&str>("abc").ok().unwrap().0 == "abc");
		assert!(scan_a::<&str>("ab-c").ok().unwrap().0 == "ab");
	}
}
<|endoftext|>


<commit_before>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use native;
use channel;
use channel::Channel;
use Filesystem;
use request::Request;

/// The session data structure
pub struct Session<FS> {
	filesystem: FS,
	mountpoint: Path,
	ch: Channel,
	proto_major: uint,
	proto_minor: uint,
	initialized: bool,
	destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
	/// Create a new session by mounting the given filesystem to the given mountpoint
	pub fn new (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
		info!("Mounting {}", mountpoint.display());
		let ch = match Channel::new(mountpoint, options) {
			Ok(ch) => ch,
			Err(err) => fail!("Unable to mount filesystem. Error {:i}", err),
		};
		Session {
			filesystem: filesystem,
			mountpoint: mountpoint.clone(),
			ch: ch,
			proto_major: 0,
			proto_minor: 0,
			initialized: false,
			destroyed: false,
		}
	}

	/// Run the session loop that receives, dispatches and replies to kernel requests.
	/// Make sure to run it on a new single threaded scheduler since the I/O in the
	/// session loop can block.
	pub fn run (&mut self) {
		let mut req = Request::new();
		loop {
			match req.read(self) {
				Err(ENOENT) => continue,		// Operation interrupted. Accordingly to FUSE, this is safe to retry
				Err(EINTR) => continue,			// Interrupted system call, retry
				Err(EAGAIN) => continue,		// Explicitly try again
				Err(ENODEV) => break,			// Filesystem was unmounted, quit the loop
				Err(err) => fail!("Lost connection to FUSE device. Error {:i}", err),
				Ok(..) => req.dispatch(self),
			}
		}
	}

	/// Run the session loop in a background task
	pub fn spawn (self) -> BackgroundSession {
		BackgroundSession::new(self)
	}
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
	fn drop (&mut self) {
		info!("Unmounted {}", self.mountpoint.display());
		// The actual unmounting takes place because self.ch is dropped here
	}
}

/// The background session data structure
pub struct BackgroundSession {
	mountpoint: Path,
}

impl BackgroundSession {
	/// Create a new background session for the given session by running its
	/// session loop in a background task. If the returned handle is dropped,
	/// the filesystem is unmounted and the given session ends.
	pub fn new<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
		let mountpoint = se.mountpoint.clone();
		// The background task is started using a a new native thread
		// since native I/O in the session loop can block
		native::task::spawn(proc() {
			let mut se = se;
			se.run();
		});
		BackgroundSession { mountpoint: mountpoint }
	}
}

impl Drop for BackgroundSession {
	fn drop (&mut self) {
		info!("Unmounting {}", self.mountpoint.display());
		// Unmounting the filesystem will eventually end the session loop,
		// drop the session and hence end the background task.
		channel::unmount(&self.mountpoint);
	}
}
<commit_msg>Give background task a reasonable name<commit_after>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use std::task;
use native;
use channel;
use channel::Channel;
use Filesystem;
use request::Request;

/// The session data structure
pub struct Session<FS> {
	filesystem: FS,
	mountpoint: Path,
	ch: Channel,
	proto_major: uint,
	proto_minor: uint,
	initialized: bool,
	destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
	/// Create a new session by mounting the given filesystem to the given mountpoint
	pub fn new (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
		info!("Mounting {}", mountpoint.display());
		let ch = match Channel::new(mountpoint, options) {
			Ok(ch) => ch,
			Err(err) => fail!("Unable to mount filesystem. Error {:i}", err),
		};
		Session {
			filesystem: filesystem,
			mountpoint: mountpoint.clone(),
			ch: ch,
			proto_major: 0,
			proto_minor: 0,
			initialized: false,
			destroyed: false,
		}
	}

	/// Run the session loop that receives, dispatches and replies to kernel requests.
	/// Make sure to run it on a new single threaded scheduler since the I/O in the
	/// session loop can block.
	pub fn run (&mut self) {
		let mut req = Request::new();
		loop {
			match req.read(self) {
				Err(ENOENT) => continue,		// Operation interrupted. Accordingly to FUSE, this is safe to retry
				Err(EINTR) => continue,			// Interrupted system call, retry
				Err(EAGAIN) => continue,		// Explicitly try again
				Err(ENODEV) => break,			// Filesystem was unmounted, quit the loop
				Err(err) => fail!("Lost connection to FUSE device. Error {:i}", err),
				Ok(..) => req.dispatch(self),
			}
		}
	}

	/// Run the session loop in a background task
	pub fn spawn (self) -> BackgroundSession {
		BackgroundSession::new(self)
	}
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
	fn drop (&mut self) {
		info!("Unmounted {}", self.mountpoint.display());
		// The actual unmounting takes place because self.ch is dropped here
	}
}

/// The background session data structure
pub struct BackgroundSession {
	mountpoint: Path,
}

impl BackgroundSession {
	/// Create a new background session for the given session by running its
	/// session loop in a background task. If the returned handle is dropped,
	/// the filesystem is unmounted and the given session ends.
	pub fn new<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
		let mountpoint = se.mountpoint.clone();
		// The background task is started using a a new native thread
		// since native I/O in the session loop can block
		let task = task::task().named(format!("FUSE {}", mountpoint.display()));
		native::task::spawn_opts(task.opts, proc() {
			let mut se = se;
			se.run();
		});
		BackgroundSession { mountpoint: mountpoint }
	}
}

impl Drop for BackgroundSession {
	fn drop (&mut self) {
		info!("Unmounting {}", self.mountpoint.display());
		// Unmounting the filesystem will eventually end the session loop,
		// drop the session and hence end the background task.
		channel::unmount(&self.mountpoint);
	}
}
<|endoftext|>


<commit_before>pub mod torrent {
    pub use self::ver_249b1b as current;
    pub use self::current::Session;
    use bincode;

    pub fn load(data: &[u8]) -> Option<Session> {
        if let Ok(m) = bincode::deserialize::<ver_249b1b::Session>(data) {
            Some(m)
        } else if let Ok(m) = bincode::deserialize::<ver_5f166d::Session>(data) {
            info!("Migrating torrent session from v5f166d");
            Some(m.migrate())
        } else {
            None
        }
    }

    impl Session {
        pub fn migrate(self) -> Self {
            self
        }
    }

    pub mod ver_249b1b {
        use torrent::{Bitfield, Info};

        use chrono::{DateTime, Utc};

        #[derive(Serialize, Deserialize)]
        pub struct Session {
            pub info: Info,
            pub pieces: Bitfield,
            pub uploaded: u64,
            pub downloaded: u64,
            pub status: Status,
            pub path: Option<String>,
            pub priority: u8,
            pub priorities: Vec<u8>,
            pub created: DateTime<Utc>,
            pub throttle_ul: Option<i64>,
            pub throttle_dl: Option<i64>,
        }

        #[derive(Clone, Debug, Serialize, Deserialize)]
        pub struct Status {
            pub paused: bool,
            pub validating: bool,
            pub error: Option<String>,
            pub state: StatusState,
        }

        #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
        pub enum StatusState {
            Magnet,
            // Torrent has not acquired all pieces
            Incomplete,
            // Torrent has acquired all pieces, regardless of validity
            Complete,
        }
    }

    pub mod ver_5f166d {
        use super::ver_249b1b as next;

        use torrent::{info, Info as TInfo, Bitfield};

        use chrono::{DateTime, Utc};
        use url::Url;

        #[derive(Serialize, Deserialize)]
        pub struct Session {
            pub info: Info,
            pub pieces: Bitfield,
            pub uploaded: u64,
            pub downloaded: u64,
            pub status: Status,
            pub path: Option<String>,
            pub wanted: Bitfield,
            pub priority: u8,
            pub priorities: Vec<u8>,
            pub created: DateTime<Utc>,
            pub throttle_ul: Option<i64>,
            pub throttle_dl: Option<i64>,
        }

        #[derive(Serialize, Deserialize)]
        pub enum Status {
            Pending,
            Paused,
            Leeching,
            Idle,
            Seeding,
            Validating,
            Magnet,
            DiskError,
        }

        #[derive(Serialize, Deserialize)]
        pub struct Info {
            pub name: String,
            pub announce: Option<String>,
            pub piece_len: u32,
            pub total_len: u64,
            pub hashes: Vec<Vec<u8>>,
            pub hash: [u8; 20],
            pub files: Vec<info::File>,
            pub private: bool,
            pub be_name: Option<Vec<u8>>,
        }

        impl Session {
            pub fn migrate(self) -> next::Session {
                let state = if self.pieces.complete() {
                    next::StatusState::Complete
                } else {
                    next::StatusState::Incomplete
                };
                let paused = match self.status {
                    Status::Paused => true,
                    _ => false,
                };
                let piece_idx = TInfo::generate_piece_idx(
                    self.info.hashes.len(),
                    self.info.piece_len as u64,
                    &self.info.files,
                );
                next::Session {
                    info: TInfo {
                        name: self.info.name,
                        announce: self.info.announce.and_then(|url| Url::parse(&url).ok()),
                        piece_len: self.info.piece_len,
                        total_len: self.info.total_len,
                        hashes: self.info.hashes,
                        hash: self.info.hash,
                        files: self.info.files,
                        private: self.info.private,
                        be_name: self.info.be_name,
                        piece_idx,
                    },
                    pieces: self.pieces,
                    uploaded: self.uploaded,
                    downloaded: self.downloaded,
                    status: next::Status {
                        paused,
                        state,
                        validating: false,
                        error: None,
                    },
                    path: self.path,
                    priority: self.priority,
                    priorities: self.priorities,
                    created: self.created,
                    throttle_ul: self.throttle_ul,
                    throttle_dl: self.throttle_dl,
                }.migrate()
            }
        }
    }
}
<commit_msg>Add in additional sessions for migration<commit_after>pub mod torrent {
    pub use self::ver_249b1b as current;
    pub use self::current::Session;
    use bincode;

    pub fn load(data: &[u8]) -> Option<Session> {
        if let Ok(m) = bincode::deserialize::<ver_249b1b::Session>(data) {
            Some(m)
        } else if let Ok(m) = bincode::deserialize::<ver_5f166d::Session>(data) {
            info!("Migrating torrent session from v5f166d");
            Some(m.migrate())
        } else if let Ok(m) = bincode::deserialize::<ver_8e1121::Session>(data) {
            info!("Migrating torrent session from v8e1121");
            Some(m.migrate())
        } else {
            None
        }
    }

    impl Session {
        pub fn migrate(self) -> Self {
            self
        }
    }

    pub mod ver_249b1b {
        use torrent::{Bitfield, Info};

        use chrono::{DateTime, Utc};

        #[derive(Serialize, Deserialize)]
        pub struct Session {
            pub info: Info,
            pub pieces: Bitfield,
            pub uploaded: u64,
            pub downloaded: u64,
            pub status: Status,
            pub path: Option<String>,
            pub priority: u8,
            pub priorities: Vec<u8>,
            pub created: DateTime<Utc>,
            pub throttle_ul: Option<i64>,
            pub throttle_dl: Option<i64>,
        }

        #[derive(Clone, Debug, Serialize, Deserialize)]
        pub struct Status {
            pub paused: bool,
            pub validating: bool,
            pub error: Option<String>,
            pub state: StatusState,
        }

        #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
        pub enum StatusState {
            Magnet,
            // Torrent has not acquired all pieces
            Incomplete,
            // Torrent has acquired all pieces, regardless of validity
            Complete,
        }
    }

    pub mod ver_5f166d {
        use super::ver_249b1b as next;

        use torrent::{info, Info as TInfo, Bitfield};

        use chrono::{DateTime, Utc};
        use url::Url;

        #[derive(Serialize, Deserialize)]
        pub struct Session {
            pub info: Info,
            pub pieces: Bitfield,
            pub uploaded: u64,
            pub downloaded: u64,
            pub status: Status,
            pub path: Option<String>,
            pub priority: u8,
            pub priorities: Vec<u8>,
            pub created: DateTime<Utc>,
            pub throttle_ul: Option<i64>,
            pub throttle_dl: Option<i64>,
        }

        #[derive(Serialize, Deserialize)]
        pub enum Status {
            Pending,
            Paused,
            Leeching,
            Idle,
            Seeding,
            Validating,
            Magnet,
            DiskError,
        }

        #[derive(Serialize, Deserialize)]
        pub struct Info {
            pub name: String,
            pub announce: String,
            pub piece_len: u32,
            pub total_len: u64,
            pub hashes: Vec<Vec<u8>>,
            pub hash: [u8; 20],
            pub files: Vec<info::File>,
            pub private: bool,
            pub be_name: Option<Vec<u8>>,
        }

        impl Session {
            pub fn migrate(self) -> super::current::Session {
                let state = if self.pieces.complete() {
                    next::StatusState::Complete
                } else {
                    next::StatusState::Incomplete
                };
                let paused = match self.status {
                    Status::Paused => true,
                    _ => false,
                };
                let piece_idx = TInfo::generate_piece_idx(
                    self.info.hashes.len(),
                    self.info.piece_len as u64,
                    &self.info.files,
                );
                next::Session {
                    info: TInfo {
                        name: self.info.name,
                        announce: Url::parse(&self.info.announce).ok(),
                        piece_len: self.info.piece_len,
                        total_len: self.info.total_len,
                        hashes: self.info.hashes,
                        hash: self.info.hash,
                        files: self.info.files,
                        private: self.info.private,
                        be_name: self.info.be_name,
                        piece_idx,
                    },
                    pieces: self.pieces,
                    uploaded: self.uploaded,
                    downloaded: self.downloaded,
                    status: next::Status {
                        paused,
                        state,
                        validating: false,
                        error: None,
                    },
                    path: self.path,
                    priority: self.priority,
                    priorities: self.priorities,
                    created: self.created,
                    throttle_ul: self.throttle_ul,
                    throttle_dl: self.throttle_dl,
                }.migrate()
            }
        }
    }

    pub mod ver_8e1121 {
        use super::ver_5f166d as next;
        use self::next::{Status, Info};

        use torrent::Bitfield;

        use chrono::{DateTime, Utc};

        #[derive(Serialize, Deserialize)]
        pub struct Session {
            pub info: Info,
            pub pieces: Bitfield,
            pub uploaded: u64,
            pub downloaded: u64,
            pub status: Status,
            pub path: Option<String>,
            pub wanted: Bitfield,
            pub priority: u8,
            pub priorities: Vec<u8>,
            pub created: DateTime<Utc>,
            pub throttle_ul: Option<i64>,
            pub throttle_dl: Option<i64>,
        }

        impl Session {
            pub fn migrate(self) -> super::current::Session {
                next::Session {
                    info: self.info,
                    pieces: self.pieces,
                    uploaded: self.uploaded,
                    downloaded: self.downloaded,
                    status: self.status,
                    path: self.path,
                    priority: self.priority,
                    priorities: self.priorities,
                    created: self.created,
                    throttle_ul: self.throttle_ul,
                    throttle_dl: self.throttle_dl,
                }.migrate()
            }
        }
    }
}
<|endoftext|>


<commit_before>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//
pub use crate::types::TokenInfo;

use futures::lock::Mutex;
use itertools::Itertools;
use std::collections::HashMap;
use std::io;
use std::path::{Path, PathBuf};

use async_trait::async_trait;

use serde::{Deserialize, Serialize};

// The storage layer allows retrieving tokens for scopes that have been
// previously granted tokens. One wrinkle is that a token granted for a set
// of scopes X is also valid for any subset of X's scopes. So when retrieving a
// token for a set of scopes provided by the caller it's beneficial to compare
// that set to all previously stored tokens to see if it is a subset of any
// existing set. To do this efficiently we store a bloom filter along with each
// token that represents the set of scopes the token is associated with. The
// bloom filter allows for efficiently skipping any entries that are
// definitively not a superset.
// The current implementation uses a 64bit bloom filter with 4 hash functions.

/// ScopeHash is a hash value derived from a list of scopes. The hash value
/// represents a fingerprint of the set of scopes *independent* of the ordering.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct ScopeHash(u64);

/// ScopeFilter represents a filter for a set of scopes. It can definitively
/// prove that a given list of scopes is not a subset of another.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct ScopeFilter(u64);

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum FilterResponse {
    Maybe,
    No,
}

impl ScopeFilter {
    /// Determine if this ScopeFilter could be a subset of the provided filter.
    fn is_subset_of(self, filter: ScopeFilter) -> FilterResponse {
        if self.0 & filter.0 == self.0 {
            FilterResponse::Maybe
        } else {
            FilterResponse::No
        }
    }
}

/// A set of scopes
#[derive(Debug)]
pub(crate) struct ScopeSet<'a, T> {
    hash: ScopeHash,
    filter: ScopeFilter,
    scopes: &'a [T],
}

// Implement Clone manually. Auto derive fails to work correctly because we want
// Clone to be implemented regardless of whether T is Clone or not.
impl<'a, T> Clone for ScopeSet<'a, T> {
    fn clone(&self) -> Self {
        ScopeSet {
            hash: self.hash,
            filter: self.filter,
            scopes: self.scopes,
        }
    }
}
impl<'a, T> Copy for ScopeSet<'a, T> {}

impl<'a, T> ScopeSet<'a, T>
where
    T: AsRef<str>,
{
    /// Convert from an array into a ScopeSet. Automatically invoked by the compiler when
    /// an array reference is passed.
    // implement an inherent from method even though From is implemented. This
    // is because passing an array ref like &[&str; 1] (&["foo"]) will be auto
    // deref'd to a slice on function boundaries, but it will not implement the
    // From trait. This inherent method just serves to auto deref from array
    // refs to slices and proxy to the From impl.
    pub fn from(scopes: &'a [T]) -> Self {
        let (hash, filter) = scopes.iter().fold(
            (ScopeHash(0), ScopeFilter(0)),
            |(mut scope_hash, mut scope_filter), scope| {
                let h = seahash::hash(scope.as_ref().as_bytes());

                // Use the first 4 6-bit chunks of the seahash as the 4 hash values
                // in the bloom filter.
                for i in 0..4 {
                    // h is a hash derived value in the range 0..64
                    let h = (h >> (6 * i)) & 0b11_1111;
                    scope_filter.0 |= 1 << h;
                }

                // xor the hashes together to get an order independent fingerprint.
                scope_hash.0 ^= h;
                (scope_hash, scope_filter)
            },
        );
        ScopeSet {
            hash,
            filter,
            scopes,
        }
    }
}

/// Implement your own token storage solution by implementing this trait. You need a way to
/// store and retrieve tokens, each keyed by a set of scopes.
#[async_trait]
pub trait TokenStorage: Send + Sync {
    /// Store a token for the given set of scopes so that it can be retrieved later by get()
    /// TokenInfo can be serialized with serde.
    async fn set(&self, scopes: &[&str], token: TokenInfo) -> anyhow::Result<()>;

    /// Retrieve a token stored by set for the given set of scopes
    async fn get(&self, scopes: &[&str]) -> Option<TokenInfo>;
}

pub(crate) enum Storage {
    Memory { tokens: Mutex<JSONTokens> },
    Disk(DiskStorage),
    Custom(Box<dyn TokenStorage>),
}

impl Storage {
    pub(crate) async fn set<T>(
        &self,
        scopes: ScopeSet<'_, T>,
        token: TokenInfo,
    ) -> anyhow::Result<()>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => Ok(tokens.lock().await.set(scopes, token)?),
            Storage::Disk(disk_storage) => Ok(disk_storage.set(scopes, token).await?),
            Storage::Custom(custom_storage) => {
                let str_scopes: Vec<_> = scopes
                    .scopes
                    .iter()
                    .map(|scope| scope.as_ref())
                    .sorted()
                    .unique()
                    .collect();

                custom_storage
                    .set(
                        &str_scopes[..], // TODO: sorted, unique
                        token,
                    )
                    .await
            }
        }
    }

    pub(crate) async fn get<T>(&self, scopes: ScopeSet<'_, T>) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().await.get(scopes),
            Storage::Disk(disk_storage) => disk_storage.get(scopes).await,
            Storage::Custom(custom_storage) => {
                let str_scopes: Vec<_> = scopes
                    .scopes
                    .iter()
                    .map(|scope| scope.as_ref())
                    .sorted()
                    .unique()
                    .collect();

                custom_storage.get(&str_scopes[..]).await
            }
        }
    }
}

/// A single stored token.

#[derive(Debug, Clone)]
struct JSONToken {
    scopes: Vec<String>,
    token: TokenInfo,
    hash: ScopeHash,
    filter: ScopeFilter,
}

impl<'de> Deserialize<'de> for JSONToken {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct RawJSONToken {
            scopes: Vec<String>,
            token: TokenInfo,
        }
        let RawJSONToken { scopes, token } = RawJSONToken::deserialize(deserializer)?;
        let ScopeSet { hash, filter, .. } = ScopeSet::from(&scopes);
        Ok(JSONToken {
            scopes,
            token,
            hash,
            filter,
        })
    }
}

impl Serialize for JSONToken {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        #[derive(Serialize)]
        struct RawJSONToken<'a> {
            scopes: &'a [String],
            token: &'a TokenInfo,
        }
        RawJSONToken {
            scopes: &self.scopes,
            token: &self.token,
        }
        .serialize(serializer)
    }
}

/// List of tokens in a JSON object
#[derive(Debug, Clone)]
pub(crate) struct JSONTokens {
    token_map: HashMap<ScopeHash, JSONToken>,
}

impl Serialize for JSONTokens {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.collect_seq(self.token_map.values())
    }
}

impl<'de> Deserialize<'de> for JSONTokens {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct V;
        impl<'de> serde::de::Visitor<'de> for V {
            type Value = JSONTokens;

            // Format a message stating what data this Visitor expects to receive.
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a sequence of JSONToken's")
            }

            fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
            where
                M: serde::de::SeqAccess<'de>,
            {
                let mut token_map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
                while let Some(json_token) = access.next_element::<JSONToken>()? {
                    token_map.insert(json_token.hash, json_token);
                }
                Ok(JSONTokens { token_map })
            }
        }

        // Instantiate our Visitor and ask the Deserializer to drive
        // it over the input data.
        deserializer.deserialize_seq(V)
    }
}

impl JSONTokens {
    pub(crate) fn new() -> Self {
        JSONTokens {
            token_map: HashMap::new(),
        }
    }

    async fn load_from_file(filename: &Path) -> Result<Self, io::Error> {
        let contents = tokio::fs::read(filename).await?;
        serde_json::from_slice(&contents).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
    }

    fn get<T>(
        &self,
        ScopeSet {
            hash,
            filter,
            scopes,
        }: ScopeSet<T>,
    ) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        if let Some(json_token) = self.token_map.get(&hash) {
            return Some(json_token.token.clone());
        }

        let requested_scopes_are_subset_of = |other_scopes: &[String]| {
            scopes
                .iter()
                .all(|s| other_scopes.iter().any(|t| t.as_str() == s.as_ref()))
        };
        // No exact match for the scopes provided. Search for any tokens that
        // exist for a superset of the scopes requested.
        self.token_map
            .values()
            .filter(|json_token| filter.is_subset_of(json_token.filter) == FilterResponse::Maybe)
            .find(|v: &&JSONToken| requested_scopes_are_subset_of(&v.scopes))
            .map(|t: &JSONToken| t.token.clone())
    }

    fn set<T>(
        &mut self,
        ScopeSet {
            hash,
            filter,
            scopes,
        }: ScopeSet<T>,
        token: TokenInfo,
    ) -> Result<(), io::Error>
    where
        T: AsRef<str>,
    {
        use std::collections::hash_map::Entry;
        match self.token_map.entry(hash) {
            Entry::Occupied(mut entry) => {
                entry.get_mut().token = token;
            }
            Entry::Vacant(entry) => {
                let json_token = JSONToken {
                    scopes: scopes.iter().map(|x| x.as_ref().to_owned()).collect(),
                    token,
                    hash,
                    filter,
                };
                entry.insert(json_token.clone());
            }
        }
        Ok(())
    }
}

pub(crate) struct DiskStorage {
    tokens: Mutex<JSONTokens>,
    filename: PathBuf,
}

impl DiskStorage {
    pub(crate) async fn new(filename: PathBuf) -> Result<Self, io::Error> {
        let tokens = match JSONTokens::load_from_file(&filename).await {
            Ok(tokens) => tokens,
            Err(e) if e.kind() == io::ErrorKind::NotFound => JSONTokens::new(),
            Err(e) => return Err(e),
        };

        Ok(DiskStorage {
            tokens: Mutex::new(tokens),
            filename,
        })
    }

    pub(crate) async fn set<T>(
        &self,
        scopes: ScopeSet<'_, T>,
        token: TokenInfo,
    ) -> Result<(), io::Error>
    where
        T: AsRef<str>,
    {
        use tokio::io::AsyncWriteExt;
        let json = {
            use std::ops::Deref;
            let mut lock = self.tokens.lock().await;
            lock.set(scopes, token)?;
            serde_json::to_string(lock.deref())
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
        };
        let mut f = open_writeable_file(&self.filename).await?;
        f.write_all(json.as_bytes()).await?;
        Ok(())
    }

    pub(crate) async fn get<T>(&self, scopes: ScopeSet<'_, T>) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        self.tokens.lock().await.get(scopes)
    }
}

#[cfg(unix)]
async fn open_writeable_file(
    filename: impl AsRef<Path>,
) -> Result<tokio::fs::File, tokio::io::Error> {
    // Ensure if the file is created it's only readable and writable by the
    // current user.
    use std::os::unix::fs::OpenOptionsExt;
    let opts: tokio::fs::OpenOptions = {
        let mut opts = std::fs::OpenOptions::new();
        opts.write(true).create(true).truncate(true).mode(0o600);
        opts.into()
    };
    opts.open(filename).await
}

#[cfg(not(unix))]
async fn open_writeable_file(
    filename: impl AsRef<Path>,
) -> Result<tokio::fs::File, tokio::io::Error> {
    // I don't have knowledge of windows or other platforms to know how to
    // create a file that's only readable by the current user.
    tokio::fs::File::create(filename).await
}

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

    #[test]
    fn test_scope_filter() {
        let foo = ScopeSet::from(&["foo"]).filter;
        let bar = ScopeSet::from(&["bar"]).filter;
        let foobar = ScopeSet::from(&["foo", "bar"]).filter;

        // foo and bar are both subsets of foobar. This condition should hold no
        // matter what changes are made to the bloom filter implementation.
        assert!(foo.is_subset_of(foobar) == FilterResponse::Maybe);
        assert!(bar.is_subset_of(foobar) == FilterResponse::Maybe);

        // These conditions hold under the current bloom filter implementation
        // because "foo" and "bar" don't collide, but if the bloom filter
        // implementations change it could be valid for them to return Maybe.
        assert!(foo.is_subset_of(bar) == FilterResponse::No);
        assert!(bar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(bar) == FilterResponse::No);
    }

    #[tokio::test]
    async fn test_disk_storage() {
        let new_token = |access_token: &str| TokenInfo {
            access_token: access_token.to_owned(),
            refresh_token: None,
            expires_at: None,
        };
        let scope_set = ScopeSet::from(&["myscope"]);
        let tempdir = tempfile::tempdir().unwrap();
        {
            let storage = DiskStorage::new(tempdir.path().join("tokenstorage.json"))
                .await
                .unwrap();
            assert!(storage.get(scope_set).await.is_none());
            storage
                .set(scope_set, new_token("my_access_token"))
                .await
                .unwrap();
            assert_eq!(
                storage.get(scope_set).await,
                Some(new_token("my_access_token"))
            );
        }
        {
            // Create a new DiskStorage instance and verify the tokens were read from disk correctly.
            let storage = DiskStorage::new(tempdir.path().join("tokenstorage.json"))
                .await
                .unwrap();
            assert_eq!(
                storage.get(scope_set).await,
                Some(new_token("my_access_token"))
            );
        }
    }
}
<commit_msg>Amend tests for the ID token field<commit_after>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//
pub use crate::types::TokenInfo;

use futures::lock::Mutex;
use itertools::Itertools;
use std::collections::HashMap;
use std::io;
use std::path::{Path, PathBuf};

use async_trait::async_trait;

use serde::{Deserialize, Serialize};

// The storage layer allows retrieving tokens for scopes that have been
// previously granted tokens. One wrinkle is that a token granted for a set
// of scopes X is also valid for any subset of X's scopes. So when retrieving a
// token for a set of scopes provided by the caller it's beneficial to compare
// that set to all previously stored tokens to see if it is a subset of any
// existing set. To do this efficiently we store a bloom filter along with each
// token that represents the set of scopes the token is associated with. The
// bloom filter allows for efficiently skipping any entries that are
// definitively not a superset.
// The current implementation uses a 64bit bloom filter with 4 hash functions.

/// ScopeHash is a hash value derived from a list of scopes. The hash value
/// represents a fingerprint of the set of scopes *independent* of the ordering.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct ScopeHash(u64);

/// ScopeFilter represents a filter for a set of scopes. It can definitively
/// prove that a given list of scopes is not a subset of another.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
struct ScopeFilter(u64);

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum FilterResponse {
    Maybe,
    No,
}

impl ScopeFilter {
    /// Determine if this ScopeFilter could be a subset of the provided filter.
    fn is_subset_of(self, filter: ScopeFilter) -> FilterResponse {
        if self.0 & filter.0 == self.0 {
            FilterResponse::Maybe
        } else {
            FilterResponse::No
        }
    }
}

/// A set of scopes
#[derive(Debug)]
pub(crate) struct ScopeSet<'a, T> {
    hash: ScopeHash,
    filter: ScopeFilter,
    scopes: &'a [T],
}

// Implement Clone manually. Auto derive fails to work correctly because we want
// Clone to be implemented regardless of whether T is Clone or not.
impl<'a, T> Clone for ScopeSet<'a, T> {
    fn clone(&self) -> Self {
        ScopeSet {
            hash: self.hash,
            filter: self.filter,
            scopes: self.scopes,
        }
    }
}
impl<'a, T> Copy for ScopeSet<'a, T> {}

impl<'a, T> ScopeSet<'a, T>
where
    T: AsRef<str>,
{
    /// Convert from an array into a ScopeSet. Automatically invoked by the compiler when
    /// an array reference is passed.
    // implement an inherent from method even though From is implemented. This
    // is because passing an array ref like &[&str; 1] (&["foo"]) will be auto
    // deref'd to a slice on function boundaries, but it will not implement the
    // From trait. This inherent method just serves to auto deref from array
    // refs to slices and proxy to the From impl.
    pub fn from(scopes: &'a [T]) -> Self {
        let (hash, filter) = scopes.iter().fold(
            (ScopeHash(0), ScopeFilter(0)),
            |(mut scope_hash, mut scope_filter), scope| {
                let h = seahash::hash(scope.as_ref().as_bytes());

                // Use the first 4 6-bit chunks of the seahash as the 4 hash values
                // in the bloom filter.
                for i in 0..4 {
                    // h is a hash derived value in the range 0..64
                    let h = (h >> (6 * i)) & 0b11_1111;
                    scope_filter.0 |= 1 << h;
                }

                // xor the hashes together to get an order independent fingerprint.
                scope_hash.0 ^= h;
                (scope_hash, scope_filter)
            },
        );
        ScopeSet {
            hash,
            filter,
            scopes,
        }
    }
}

/// Implement your own token storage solution by implementing this trait. You need a way to
/// store and retrieve tokens, each keyed by a set of scopes.
#[async_trait]
pub trait TokenStorage: Send + Sync {
    /// Store a token for the given set of scopes so that it can be retrieved later by get()
    /// TokenInfo can be serialized with serde.
    async fn set(&self, scopes: &[&str], token: TokenInfo) -> anyhow::Result<()>;

    /// Retrieve a token stored by set for the given set of scopes
    async fn get(&self, scopes: &[&str]) -> Option<TokenInfo>;
}

pub(crate) enum Storage {
    Memory { tokens: Mutex<JSONTokens> },
    Disk(DiskStorage),
    Custom(Box<dyn TokenStorage>),
}

impl Storage {
    pub(crate) async fn set<T>(
        &self,
        scopes: ScopeSet<'_, T>,
        token: TokenInfo,
    ) -> anyhow::Result<()>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => Ok(tokens.lock().await.set(scopes, token)?),
            Storage::Disk(disk_storage) => Ok(disk_storage.set(scopes, token).await?),
            Storage::Custom(custom_storage) => {
                let str_scopes: Vec<_> = scopes
                    .scopes
                    .iter()
                    .map(|scope| scope.as_ref())
                    .sorted()
                    .unique()
                    .collect();

                custom_storage
                    .set(
                        &str_scopes[..], // TODO: sorted, unique
                        token,
                    )
                    .await
            }
        }
    }

    pub(crate) async fn get<T>(&self, scopes: ScopeSet<'_, T>) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().await.get(scopes),
            Storage::Disk(disk_storage) => disk_storage.get(scopes).await,
            Storage::Custom(custom_storage) => {
                let str_scopes: Vec<_> = scopes
                    .scopes
                    .iter()
                    .map(|scope| scope.as_ref())
                    .sorted()
                    .unique()
                    .collect();

                custom_storage.get(&str_scopes[..]).await
            }
        }
    }
}

/// A single stored token.

#[derive(Debug, Clone)]
struct JSONToken {
    scopes: Vec<String>,
    token: TokenInfo,
    hash: ScopeHash,
    filter: ScopeFilter,
}

impl<'de> Deserialize<'de> for JSONToken {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        #[derive(Deserialize)]
        struct RawJSONToken {
            scopes: Vec<String>,
            token: TokenInfo,
        }
        let RawJSONToken { scopes, token } = RawJSONToken::deserialize(deserializer)?;
        let ScopeSet { hash, filter, .. } = ScopeSet::from(&scopes);
        Ok(JSONToken {
            scopes,
            token,
            hash,
            filter,
        })
    }
}

impl Serialize for JSONToken {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        #[derive(Serialize)]
        struct RawJSONToken<'a> {
            scopes: &'a [String],
            token: &'a TokenInfo,
        }
        RawJSONToken {
            scopes: &self.scopes,
            token: &self.token,
        }
        .serialize(serializer)
    }
}

/// List of tokens in a JSON object
#[derive(Debug, Clone)]
pub(crate) struct JSONTokens {
    token_map: HashMap<ScopeHash, JSONToken>,
}

impl Serialize for JSONTokens {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.collect_seq(self.token_map.values())
    }
}

impl<'de> Deserialize<'de> for JSONTokens {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct V;
        impl<'de> serde::de::Visitor<'de> for V {
            type Value = JSONTokens;

            // Format a message stating what data this Visitor expects to receive.
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("a sequence of JSONToken's")
            }

            fn visit_seq<M>(self, mut access: M) -> Result<Self::Value, M::Error>
            where
                M: serde::de::SeqAccess<'de>,
            {
                let mut token_map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
                while let Some(json_token) = access.next_element::<JSONToken>()? {
                    token_map.insert(json_token.hash, json_token);
                }
                Ok(JSONTokens { token_map })
            }
        }

        // Instantiate our Visitor and ask the Deserializer to drive
        // it over the input data.
        deserializer.deserialize_seq(V)
    }
}

impl JSONTokens {
    pub(crate) fn new() -> Self {
        JSONTokens {
            token_map: HashMap::new(),
        }
    }

    async fn load_from_file(filename: &Path) -> Result<Self, io::Error> {
        let contents = tokio::fs::read(filename).await?;
        serde_json::from_slice(&contents).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
    }

    fn get<T>(
        &self,
        ScopeSet {
            hash,
            filter,
            scopes,
        }: ScopeSet<T>,
    ) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        if let Some(json_token) = self.token_map.get(&hash) {
            return Some(json_token.token.clone());
        }

        let requested_scopes_are_subset_of = |other_scopes: &[String]| {
            scopes
                .iter()
                .all(|s| other_scopes.iter().any(|t| t.as_str() == s.as_ref()))
        };
        // No exact match for the scopes provided. Search for any tokens that
        // exist for a superset of the scopes requested.
        self.token_map
            .values()
            .filter(|json_token| filter.is_subset_of(json_token.filter) == FilterResponse::Maybe)
            .find(|v: &&JSONToken| requested_scopes_are_subset_of(&v.scopes))
            .map(|t: &JSONToken| t.token.clone())
    }

    fn set<T>(
        &mut self,
        ScopeSet {
            hash,
            filter,
            scopes,
        }: ScopeSet<T>,
        token: TokenInfo,
    ) -> Result<(), io::Error>
    where
        T: AsRef<str>,
    {
        use std::collections::hash_map::Entry;
        match self.token_map.entry(hash) {
            Entry::Occupied(mut entry) => {
                entry.get_mut().token = token;
            }
            Entry::Vacant(entry) => {
                let json_token = JSONToken {
                    scopes: scopes.iter().map(|x| x.as_ref().to_owned()).collect(),
                    token,
                    hash,
                    filter,
                };
                entry.insert(json_token.clone());
            }
        }
        Ok(())
    }
}

pub(crate) struct DiskStorage {
    tokens: Mutex<JSONTokens>,
    filename: PathBuf,
}

impl DiskStorage {
    pub(crate) async fn new(filename: PathBuf) -> Result<Self, io::Error> {
        let tokens = match JSONTokens::load_from_file(&filename).await {
            Ok(tokens) => tokens,
            Err(e) if e.kind() == io::ErrorKind::NotFound => JSONTokens::new(),
            Err(e) => return Err(e),
        };

        Ok(DiskStorage {
            tokens: Mutex::new(tokens),
            filename,
        })
    }

    pub(crate) async fn set<T>(
        &self,
        scopes: ScopeSet<'_, T>,
        token: TokenInfo,
    ) -> Result<(), io::Error>
    where
        T: AsRef<str>,
    {
        use tokio::io::AsyncWriteExt;
        let json = {
            use std::ops::Deref;
            let mut lock = self.tokens.lock().await;
            lock.set(scopes, token)?;
            serde_json::to_string(lock.deref())
                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
        };
        let mut f = open_writeable_file(&self.filename).await?;
        f.write_all(json.as_bytes()).await?;
        Ok(())
    }

    pub(crate) async fn get<T>(&self, scopes: ScopeSet<'_, T>) -> Option<TokenInfo>
    where
        T: AsRef<str>,
    {
        self.tokens.lock().await.get(scopes)
    }
}

#[cfg(unix)]
async fn open_writeable_file(
    filename: impl AsRef<Path>,
) -> Result<tokio::fs::File, tokio::io::Error> {
    // Ensure if the file is created it's only readable and writable by the
    // current user.
    use std::os::unix::fs::OpenOptionsExt;
    let opts: tokio::fs::OpenOptions = {
        let mut opts = std::fs::OpenOptions::new();
        opts.write(true).create(true).truncate(true).mode(0o600);
        opts.into()
    };
    opts.open(filename).await
}

#[cfg(not(unix))]
async fn open_writeable_file(
    filename: impl AsRef<Path>,
) -> Result<tokio::fs::File, tokio::io::Error> {
    // I don't have knowledge of windows or other platforms to know how to
    // create a file that's only readable by the current user.
    tokio::fs::File::create(filename).await
}

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

    #[test]
    fn test_scope_filter() {
        let foo = ScopeSet::from(&["foo"]).filter;
        let bar = ScopeSet::from(&["bar"]).filter;
        let foobar = ScopeSet::from(&["foo", "bar"]).filter;

        // foo and bar are both subsets of foobar. This condition should hold no
        // matter what changes are made to the bloom filter implementation.
        assert!(foo.is_subset_of(foobar) == FilterResponse::Maybe);
        assert!(bar.is_subset_of(foobar) == FilterResponse::Maybe);

        // These conditions hold under the current bloom filter implementation
        // because "foo" and "bar" don't collide, but if the bloom filter
        // implementations change it could be valid for them to return Maybe.
        assert!(foo.is_subset_of(bar) == FilterResponse::No);
        assert!(bar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(bar) == FilterResponse::No);
    }

    #[tokio::test]
    async fn test_disk_storage() {
        let new_token = |access_token: &str| TokenInfo {
            access_token: access_token.to_owned(),
            refresh_token: None,
            expires_at: None,
            id_token: None,
        };
        let scope_set = ScopeSet::from(&["myscope"]);
        let tempdir = tempfile::tempdir().unwrap();
        {
            let storage = DiskStorage::new(tempdir.path().join("tokenstorage.json"))
                .await
                .unwrap();
            assert!(storage.get(scope_set).await.is_none());
            storage
                .set(scope_set, new_token("my_access_token"))
                .await
                .unwrap();
            assert_eq!(
                storage.get(scope_set).await,
                Some(new_token("my_access_token"))
            );
        }
        {
            // Create a new DiskStorage instance and verify the tokens were read from disk correctly.
            let storage = DiskStorage::new(tempdir.path().join("tokenstorage.json"))
                .await
                .unwrap();
            assert_eq!(
                storage.get(scope_set).await,
                Some(new_token("my_access_token"))
            );
        }
    }
}
<|endoftext|>


<commit_before>use crate::error::{Error, ParseSectorError, ParseSectorGroupError, ParseSectorTypeError};
use std;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;
use std::string::ToString;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    #[serde(default = "default_cache_dir")]
    pub cache_dir: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }
fn default_cache_dir() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectora/cache");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}
fn default_socket_path() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectorad");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}
fn default_socket_dir() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectora");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}

impl Config {
    pub fn from_path(configpath: &std::path::Path) -> Result<Self, Error> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Debug, Clone)]
pub struct SocketConfig {
    pub socket_path: String,
    pub socket_dir: String,
}

impl SocketConfig {
    pub fn new() -> Self {
        SocketConfig { socket_path: default_socket_path(),
                       socket_dir: default_socket_dir() }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn from_path(configpath: &std::path::Path) -> Result<Self, Error> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

impl fmt::Display for SectorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SectorType::Team => write!(f, "T"),
            SectorType::Repo => write!(f, "R"),
        }
    }
}

impl FromStr for SectorType {
    type Err = ParseSectorTypeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "T" => Ok(SectorType::Team),
            "R" => Ok(SectorType::Repo),
            _ => Err(ParseSectorTypeError::UnknownType),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo }
    }
}

impl fmt::Display for Sector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}:{}", self.id, self.name, self.sector_type) }
}

impl FromStr for Sector {
    type Err = ParseSectorError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<&str>>();
        if parts.len() == 3 {
            Ok(Self { id: parts[0].parse().map_err(ParseSectorError::Id)?,
                      name: String::from(parts[1]),
                      sector_type: parts[2].parse().map_err(ParseSectorError::Type)? })
        } else {
            Err(ParseSectorError::BadFormat)
        }
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

impl fmt::Display for SectorGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let members_str = self.members
                              .values()
                              .map(ToString::to_string)
                              .collect::<Vec<_>>()
                              .join(" ");
        writeln!(f,
                 "{}\t{}\t{}\t{}",
                 self.sector,
                 self.gid.map(|i| i.to_string()).unwrap_or_default(),
                 self.group.clone().unwrap_or_default(),
                 members_str)
    }
}

impl FromStr for SectorGroup {
    type Err = ParseSectorGroupError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split('\t').collect::<Vec<&str>>();
        let sector = parts[0].parse().map_err(ParseSectorGroupError::Sector)?;
        let gid: Option<u64> = match parts[1] {
            "" => None,
            s => Some(s.parse().map_err(ParseSectorGroupError::Gid)?),
        };
        let group: Option<String> = match parts[2] {
            "" => None,
            s => Some(String::from(s)),
        };
        let members = parts[3].split(' ')
                              .map(|s| s.parse::<Member>().map_err(ParseSectorGroupError::Member))
                              .collect::<Result<Vec<Member>, _>>()?
                              .into_iter()
                              .map(|m| (m.login.clone(), m))
                              .collect::<HashMap<_, _>>();
        Ok(Self { sector,
                  gid,
                  group,
                  members })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

impl fmt::Display for Member {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.id, self.login) }
}

impl FromStr for Member {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse()?,
                  login: String::from(parts[1]) })
    }
}

#[allow(dead_code)]
pub struct MemberGid {
    pub member: Member,
    pub gid: u64,
}

impl fmt::Display for MemberGid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}|{}", self.member, self.gid) }
}

impl FromStr for MemberGid {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split('|').collect::<Vec<&str>>();
        Ok(Self { member: parts[0].parse()?,
                  gid: parts[1].parse()? })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Rate {
    pub limit: usize,
    pub remaining: usize,
    pub reset: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RateLimit {
    pub rate: Rate,
}
<commit_msg>Rename<commit_after>use crate::error::{Error, ParseSectorError, ParseSectorGroupError, ParseSectorTypeError};
use std;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::str::FromStr;
use std::string::ToString;
use toml;

#[derive(Deserialize, Debug, Clone)]
pub struct Config {
    pub token: String,
    pub org: String,
    #[serde(default = "default_team")]
    pub team: Vec<TeamConfig>,
    #[serde(default = "default_repo")]
    pub repo: Vec<RepoConfig>,
    #[serde(default = "default_endpoint")]
    pub endpoint: String,
    #[serde(default = "default_home")]
    pub home: String,
    #[serde(default = "default_sh")]
    pub sh: String,
    #[serde(default = "default_cache_duration")]
    pub cache_duration: u64,
    #[serde(default = "default_cert_path")]
    pub cert_path: String,
    #[serde(default = "default_user_conf_path")]
    pub user_conf_path: String,
    #[serde(default = "default_cache_dir")]
    pub cache_dir: String,
    pub proxy_url: Option<String>,
}

fn default_team() -> Vec<TeamConfig> { Vec::new() }
fn default_repo() -> Vec<RepoConfig> { Vec::new() }
fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }
fn default_user_conf_path() -> String { String::from(".config/sectora.toml") }
fn default_cache_dir() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectora/cache");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}

fn get_socket_path() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectorad");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}

fn get_socket_dir() -> String {
    let mut path = std::env::temp_dir();
    path.push("sectora");
    String::from(path.as_os_str().to_str().unwrap_or_default())
}

impl Config {
    pub fn from_path(configpath: &std::path::Path) -> Result<Self, Error> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<Config>(&contents)?)
    }
}

#[derive(Debug, Clone)]
pub struct SocketConfig {
    pub socket_path: String,
    pub socket_dir: String,
}

impl SocketConfig {
    pub fn new() -> Self {
        SocketConfig { socket_path: get_socket_path(),
                       socket_dir: get_socket_dir() }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserConfig {
    pub sh: Option<String>,
    pub pass: Option<String>,
}

impl UserConfig {
    #[allow(dead_code)]
    pub fn from_path(configpath: &std::path::Path) -> Result<Self, Error> {
        let mut file = File::open(configpath)?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)?;
        Ok(toml::from_str::<UserConfig>(&contents)?)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Team {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TeamConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Repo {
    pub id: u64,
    pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RepoConfig {
    pub name: String,
    pub gid: Option<u64>,
    pub group: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SectorType {
    Team,
    Repo,
}

impl fmt::Display for SectorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            SectorType::Team => write!(f, "T"),
            SectorType::Repo => write!(f, "R"),
        }
    }
}

impl FromStr for SectorType {
    type Err = ParseSectorTypeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "T" => Ok(SectorType::Team),
            "R" => Ok(SectorType::Repo),
            _ => Err(ParseSectorTypeError::UnknownType),
        }
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Sector {
    pub id: u64,
    pub name: String,
    pub sector_type: SectorType,
}

impl From<Team> for Sector {
    fn from(team: Team) -> Self {
        Self { id: team.id,
               name: team.name,
               sector_type: SectorType::Team }
    }
}

impl From<Repo> for Sector {
    fn from(repo: Repo) -> Self {
        Self { id: repo.id,
               name: repo.name,
               sector_type: SectorType::Repo }
    }
}

impl fmt::Display for Sector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}:{}", self.id, self.name, self.sector_type) }
}

impl FromStr for Sector {
    type Err = ParseSectorError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<&str>>();
        if parts.len() == 3 {
            Ok(Self { id: parts[0].parse().map_err(ParseSectorError::Id)?,
                      name: String::from(parts[1]),
                      sector_type: parts[2].parse().map_err(ParseSectorError::Type)? })
        } else {
            Err(ParseSectorError::BadFormat)
        }
    }
}

#[derive(Debug, Clone)]
pub struct SectorGroup {
    pub sector: Sector,
    pub gid: Option<u64>,
    pub group: Option<String>,
    pub members: HashMap<String, Member>,
}

impl SectorGroup {
    #[allow(dead_code)]
    pub fn get_gid(&self) -> u64 { self.gid.unwrap_or(self.sector.id) }
    #[allow(dead_code)]
    pub fn get_group(&self) -> String { self.group.clone().unwrap_or(self.sector.name.clone()) }
}

impl fmt::Display for SectorGroup {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let members_str = self.members
                              .values()
                              .map(ToString::to_string)
                              .collect::<Vec<_>>()
                              .join(" ");
        writeln!(f,
                 "{}\t{}\t{}\t{}",
                 self.sector,
                 self.gid.map(|i| i.to_string()).unwrap_or_default(),
                 self.group.clone().unwrap_or_default(),
                 members_str)
    }
}

impl FromStr for SectorGroup {
    type Err = ParseSectorGroupError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split('\t').collect::<Vec<&str>>();
        let sector = parts[0].parse().map_err(ParseSectorGroupError::Sector)?;
        let gid: Option<u64> = match parts[1] {
            "" => None,
            s => Some(s.parse().map_err(ParseSectorGroupError::Gid)?),
        };
        let group: Option<String> = match parts[2] {
            "" => None,
            s => Some(String::from(s)),
        };
        let members = parts[3].split(' ')
                              .map(|s| s.parse::<Member>().map_err(ParseSectorGroupError::Member))
                              .collect::<Result<Vec<Member>, _>>()?
                              .into_iter()
                              .map(|m| (m.login.clone(), m))
                              .collect::<HashMap<_, _>>();
        Ok(Self { sector,
                  gid,
                  group,
                  members })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Member {
    pub id: u64,
    pub login: String,
}

impl fmt::Display for Member {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.id, self.login) }
}

impl FromStr for Member {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<&str>>();
        Ok(Self { id: parts[0].parse()?,
                  login: String::from(parts[1]) })
    }
}

#[allow(dead_code)]
pub struct MemberGid {
    pub member: Member,
    pub gid: u64,
}

impl fmt::Display for MemberGid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}|{}", self.member, self.gid) }
}

impl FromStr for MemberGid {
    type Err = std::num::ParseIntError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split('|').collect::<Vec<&str>>();
        Ok(Self { member: parts[0].parse()?,
                  gid: parts[1].parse()? })
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PublicKey {
    pub id: u64,
    pub key: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Rate {
    pub limit: usize,
    pub remaining: usize,
    pub reset: usize,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct RateLimit {
    pub rate: Rate,
}
<|endoftext|>


<commit_before>use luminance::{Dim2, Flat, Sampler};
pub use luminance::{RGBA32F, Texture};
use image;
use std::ops::Deref;
use std::path::Path;

use resource::{Cache, Load, LoadError, Reload, Result};

/// Load an RGBA texture from an image at a path.
pub fn load_rgba_texture<P>(path: P, sampler: &Sampler, linear: bool) -> Result<Texture<Flat, Dim2, RGBA32F>> where P: AsRef<Path> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

  let image = image::open(path).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?.to_rgba();
  let dim = image.dimensions();
  let raw: Vec<f32> = image.into_raw().into_iter().map(|x| {
    let y = x as f32 / 255.;

    if linear {
      y
    } else {
      y.powf(1. / 2.2)
    }
  }).collect();

  let tex = Texture::new(dim, 0, sampler).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?;
  tex.upload_raw(false, &raw);

  Ok(tex)
}

/// Save an RGBA image on disk.
pub fn save_rgba_texture<P>(texture: &Texture<Flat, Dim2, RGBA32F>, path: P) where P: AsRef<Path> {
  info!("saving texture image to: \x1b[35m{:?}", path.as_ref());

  let texels = texture.get_raw_texels();
  let (w, h) = texture.size();
  let mut output = Vec::with_capacity((w * h) as usize);

  for texel in &texels {
    output.push((texel * 255.) as u8);
  }

  let _ = image::save_buffer(path, &output, w, h, image::ColorType::RGBA(8));
}

pub struct TextureImage {
  pub texture: Texture<Flat, Dim2, RGBA32F>,
  sampler: Sampler,
  linear: bool,
}

impl Deref for TextureImage {
  type Target = Texture<Flat, Dim2, RGBA32F>;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

impl<'a> Load<'a> for TextureImage {
  type Args = (Sampler, bool);

  fn load<P>(path: P, _: &mut Cache<'a>, (sampler, linear): Self::Args) -> Result<Self> where P: AsRef<Path> {
    load_rgba_texture(path, &sampler, linear)
      .map(|tex| TextureImage {
        texture: tex,
        sampler: sampler,
        linear: linear
      })
  }
}

impl<'a> Reload<'a> for TextureImage {
  fn reload_args(&self) -> Self::Args {
    (self.sampler, self.linear)
  }
}
<commit_msg>Export Dim2 and Flat from texture as it might be used.<commit_after>use luminance::Sampler;
pub use luminance::{Dim2, Flat, RGBA32F, Texture};
use image;
use std::ops::Deref;
use std::path::Path;

use resource::{Cache, Load, LoadError, Reload, Result};

/// Load an RGBA texture from an image at a path.
pub fn load_rgba_texture<P>(path: P, sampler: &Sampler, linear: bool) -> Result<Texture<Flat, Dim2, RGBA32F>> where P: AsRef<Path> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

  let image = image::open(path).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?.to_rgba();
  let dim = image.dimensions();
  let raw: Vec<f32> = image.into_raw().into_iter().map(|x| {
    let y = x as f32 / 255.;

    if linear {
      y
    } else {
      y.powf(1. / 2.2)
    }
  }).collect();

  let tex = Texture::new(dim, 0, sampler).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?;
  tex.upload_raw(false, &raw);

  Ok(tex)
}

/// Save an RGBA image on disk.
pub fn save_rgba_texture<P>(texture: &Texture<Flat, Dim2, RGBA32F>, path: P) where P: AsRef<Path> {
  info!("saving texture image to: \x1b[35m{:?}", path.as_ref());

  let texels = texture.get_raw_texels();
  let (w, h) = texture.size();
  let mut output = Vec::with_capacity((w * h) as usize);

  for texel in &texels {
    output.push((texel * 255.) as u8);
  }

  let _ = image::save_buffer(path, &output, w, h, image::ColorType::RGBA(8));
}

pub struct TextureImage {
  pub texture: Texture<Flat, Dim2, RGBA32F>,
  sampler: Sampler,
  linear: bool,
}

impl Deref for TextureImage {
  type Target = Texture<Flat, Dim2, RGBA32F>;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

impl<'a> Load<'a> for TextureImage {
  type Args = (Sampler, bool);

  fn load<P>(path: P, _: &mut Cache<'a>, (sampler, linear): Self::Args) -> Result<Self> where P: AsRef<Path> {
    load_rgba_texture(path, &sampler, linear)
      .map(|tex| TextureImage {
        texture: tex,
        sampler: sampler,
        linear: linear
      })
  }
}

impl<'a> Reload<'a> for TextureImage {
  fn reload_args(&self) -> Self::Args {
    (self.sampler, self.linear)
  }
}
<|endoftext|>