turntable 0.0.1

Turntable is unpkg copy
Documentation
use std::str::FromStr;

use poem::{FromRequest, Request, RequestBody};
use urlencoding::decode;

use crate::errors::AppError;

#[derive(Debug, Clone, Default)]
pub struct PackagePathname {
  pub package_name: String,
  pub package_version: String,
  pub package_spec: String,
  pub filename: String,
}

#[poem::async_trait]
impl<'a> FromRequest<'a> for &'a PackagePathname {
  async fn from_request(req: &'a Request, _: &mut RequestBody) -> poem::Result<Self> {
    req
      .extensions()
      .get::<PackagePathname>()
      .ok_or(anyhow::anyhow!(
        "get package pathname from the request extensions"
      ))
      .map_err(Into::into)
  }
}

impl FromStr for PackagePathname {
  type Err = AppError;

  fn from_str(pathname: &str) -> poem::Result<Self, Self::Err> {
    let pathname = decode(pathname).map_err(|_| AppError::InvalidURL(pathname.to_owned()))?;

    let package_pathname_format = regex!("^/((?:@[^/@]+/)?[^/@]+)(?:@([^/]+))?(/.*)?$");
    let captures = package_pathname_format.captures(&pathname);

    match captures {
      Some(matched) => {
        let package_name = matched.get(1).map(|s| s.as_str()).unwrap_or_default();
        let package_version = matched.get(2).map(|s| s.as_str()).unwrap_or("latest");
        let filename = matched
          .get(3)
          .map(|s| regex!(r"//+").replace_all(s.as_str(), "/").to_string())
          .unwrap_or_default();

        Ok(PackagePathname {
          package_name: package_name.into(),
          package_version: package_version.into(),
          package_spec: format!("{}@{}", package_name, package_version),
          filename,
        })
      }
      None => Err(AppError::InvalidURL(pathname.to_string())),
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_parse_package_pathname() {
    let parsed: PackagePathname = "/@scope/name@version/file.js".parse().unwrap();
    assert_eq!(parsed.package_name, "@scope/name");
    assert_eq!(parsed.package_version, "version");
    assert_eq!(parsed.package_spec, "@scope/name@version");
    assert_eq!(parsed.filename, "/file.js");
  }
}