Skip to content
This repository has been archived by the owner on May 7, 2024. It is now read-only.

Add typing to manifest.package.name #7

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
140 changes: 139 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,122 @@ pub static README_PATHS: &[&str; 5] = &[

pub static LICENSE_PATHS: &[&str; 3] = &["LICENSE", "LICENSE.md", "COPYING"];

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PackageName {
pub namespace: Namespace,
pub name: String,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Namespace {
/// A named entity (e.g. a user or organisation).
Named(String),
/// The special namespace granted to "official" packages.
Underscore,
}

impl Namespace {
pub fn parse(s: &str) -> Result<Self, &'static str> {
if s.chars()
.any(|c| !(char::is_alphanumeric(c) || c == '_' || c == '-'))
{
return Err("invalid characters in namespace, only alphanumeric, _ and - allowed");
}
match s {
"_" => Ok(Self::Underscore),
other => Ok(Self::Named(other.to_string())),
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Namespace::Named(s) => Some(s),
Namespace::Underscore => None,
}
}
}

impl std::str::FromStr for Namespace {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}

impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Namespace::Named(s) => write!(f, "{s}"),
Namespace::Underscore => write!(f, "_"),
}
}
}

impl PackageName {
/// Parses the package name from a `namespace/name` format
pub fn parse(s: &str) -> Result<Self, &'static str> {
fschutt marked this conversation as resolved.
Show resolved Hide resolved
if !s.contains('/') {
return Err("no / in package name");
}
let mut split = s.split('/');
let namespace = split.next().ok_or("no namespace in package name")?;
let namespace = Namespace::parse(namespace)?;
let name = split.next().ok_or("no name in package name")?.to_string();
if name
.chars()
.any(|c| !(char::is_alphanumeric(c) || c == '_' || c == '-'))
|| split.next().is_some()
{
return Err("invalid characters in name, only alphanumeric, _ and - allowed");
}
Ok(Self { name, namespace })
}

/// Returns the `namespace/name` package name
pub fn to_string(&self) -> String {
format!("{}", self)
}
fschutt marked this conversation as resolved.
Show resolved Hide resolved
}

impl std::str::FromStr for PackageName {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}

impl fmt::Display for PackageName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}", self.namespace, self.name)
}
}

mod serde_package_name {
use super::PackageName;
use serde::{Deserialize, Deserializer, Serializer};

pub fn serialize<S>(package: &PackageName, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&package.to_string())
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<PackageName, D::Error>
where
D: Deserializer<'de>,
{
let buf = String::deserialize(deserializer)?;
PackageName::parse(&buf).map_err(serde::de::Error::custom)
}
}
fschutt marked this conversation as resolved.
Show resolved Hide resolved

/// Describes a command for a wapm module
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Package {
pub name: String,
#[serde(with = "serde_package_name")]
pub name: PackageName,
pub version: Version,
pub description: String,
pub license: Option<String>,
Expand Down Expand Up @@ -966,3 +1078,29 @@ annotations = { file = "Runefile.yml", kind = "yaml" }
);
}
}

#[test]
fn test_package_name_parse() {
fschutt marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(
PackageName::parse("hello").unwrap_err(),
"no / in package name"
);
assert_eq!(
PackageName::parse("hello/test").unwrap(),
PackageName {
namespace: Namespace::Named("hello".to_string()),
name: "test".to_string()
}
);
assert_eq!(
PackageName::parse("_/test").unwrap(),
PackageName {
namespace: Namespace::Underscore,
name: "test".to_string()
}
);
assert_eq!(
PackageName::parse("_/_/test").unwrap_err(),
"invalid characters in name, only alphanumeric, _ and - allowed",
);
}