use std::fmt;
use std::str::FromStr;
pub mod error;
pub use error::CIKError;
pub fn parse(value: &str) -> Result<CIK, CIKError> {
let s: String = value.into();
if s.is_empty() || s.len() > 10 {
Err(CIKError::InvalidLength { was: s.len() })
} else {
match s.parse::<u64>() {
Ok(value) => build(value),
Err(_err) => Err(CIKError::InvalidFormat { was: s }),
}
}
}
pub fn build(value: u64) -> Result<CIK, CIKError> {
if !(1..=9_999_999_999).contains(&value) {
return Err(CIKError::InvalidValue { was: value });
}
Ok(CIK(value))
}
pub fn validate(value: &str) -> bool {
if value.is_empty() || value.len() > 10 {
println!("Bad length: {:?}", value);
return false;
}
let b = value.as_bytes();
return b.iter().all(|b| *b >= b'0' && *b <= b'9');
}
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Hash)]
#[repr(transparent)]
#[allow(clippy::upper_case_acronyms)]
pub struct CIK(u64);
impl fmt::Display for CIK {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Debug for CIK {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CIK{}", self.0)
}
}
impl FromStr for CIK {
type Err = CIKError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse(s)
}
}
impl TryFrom<u64> for CIK {
type Error = CIKError;
fn try_from(value: u64) -> Result<Self, Self::Error> {
build(value)
}
}
impl CIK {
pub fn value(&self) -> u64 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn parse_cik_for_apple() {
match parse("320193") {
Ok(cik) => {
assert_eq!(cik.to_string(), "320193");
assert_eq!(cik.value(), 320193);
}
Err(err) => assert!(false, "Did not expect parsing to fail: {}", err),
}
}
#[test]
fn build_cik_for_apple() {
match build(320193) {
Ok(cik) => {
assert_eq!(cik.to_string(), "320193");
assert_eq!(cik.value(), 320193);
}
Err(err) => assert!(false, "Did not expect building to fail: {}", err),
}
}
#[test]
fn reject_empty_string() {
let res = parse("");
assert!(res.is_err());
}
#[test]
fn reject_non_digit_string() {
let res = parse("-1");
assert!(res.is_err());
}
#[test]
fn reject_zero_string() {
let res = parse("0");
assert!(res.is_err());
}
#[test]
fn reject_long_string() {
let res = parse("10000000000");
assert!(res.is_err());
}
#[test]
fn reject_zero_value() {
let res = build(0);
assert!(res.is_err());
}
#[test]
fn reject_large_value() {
let res = build(10_000_000_000);
assert!(res.is_err());
}
proptest! {
#[test]
#[allow(unused_must_use)]
fn doesnt_crash(s in "\\PC*") {
parse(&s);
}
}
}