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

Commit

Permalink
new: Use strict types for metadata fields. (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored Aug 9, 2023
1 parent ae6b202 commit 94b9027
Show file tree
Hide file tree
Showing 29 changed files with 787 additions and 218 deletions.
245 changes: 97 additions & 148 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@ resolver = "2"
members = ["crates/*"]

[workspace.dependencies]
clap = { version = "4.3.19", default-features = false, features = ["std"] }
clap = { version = "4.3.21", default-features = false, features = ["std"] }
miette = "5.10.0"
schematic = { version = "0.11.0", default-features = false, features = [
once_cell = "1.18.0"
relative-path = { version = "1.8.0", features = ["serde"] }
schematic = { version = "0.11.1", default-features = false, features = [
"toml",
"valid_url",
] }
semver = "1.0.18"
serde = "1.0.177"
serde = "1.0.183"
starbase = "0.2.0"
starbase_sandbox = { version = "0.1.8" }
starbase_styles = "0.1.12"
starbase_utils = { version = "0.2.14", default-features = false, features = [
starbase_utils = { version = "0.2.17", default-features = false, features = [
"glob",
] }
thiserror = "1.0.44"
tokio = { version = "1.29.1", features = ["full", "tracing"] }
tracing = "0.1.37"
url = { version = "2.4.0", features = ["serde"] }
5 changes: 5 additions & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ license = "MIT"

[dependencies]
clap = { workspace = true }
miette = { workspace = true }
once_cell = { workspace = true }
regex = "1.9.3"
schematic = { workspace = true }
serde = { workspace = true }
spdx = "0.10.2"
thiserror = { workspace = true }
6 changes: 6 additions & 0 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
#![allow(clippy::from_over_into)]

mod license_type;
mod package_name;
mod target;

pub use license_type::*;
pub use package_name::*;
pub use target::*;
44 changes: 44 additions & 0 deletions crates/common/src/license_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use schematic::{SchemaType, Schematic};
use serde::{Deserialize, Serialize};
use spdx::Expression;
use std::ops::Deref;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(try_from = "String", into = "String")]
pub struct LicenseType(Expression);

impl LicenseType {
pub fn parse(value: &str) -> Result<Self, spdx::ParseError> {
Ok(Self(Expression::parse(value)?))
}
}

impl TryFrom<String> for LicenseType {
type Error = spdx::ParseError;

fn try_from(value: String) -> Result<Self, Self::Error> {
LicenseType::parse(&value)
}
}

impl Into<String> for LicenseType {
fn into(self) -> String {
self.0.to_string()
}
}

impl Deref for LicenseType {
type Target = Expression;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl Eq for LicenseType {}

impl Schematic for LicenseType {
fn generate_schema() -> SchemaType {
SchemaType::string()
}
}
148 changes: 148 additions & 0 deletions crates/common/src/package_name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use miette::Diagnostic;
use once_cell::sync::Lazy;
use regex::Regex;
use schematic::{validate::HasLength, SchemaType, Schematic};
use serde::{Deserialize, Serialize};
use thiserror::Error;

#[derive(Debug, Diagnostic, Error)]
pub enum PackageNameError {
#[diagnostic(code(package::name::not_empty))]
#[error("Package name must not be empty.")]
Empty,

#[diagnostic(code(package::name::no_repeating_dashes))]
#[error("Repeating dashes are not allowed in package names.")]
NoRepeatingDashes,

#[diagnostic(code(package::name::missing_namespace))]
#[error("Missing namespace from package name.")]
MissingNamespace,

#[diagnostic(code(package::name::invalid_namespace))]
#[error("Only alpha-numeric characters and dashes are allowed in the package namespace.")]
InvalidNamespace,

#[diagnostic(code(package::name::namespace_length))]
#[error("Package namespace (left of /) may only be 2-32 characters in length.")]
NamespaceLength,

#[diagnostic(code(package::name::invalid_name))]
#[error("Only alpha-numeric characters and dashes are allowed in the package name.")]
InvalidName,

#[diagnostic(code(package::name::name_length))]
#[error("Package name (right of /) may only be 2-32 characters in length.")]
NameLength,
}

pub static NAME_SEPARATOR: char = '/';

pub static COMPONENT_PATTERN: Lazy<Regex> =
Lazy::new(|| Regex::new("^([a-z][a-z0-9-]{0,30}[a-z0-9])$").unwrap());

pub static REPEATING_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new("(-{2,})").unwrap());

fn components(value: &str) -> (&str, &str) {
let mut comps = value.split(NAME_SEPARATOR);

(comps.next().unwrap(), comps.next().unwrap())
}

#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(try_from = "String", into = "String")]
pub struct PackageName(String);

impl PackageName {
pub fn parse(value: &str) -> Result<Self, PackageNameError> {
if value.is_empty() {
return Err(PackageNameError::Empty);
} else if !value.contains(NAME_SEPARATOR) {
return Err(PackageNameError::MissingNamespace);
} else if REPEATING_PATTERN.is_match(value) {
return Err(PackageNameError::NoRepeatingDashes);
}

let (namespace, package) = components(value);

// Check namespace first
if namespace.len() < 2 || namespace.len() > 32 {
return Err(PackageNameError::NamespaceLength);
}

if !COMPONENT_PATTERN.is_match(namespace) {
return Err(PackageNameError::InvalidNamespace);
}

// Then check package
if package.len() < 2 || package.len() > 32 {
return Err(PackageNameError::NameLength);
}

if !COMPONENT_PATTERN.is_match(package) {
return Err(PackageNameError::InvalidName);
}

Ok(Self(value.to_owned()))
}

pub fn as_str(&self) -> &str {
&self.0
}

pub fn components(&self) -> (&str, &str) {
components(&self.0)
}

pub fn namespace(&self) -> &str {
self.components().0
}

pub fn package(&self) -> &str {
self.components().1
}
}

impl TryFrom<String> for PackageName {
type Error = PackageNameError;

fn try_from(value: String) -> Result<Self, PackageNameError> {
PackageName::parse(&value)
}
}

impl Into<String> for PackageName {
fn into(self) -> String {
self.0
}
}

impl AsRef<str> for PackageName {
fn as_ref(&self) -> &str {
&self.0
}
}

impl AsRef<String> for PackageName {
fn as_ref(&self) -> &String {
&self.0
}
}

impl AsRef<PackageName> for PackageName {
fn as_ref(&self) -> &PackageName {
self
}
}

impl HasLength for PackageName {
fn length(&self) -> usize {
self.0.len()
}
}

impl Schematic for PackageName {
fn generate_schema() -> SchemaType {
SchemaType::string()
}
}
Loading

0 comments on commit 94b9027

Please sign in to comment.