geolite 0.0.18

open source geocode solution
use std::fmt;

use crate::admin_level::id::{admin_level_id, osm_element_kind};

const SEPARATOR: char = '.';
const HOUSE_NUMBER_MARK: &str = ".n";

pub struct address_id {
  pub iso: String,
  pub leaf: i64,
  pub path: Vec<i64>,
  pub house_number: Option<String>,
}

impl address_id {
  pub fn encode(iso: &str, leaf: i64, path: &[i64], house_number: Option<&str>) -> String {
    let mut encoded = String::with_capacity(3 + (path.len() + 1) * 12);
    encoded.push_str(&iso.to_ascii_lowercase());
    for id in path.iter().rev().chain(std::iter::once(&leaf)) {
      encoded.push(SEPARATOR);
      push_element(&mut encoded, *id);
    }
    if let Some(number) = house_number {
      encoded.push_str(HOUSE_NUMBER_MARK);
      encoded.push_str(number);
    }
    encoded
  }

  pub fn parse(raw: &str) -> Option<Self> {
    let lowered = raw.trim().to_ascii_lowercase();
    let (elements, house_number) = match lowered.split_once(HOUSE_NUMBER_MARK) {
      Some((_, "")) => return None,
      Some((elements, number)) => (elements, Some(number.to_string())),
      None => (lowered.as_str(), None),
    };
    let mut segments = elements.split(SEPARATOR);
    let iso = segments.next()?;
    if iso.len() != 2 || !iso.bytes().all(|b| b.is_ascii_lowercase()) {
      return None;
    }
    let mut ids: Vec<i64> = Vec::new();
    for segment in segments {
      ids.push(parse_element(segment)?);
    }
    let leaf = ids.pop()?;
    ids.reverse();
    Some(address_id {
      iso: iso.to_string(),
      leaf,
      path: ids,
      house_number,
    })
  }
}

impl fmt::Display for address_id {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    f.write_str(&Self::encode(
      &self.iso,
      self.leaf,
      &self.path,
      self.house_number.as_deref(),
    ))
  }
}

fn push_element(encoded: &mut String, id: i64) {
  let element = admin_level_id::from_raw(id as u64);
  encoded.push(match element.kind() {
    osm_element_kind::relation => 'r',
    osm_element_kind::way => 'w',
  });
  encoded.push_str(&element.osm_id().to_string());
}

fn parse_element(segment: &str) -> Option<i64> {
  let element_of: fn(u64) -> admin_level_id = match segment.as_bytes().first()? {
    b'r' => admin_level_id::from_relation,
    b'w' => admin_level_id::from_way,
    _ => return None,
  };
  let digits = &segment[1..];
  if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) {
    return None;
  }
  let osm_id: u64 = digits.parse().ok()?;
  if osm_id >> 62 != 0 {
    return None;
  }
  Some(element_of(osm_id).raw() as i64)
}