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); let latlng = LatLng::from_degrees(lat, lon);
// We don't want to entertain any invalid latitudes or longitudes // We don't want to entertain any invalid latitudes or longitudes
if latlng.normalized() != latlng { if latlng.normalized() == latlng {
Err(Error::InvalidLatLng)
} else {
Ok(Into::<CellID>::into(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()))) (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 /// The minimum number value for V0
pub const V0_MIN_NUMBER: u32 = 1; pub const V0_MIN_NUMBER: u32 = 1;
// Any encoding or decoding error
#[derive(Error, Debug, Eq, PartialEq)] #[derive(Error, Debug, Eq, PartialEq)]
pub enum Error { pub enum Error {
/// The given word is not in the wordlist
#[error("Word does not exist: {0}")] #[error("Word does not exist: {0}")]
WordDoesNotExist(String), WordDoesNotExist(String),
#[error("Invalid version: {0}")] /// The encoded version bit is not supported by this version of the crate
InvalidVersion(u8), #[error("Unimplemented version: {0}")]
#[error("Unimplemented version")] UnimplementedVersion(u8),
UnimplementedVersion(Version), /// The number component in the address is too large
#[error("Number out of range")] #[error("Number out of range")]
NumberOutOfRange(Number), NumberOutOfRange(Number),
#[error("Invalid encoding")] /// The string to decode is not ascii
InvalidEncoding, #[error("The string given is not ascii")]
NonAscii,
/// The number of components given to decode is incorrect
#[error("Wrong number of components")] #[error("Wrong number of components")]
WrongComponentCount, WrongComponentCount,
/// No number component was found
///
/// See [`Algorithm::from_str`] for the possible position of the number component
#[error("No number component")] #[error("No number component")]
NoNumberComponent, NoNumberComponent,
/// The empty string was given to decode
#[error("Empty string given")] #[error("Empty string given")]
Empty, 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")] #[error("Invalid latitude or longitude")]
InvalidLatLng, InvalidLatLng,
} }
#[allow(clippy::doc_markdown)]
/// An encoded this_algorithm address /// An encoded this_algorithm address
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Address<'a> { pub struct Address<'a> {
@ -62,14 +80,6 @@ pub enum Version {
V0, V0,
} }
// pub trait Alg {
// type Err;
// fn to_cellid(&self) -> Result<CellID, Self::Err>;
// fn from_cellid();
// }
impl FromStr for Address<'_> { impl FromStr for Address<'_> {
type Err = Error; type Err = Error;
@ -77,9 +87,15 @@ impl FromStr for Address<'_> {
/// ///
/// * `0000 WORD0 WORD1 WORD2` (regular) /// * `0000 WORD0 WORD1 WORD2` (regular)
/// * `WORD2 WORD1 WORD0 0000` (reversed) /// * `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> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.is_ascii() { if !s.is_ascii() {
return Err(Error::InvalidEncoding); return Err(Error::NonAscii);
} }
let s = s.trim().to_ascii_uppercase(); let s = s.trim().to_ascii_uppercase();
@ -109,12 +125,19 @@ impl FromStr for Address<'_> {
components.into_iter().skip(1).collect::<Vec<_>>() components.into_iter().skip(1).collect::<Vec<_>>()
}; };
Address::from_components(number, other_components) Address::from_components(number, &other_components)
} }
} }
impl Address<'_> { impl Address<'_> {
/// Converts a latitude and longitude into an encoded 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> { pub fn from_lat_lon(lat: f64, lon: f64) -> Result<Self, Error> {
let cellid = conversions::lat_lon_to_cellid(lat, lon)?; let cellid = conversions::lat_lon_to_cellid(lat, lon)?;
@ -122,11 +145,13 @@ impl Address<'_> {
} }
/// Constructs an address from the given cellid /// Constructs an address from the given cellid
#[must_use]
pub fn from_cellid(cellid: CellID) -> Self { pub fn from_cellid(cellid: CellID) -> Self {
v0::UnpackedCellID::from(cellid).into() v0::UnpackedCellID::from(cellid).into()
} }
/// Constructs an address from a u64 representation of a cellid /// Constructs an address from a u64 representation of a cellid
#[must_use]
pub fn from_cellid_u64(cellid_u64: u64) -> Self { pub fn from_cellid_u64(cellid_u64: u64) -> Self {
v0::UnpackedCellID::from(CellID(cellid_u64)).into() v0::UnpackedCellID::from(CellID(cellid_u64)).into()
} }
@ -137,7 +162,7 @@ impl Address<'_> {
// } // }
// Parses an address v0 given the number and word components // 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) { if !(V0_MIN_NUMBER..=V0_MAX_NUMBER).contains(&number) {
return Err(Error::NumberOutOfRange(number)); return Err(Error::NumberOutOfRange(number));
} }
@ -150,7 +175,9 @@ impl Address<'_> {
let words = TryInto::<[&'static Word; 3]>::try_into( let words = TryInto::<[&'static Word; 3]>::try_into(
word_components word_components
.iter() .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>>()?, .collect::<Result<Vec<&'static Word>, Error>>()?,
) )
// This unwrap is okay because we just checked to make sure the number of word components was 3 // 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: &[&str]) -> Result<Self, Error> {
fn from_components(number: Number, other_components: Vec<&str>) -> Result<Self, Error> {
match extract_version(number)? { match extract_version(number)? {
Version::V0 => Self::parse_v0(number, other_components), Version::V0 => Self::parse_v0(number, other_components),
} }
} }
/// Get the address as a [`CellID`] value
#[must_use]
pub fn as_cell_id(&self) -> CellID { pub fn as_cell_id(&self) -> CellID {
match self.version { match self.version {
Version::V0 => { Version::V0 => UnpackedCellID::from(self).into(),
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)
}
} }
} }
// TODO: Test this // TODO: Test this
#[must_use]
pub fn to_lat_lon(&self) -> (f64, f64) { pub fn to_lat_lon(&self) -> (f64, f64) {
let lat_lng = LatLng::from(self.as_cell_id()); let lat_lng = LatLng::from(self.as_cell_id());
(lat_lng.lat.deg(), lat_lng.lng.deg()) (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 /// The version number is set by the two bits 11 and 12
// TODO: impl TryFrom ? // 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 { match ((number >> 10) & 0b11) as u8 {
0 => Ok(Version::V0), 0 => Ok(Version::V0),
v => Err(Error::InvalidVersion(v)), v => Err(Error::UnimplementedVersion(v)),
} }
} }
@ -299,41 +307,41 @@ mod tests {
fn test_parse_v0() { fn test_parse_v0() {
// Regular case // Regular case
assert_eq!( assert_eq!(
Address::parse_v0(1000, vec!["apple", "orange", "grape"]), Address::parse_v0(1000, &["apple", "orange", "grape"]),
Ok(addr![1000, apple, orange, grape]) Ok(addr![1000, apple, orange, grape])
); );
// Number is on the edge // Number is on the edge
assert_eq!( assert_eq!(
Address::parse_v0(1, vec!["apple", "orange", "grape"]), Address::parse_v0(1, &["apple", "orange", "grape"]),
Ok(addr![1, apple, orange, grape]) Ok(addr![1, apple, orange, grape])
); );
assert_eq!( assert_eq!(
Address::parse_v0(1024, vec!["apple", "orange", "grape"]), Address::parse_v0(1024, &["apple", "orange", "grape"]),
Ok(addr![1024, apple, orange, grape]) Ok(addr![1024, apple, orange, grape])
); );
assert_eq!( 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]) Ok(addr![V0_MAX_NUMBER, apple, orange, grape])
); );
assert_eq!( 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]) Ok(addr![V0_MIN_NUMBER, apple, orange, grape])
); );
// Word not found // 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 // Too few words
assert!(Address::parse_v0(1000, vec!["apple", "orange"]).is_err()); assert!(Address::parse_v0(1000, &["apple", "orange"]).is_err());
assert!(Address::parse_v0(1000, vec!["apple"]).is_err()); assert!(Address::parse_v0(1000, &["apple"]).is_err());
assert!(Address::parse_v0(1000, vec![]).is_err()); assert!(Address::parse_v0(1000, &[]).is_err());
// Too many words // 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 // Number too large or small
assert!(Address::parse_v0(10_000, vec!["apple", "orange", "grape"]).is_err()); assert!(Address::parse_v0(10_000, &["apple", "orange", "grape"]).is_err());
assert!(Address::parse_v0(0, vec!["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]; let word2 = words::NUMBER_TO_WORDS[unpacked_cell_id.word2_bits as usize][0];
Self { Self {
number: unpacked_cell_id.number_bits as u32, number: u32::from(unpacked_cell_id.number_bits),
words: [word0, word1, word2], words: [word0, word1, word2],
version: Version::V0, version: Version::V0,
} }
@ -47,36 +47,33 @@ impl From<&Address<'_>> for UnpackedCellID {
fn from(value: &Address) -> Self { fn from(value: &Address) -> Self {
Self { Self {
number_bits: value.number as u16, number_bits: value.number as u16,
word0_bits: value.words[0].number as u16, word0_bits: value.words[0].number,
word1_bits: value.words[1].number as u16, word1_bits: value.words[1].number,
word2_bits: value.words[2].number as u16, word2_bits: value.words[2].number,
} }
} }
} }
#[allow(clippy::use_self)]
impl From<UnpackedCellID> for u64 { impl From<UnpackedCellID> for u64 {
fn from(value: UnpackedCellID) -> Self { fn from(value: UnpackedCellID) -> Self {
let ten_bits = 0b1111_1111_11; let ten_bits = 0b1111_1111_11;
let thirteen_bits = 0b1111_1111_1111_1; let thirteen_bits = 0b1_1111_1111_1111;
let mut ret = 0b0; let mut ret = 0b0;
// Add words in reverse order // Add words in reverse order
ret = (ret << 13) | (value.word2_bits as u64 & thirteen_bits); ret = (ret << 13) | (u64::from(value.word2_bits) & thirteen_bits);
ret = (ret << 13) | (value.word1_bits as u64 & thirteen_bits); ret = (ret << 13) | (u64::from(value.word1_bits) & thirteen_bits);
ret = (ret << 13) | (value.word0_bits as u64 & thirteen_bits); ret = (ret << 13) | (u64::from(value.word0_bits) & thirteen_bits);
// for word in self.words.iter().rev() {
// ret = (ret << 13) | (word.number as u64 & thirteen_bits);
// }
// Add the number // 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 // Add the final bit
ret = (ret << 1) | 0b1; ret = (ret << 1) | 0b1;
// Shift the whole id left by the number of unused levels * 2 // 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 ret
} }
@ -84,7 +81,7 @@ impl From<UnpackedCellID> for u64 {
impl From<UnpackedCellID> for CellID { impl From<UnpackedCellID> for CellID {
fn from(value: UnpackedCellID) -> Self { fn from(value: UnpackedCellID) -> Self {
CellID(value.into()) Self(value.into())
} }
} }

View File

@ -1,11 +1,24 @@
use common::test_events; use common::test_events;
use s2::cellid::CellID; use s2::cellid::CellID;
use this_algorithm::Error;
use this_algorithm::{Address, CELLID_LEVEL}; use this_algorithm::{Address, CELLID_LEVEL};
#[macro_use] #[macro_use]
mod common; mod common;
use common::CELLID_LEVEL_23_BITMASK; use common::CELLID_LEVEL_23_BITMASK;
use common::CELLID_LEVEL_23_END_BIT; 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] #[test]
fn test_cellid_translation() { fn test_cellid_translation() {
for (idx, entry) in test_events().iter().enumerate() { 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(); let addr = Address::from_lat_lon(entry.lat, entry.lon).unwrap();
eprintln!("({}, {}) => {addr}", entry.lat, entry.lon); eprintln!("({}, {}) => {addr}", entry.lat, entry.lon);
} }
assert!(false); // TODO:
// assert!(false);
} }
#[test] #[test]

View File

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