Fix all clippy warnings

This commit is contained in:
Austen Adler 2023-02-28 09:13:35 -05:00
parent da28c92a2b
commit b620ceec9b
5 changed files with 97 additions and 78 deletions

View File

@ -8,14 +8,14 @@ pub fn lat_lon_to_cellid(lat: f64, lon: f64) -> Result<CellID, Error> {
let latlng = LatLng::from_degrees(lat, lon);
// We don't want to entertain any invalid latitudes or longitudes
if latlng.normalized() != latlng {
Err(Error::InvalidLatLng)
} else {
if latlng.normalized() == latlng {
Ok(Into::<CellID>::into(latlng))
} else {
Err(Error::InvalidLatLng)
}
}
pub(crate) fn extract_binary(number: u64, range: RangeInclusive<usize>) -> u64 {
pub fn extract_binary(number: u64, range: RangeInclusive<usize>) -> u64 {
(number >> range.start()) & (u64::MAX >> (63 - (range.end() - range.start())))
}

View File

@ -27,28 +27,46 @@ pub const V0_MAX_NUMBER: u32 = 1024;
/// The minimum number value for V0
pub const V0_MIN_NUMBER: u32 = 1;
// Any encoding or decoding error
#[derive(Error, Debug, Eq, PartialEq)]
pub enum Error {
/// The given word is not in the wordlist
#[error("Word does not exist: {0}")]
WordDoesNotExist(String),
#[error("Invalid version: {0}")]
InvalidVersion(u8),
#[error("Unimplemented version")]
UnimplementedVersion(Version),
/// The encoded version bit is not supported by this version of the crate
#[error("Unimplemented version: {0}")]
UnimplementedVersion(u8),
/// The number component in the address is too large
#[error("Number out of range")]
NumberOutOfRange(Number),
#[error("Invalid encoding")]
InvalidEncoding,
/// The string to decode is not ascii
#[error("The string given is not ascii")]
NonAscii,
/// The number of components given to decode is incorrect
#[error("Wrong number of components")]
WrongComponentCount,
/// No number component was found
///
/// See [`Algorithm::from_str`] for the possible position of the number component
#[error("No number component")]
NoNumberComponent,
/// The empty string was given to decode
#[error("Empty string given")]
Empty,
/// The latitude given is not in the valid range
///
/// This is the case when any of these are true:
/// * `lat == f64::NAN`
/// * `lon == f64::NAN`
/// * `lat < -90`
/// * `lat > 90`
/// * `lon < -180`
/// * `lon > 180`
#[error("Invalid latitude or longitude")]
InvalidLatLng,
}
#[allow(clippy::doc_markdown)]
/// An encoded this_algorithm address
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Address<'a> {
@ -62,14 +80,6 @@ pub enum Version {
V0,
}
// pub trait Alg {
// type Err;
// fn to_cellid(&self) -> Result<CellID, Self::Err>;
// fn from_cellid();
// }
impl FromStr for Address<'_> {
type Err = Error;
@ -77,9 +87,15 @@ impl FromStr for Address<'_> {
///
/// * `0000 WORD0 WORD1 WORD2` (regular)
/// * `WORD2 WORD1 WORD0 0000` (reversed)
///
/// # Errors
///
/// This function will return an error if the passed data does not correctly decode.
///
/// See [`enum@Error`] for possible errors
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.is_ascii() {
return Err(Error::InvalidEncoding);
return Err(Error::NonAscii);
}
let s = s.trim().to_ascii_uppercase();
@ -109,12 +125,19 @@ impl FromStr for Address<'_> {
components.into_iter().skip(1).collect::<Vec<_>>()
};
Address::from_components(number, other_components)
Address::from_components(number, &other_components)
}
}
impl Address<'_> {
/// Converts a latitude and longitude into an encoded address
///
/// # Errors
///
/// This will return [`Error::InvalidLatLng`] when the latitude or longitude are not valid
///
/// See [`Error::InvalidLatLng`] for the valid values
pub fn from_lat_lon(lat: f64, lon: f64) -> Result<Self, Error> {
let cellid = conversions::lat_lon_to_cellid(lat, lon)?;
@ -122,11 +145,13 @@ impl Address<'_> {
}
/// Constructs an address from the given cellid
#[must_use]
pub fn from_cellid(cellid: CellID) -> Self {
v0::UnpackedCellID::from(cellid).into()
}
/// Constructs an address from a u64 representation of a cellid
#[must_use]
pub fn from_cellid_u64(cellid_u64: u64) -> Self {
v0::UnpackedCellID::from(CellID(cellid_u64)).into()
}
@ -137,7 +162,7 @@ impl Address<'_> {
// }
// Parses an address v0 given the number and word components
fn parse_v0(number: Number, word_components: Vec<&str>) -> Result<Self, Error> {
fn parse_v0(number: Number, word_components: &[&str]) -> Result<Self, Error> {
if !(V0_MIN_NUMBER..=V0_MAX_NUMBER).contains(&number) {
return Err(Error::NumberOutOfRange(number));
}
@ -150,7 +175,9 @@ impl Address<'_> {
let words = TryInto::<[&'static Word; 3]>::try_into(
word_components
.iter()
.map(|w| words::get_word(w).ok_or_else(|| Error::WordDoesNotExist(w.to_string())))
.map(|w| {
words::get_word(w).ok_or_else(|| Error::WordDoesNotExist((*w).to_string()))
})
.collect::<Result<Vec<&'static Word>, Error>>()?,
)
// This unwrap is okay because we just checked to make sure the number of word components was 3
@ -163,41 +190,22 @@ impl Address<'_> {
})
}
/// Builds an `Address` from a number and all word components
fn from_components(number: Number, other_components: Vec<&str>) -> Result<Self, Error> {
fn from_components(number: Number, other_components: &[&str]) -> Result<Self, Error> {
match extract_version(number)? {
Version::V0 => Self::parse_v0(number, other_components),
}
}
/// Get the address as a [`CellID`] value
#[must_use]
pub fn as_cell_id(&self) -> CellID {
match self.version {
Version::V0 => {
UnpackedCellID::from(self).into()
// let ten_bits = 0b1111_1111_11;
// let thirteen_bits = 0b1111_1111_1111_1;
// let mut ret = 0b0;
// // Add words in reverse order
// for word in self.words.iter().rev() {
// ret = (ret << 13) | (word.number as u64 & thirteen_bits);
// }
// // Add the number
// ret = (ret << 10) | (self.number as u64 & ten_bits);
// // Add the final bit
// ret = (ret << 1) | 0b1;
// // Shift the whole id left by the number of unused levels * 2
// ret = ret << ((s2::cellid::MAX_LEVEL - CELLID_LEVEL as u64) * 2);
// CellID(ret)
}
Version::V0 => UnpackedCellID::from(self).into(),
}
}
// TODO: Test this
#[must_use]
pub fn to_lat_lon(&self) -> (f64, f64) {
let lat_lng = LatLng::from(self.as_cell_id());
(lat_lng.lat.deg(), lat_lng.lng.deg())
@ -208,10 +216,10 @@ impl Address<'_> {
///
/// The version number is set by the two bits 11 and 12
// TODO: impl TryFrom ?
fn extract_version(number: Number) -> Result<Version, Error> {
const fn extract_version(number: Number) -> Result<Version, Error> {
match ((number >> 10) & 0b11) as u8 {
0 => Ok(Version::V0),
v => Err(Error::InvalidVersion(v)),
v => Err(Error::UnimplementedVersion(v)),
}
}
@ -299,41 +307,41 @@ mod tests {
fn test_parse_v0() {
// Regular case
assert_eq!(
Address::parse_v0(1000, vec!["apple", "orange", "grape"]),
Address::parse_v0(1000, &["apple", "orange", "grape"]),
Ok(addr![1000, apple, orange, grape])
);
// Number is on the edge
assert_eq!(
Address::parse_v0(1, vec!["apple", "orange", "grape"]),
Address::parse_v0(1, &["apple", "orange", "grape"]),
Ok(addr![1, apple, orange, grape])
);
assert_eq!(
Address::parse_v0(1024, vec!["apple", "orange", "grape"]),
Address::parse_v0(1024, &["apple", "orange", "grape"]),
Ok(addr![1024, apple, orange, grape])
);
assert_eq!(
Address::parse_v0(V0_MAX_NUMBER, vec!["apple", "orange", "grape"]),
Address::parse_v0(V0_MAX_NUMBER, &["apple", "orange", "grape"]),
Ok(addr![V0_MAX_NUMBER, apple, orange, grape])
);
assert_eq!(
Address::parse_v0(V0_MIN_NUMBER, vec!["apple", "orange", "grape"]),
Address::parse_v0(V0_MIN_NUMBER, &["apple", "orange", "grape"]),
Ok(addr![V0_MIN_NUMBER, apple, orange, grape])
);
// Word not found
assert!(Address::parse_v0(1000, vec!["ASDF", "orange", "grape"]).is_err());
assert!(Address::parse_v0(1000, &["ASDF", "orange", "grape"]).is_err());
// Too few words
assert!(Address::parse_v0(1000, vec!["apple", "orange"]).is_err());
assert!(Address::parse_v0(1000, vec!["apple"]).is_err());
assert!(Address::parse_v0(1000, vec![]).is_err());
assert!(Address::parse_v0(1000, &["apple", "orange"]).is_err());
assert!(Address::parse_v0(1000, &["apple"]).is_err());
assert!(Address::parse_v0(1000, &[]).is_err());
// Too many words
assert!(Address::parse_v0(1000, vec!["apple", "orange", "grape", "banana"]).is_err());
assert!(Address::parse_v0(1000, &["apple", "orange", "grape", "banana"]).is_err());
// Number too large or small
assert!(Address::parse_v0(10_000, vec!["apple", "orange", "grape"]).is_err());
assert!(Address::parse_v0(0, vec!["apple", "orange", "grape"]).is_err());
assert!(Address::parse_v0(10_000, &["apple", "orange", "grape"]).is_err());
assert!(Address::parse_v0(0, &["apple", "orange", "grape"]).is_err());
}
}

View File

@ -36,7 +36,7 @@ impl From<UnpackedCellID> for Address<'_> {
let word2 = words::NUMBER_TO_WORDS[unpacked_cell_id.word2_bits as usize][0];
Self {
number: unpacked_cell_id.number_bits as u32,
number: u32::from(unpacked_cell_id.number_bits),
words: [word0, word1, word2],
version: Version::V0,
}
@ -47,36 +47,33 @@ impl From<&Address<'_>> for UnpackedCellID {
fn from(value: &Address) -> Self {
Self {
number_bits: value.number as u16,
word0_bits: value.words[0].number as u16,
word1_bits: value.words[1].number as u16,
word2_bits: value.words[2].number as u16,
word0_bits: value.words[0].number,
word1_bits: value.words[1].number,
word2_bits: value.words[2].number,
}
}
}
#[allow(clippy::use_self)]
impl From<UnpackedCellID> for u64 {
fn from(value: UnpackedCellID) -> Self {
let ten_bits = 0b1111_1111_11;
let thirteen_bits = 0b1111_1111_1111_1;
let thirteen_bits = 0b1_1111_1111_1111;
let mut ret = 0b0;
// Add words in reverse order
ret = (ret << 13) | (value.word2_bits as u64 & thirteen_bits);
ret = (ret << 13) | (value.word1_bits as u64 & thirteen_bits);
ret = (ret << 13) | (value.word0_bits as u64 & thirteen_bits);
// for word in self.words.iter().rev() {
// ret = (ret << 13) | (word.number as u64 & thirteen_bits);
// }
ret = (ret << 13) | (u64::from(value.word2_bits) & thirteen_bits);
ret = (ret << 13) | (u64::from(value.word1_bits) & thirteen_bits);
ret = (ret << 13) | (u64::from(value.word0_bits) & thirteen_bits);
// Add the number
ret = (ret << 10) | (value.number_bits as u64 & ten_bits);
ret = (ret << 10) | (u64::from(value.number_bits) & ten_bits);
// Add the final bit
ret = (ret << 1) | 0b1;
// Shift the whole id left by the number of unused levels * 2
ret = ret << ((s2::cellid::MAX_LEVEL - CELLID_LEVEL as u64) * 2);
ret <<= (s2::cellid::MAX_LEVEL - CELLID_LEVEL) * 2;
ret
}
@ -84,7 +81,7 @@ impl From<UnpackedCellID> for u64 {
impl From<UnpackedCellID> for CellID {
fn from(value: UnpackedCellID) -> Self {
CellID(value.into())
Self(value.into())
}
}

View File

@ -1,11 +1,24 @@
use common::test_events;
use s2::cellid::CellID;
use this_algorithm::Error;
use this_algorithm::{Address, CELLID_LEVEL};
#[macro_use]
mod common;
use common::CELLID_LEVEL_23_BITMASK;
use common::CELLID_LEVEL_23_END_BIT;
#[test]
fn test_invalid_lat_lon() {
assert_eq!(
Address::from_lat_lon(1.0_f64, 400.0_f64),
Err(Error::InvalidLatLng)
);
assert_eq!(
Address::from_lat_lon(1.0_f64, f64::NAN),
Err(Error::InvalidLatLng)
);
}
#[test]
fn test_cellid_translation() {
for (idx, entry) in test_events().iter().enumerate() {
@ -44,7 +57,8 @@ fn test_encoding() {
let addr = Address::from_lat_lon(entry.lat, entry.lon).unwrap();
eprintln!("({}, {}) => {addr}", entry.lat, entry.lon);
}
assert!(false);
// TODO:
// assert!(false);
}
#[test]

View File

@ -59,7 +59,7 @@ fn write_word_map(mut file: impl Write, words: &[Word]) {
writeln!(
&mut file,
r#"/// Mapping from all caps `&str` to `&'static Word`
pub static WORD_MAP: phf::Map<&'static str, &'static Word> = {};"#,
pub static WORD_MAP: phf::Map<&'static str, &Word> = {};"#,
word_map.build()
)
.unwrap();
@ -76,7 +76,7 @@ fn write_number_to_words(mut file: impl Write, words: &[Word]) {
&mut file,
// "pub static NUMBER_TO_WORDS: &[&[usize]] = &["
r#"/// Mapping from each number to its associated word
pub static NUMBER_TO_WORDS: &[&[&'static Word]] = &["#
pub static NUMBER_TO_WORDS: &[&[&Word]] = &["#
)
.unwrap();