message: mockchain: Implement FromStr for BlockDate
diff: @@ -3,7 +3,7 @@ use crate::key::{Hash, PrivateKey, PublicKey, Signature};
  use crate::transaction::*;
  use chain_core::property;
  -use std::fmt;
  +use std::{error, fmt, num::ParseIntError, str};
  /// Non unique identifier of the transaction position in the
  /// blockchain. There may be many transactions related to the same
  @@ -107,6 +107,51 @@ impl fmt::Display for BlockDate {
  }
  }
  +#[derive(Debug)]
  +pub enum BlockDateParseError {
  + DotMissing,
  + BadEpochId(ParseIntError),
  + BadSlotId(ParseIntError),
  +}
  +
  +const EXPECT_FORMAT_MESSAGE: &'static str = "expected block date format EPOCH.SLOT";
  +
  +impl fmt::Display for BlockDateParseError {
  + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  + use BlockDateParseError::*;
  + match self {
  + DotMissing => write!(f, "{}", EXPECT_FORMAT_MESSAGE),
  + BadEpochId(_) => write!(f, "invalid epoch ID, {}", EXPECT_FORMAT_MESSAGE),
  + BadSlotId(_) => write!(f, "invalid slot ID, {}", EXPECT_FORMAT_MESSAGE),
  + }
  + }
  +}
  +
  +impl error::Error for BlockDateParseError {
  + fn source(&self) -> Option<&(dyn error::Error + 'static)> {
  + use BlockDateParseError::*;
  + match self {
  + DotMissing => None,
  + BadEpochId(e) => Some(e),
  + BadSlotId(e) => Some(e),
  + }
  + }
  +}
  +
  +impl str::FromStr for BlockDate {
  + type Err = BlockDateParseError;
  +
  + fn from_str(s: &str) -> Result<BlockDate, BlockDateParseError> {
  + let (ep, sp) = match s.find('.') {
  + None => return Err(BlockDateParseError::DotMissing),
  + Some(pos) => s.split_at(pos),
  + };
  + let epoch = str::parse::<u64>(ep).map_err(|e| BlockDateParseError::BadEpochId(e))?;
  + let slot_id = str::parse::<u64>(sp).map_err(|e| BlockDateParseError::BadSlotId(e))?;
  + Ok(BlockDate { epoch, slot_id })
  + }
  +}
  +
  impl property::Block for Block {
  type Id = Hash;
  type Date = BlockDate;
  