diff --git a/src/args.rs b/src/args.rs
index e7d76c3..7e211ed 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -62,6 +62,7 @@ pub struct CommandContext<'i, S, E>(PhantomData<(&'i str, S, E)>);
/// use ::iosonism::error::ReadError;
/// use ::iosonism::strcursor::StringReader;
///
+/// #[derive(PartialEq, Eq, Hash)]
/// struct BoolArgumentType;
///
/// impl<S, E> ArgumentType<S, E> for BoolArgumentType
@@ -165,8 +166,10 @@ where T: ArgumentType<S, E> + Send + Sync + Eq + ::std::hash::Hash + Any {
}
fn dyn_hash(&self, mut hasher: &mut dyn ::std::hash::Hasher) {
+ // we do actually compare the typeid above, through downcast_ref!
+ self.type_id().hash(&mut hasher);
// rust-lang/rust#44015
- self.hash(&mut hasher)
+ self.hash(&mut hasher);
}
}
@@ -261,7 +264,7 @@ impl Display for BoolArgumentType {
}
/// An integer argument.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Default)]
pub struct IntegerArgumentType<T, R: RangeBounds<T>> {
/// The valid range for this argument.
pub range: R,
@@ -366,9 +369,8 @@ impl<T: Display, R: RangeBounds<T>> Display for IntegerArgumentType<T, R> {
}
}
-// FIXME implement Eq and Hash properly
/// A float argument.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash, Default)]
+#[derive(Copy, Clone, Debug, Default)]
pub struct FloatArgumentType<T, R: RangeBounds<T>> {
/// The valid range for this argument.
pub range: R,
@@ -410,13 +412,88 @@ pub fn bounded_float<T, R: RangeBounds<T>>(
}
}
+/// Implement Eq and Hash for FloatArgumentType.
+macro_rules! impl_eq_hash {
+ ($t:ty) => {
+ impl<R: RangeBounds<$t>> PartialEq for FloatArgumentType<$t, R> {
+ fn eq(&self, other: &Self) -> bool {
+ use ::std::ops::Bound;
+ (match (self.range.start_bound(), other.range.start_bound()) {
+ (Bound::Included(left), Bound::Included(right)) => {
+ left.to_bits() == right.to_bits()
+ },
+ (Bound::Excluded(left), Bound::Excluded(right)) => {
+ left.to_bits() == right.to_bits()
+ },
+ (Bound::Unbounded, Bound::Unbounded) => {
+ true
+ },
+ _ => {
+ false
+ },
+ } && match (self.range.end_bound(), other.range.end_bound()) {
+ (Bound::Included(left), Bound::Included(right)) => {
+ left.to_bits() == right.to_bits()
+ },
+ (Bound::Excluded(left), Bound::Excluded(right)) => {
+ left.to_bits() == right.to_bits()
+ },
+ (Bound::Unbounded, Bound::Unbounded) => {
+ true
+ },
+ _ => {
+ false
+ },
+ })
+ }
+ }
+ impl<R: RangeBounds<$t>> Eq for FloatArgumentType<$t, R> {
+ }
+ impl<R: RangeBounds<$t>> Hash for FloatArgumentType<$t, R> {
+ fn hash<H: ::std::hash::Hasher>(&self, h: &mut H) {
+ use ::std::ops::Bound;
+ match self.range.start_bound() {
+ Bound::Included(f) => {
+ 0usize.hash(h);
+ f.to_bits().hash(h);
+ },
+ Bound::Excluded(f) => {
+ 1usize.hash(h);
+ f.to_bits().hash(h);
+ },
+ Bound::Unbounded => {
+ 2usize.hash(h);
+ }
+ }
+ match self.range.end_bound() {
+ Bound::Included(f) => {
+ 0usize.hash(h);
+ f.to_bits().hash(h);
+ },
+ Bound::Excluded(f) => {
+ 1usize.hash(h);
+ f.to_bits().hash(h);
+ },
+ Bound::Unbounded => {
+ 2usize.hash(h);
+ }
+ }
+ }
+ }
+ }
+}
+
+impl_eq_hash!(f32);
+impl_eq_hash!(f64);
+
/// An `ArgumentType` for float types.
impl<S, E, T, R> ArgumentType<S, E> for FloatArgumentType<T, R>
where
for<'i> E: ReadError<'i, Cursor<&'i str>>,
for<'i> E: RangeError<'i, Cursor<&'i str>, T, R>,
- R: RangeBounds<T> + Eq + Hash,
- T: PartialOrd<T> + FromStr<Err=ParseFloatError> + Any + Display + Eq + Hash,
+ R: RangeBounds<T>,
+ T: PartialOrd<T> + FromStr<Err=ParseFloatError> + Any + Display,
+ Self: Eq + Hash,
{
/// A `FloatArgumentType` parses a float type.
type Result = T;
@@ -474,7 +551,7 @@ impl<T: Display, R: RangeBounds<T>> Display for FloatArgumentType<T, R> {
}
/// A string argument.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct StringArgumentType(StringMode);
/// Creates a string argument that accepts simple words.
|