Skip to content

Commit

Permalink
wip: add 'directory' to packages
Browse files Browse the repository at this point in the history
  • Loading branch information
poliorcetics committed Apr 26, 2024
1 parent cee594b commit ff3de6d
Show file tree
Hide file tree
Showing 8 changed files with 228 additions and 9 deletions.
1 change: 1 addition & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub async fn init(kind: Option<PackageType>, name: Option<PackageName>) -> miett
Ok(PackageManifest {
kind,
name,
directory: None,
version: INITIAL_VERSION,
description: None,
})
Expand Down
5 changes: 4 additions & 1 deletion src/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use url::Url;

use crate::{
errors::{DeserializationError, FileExistsError, FileNotFound, SerializationError, WriteError},
package::{Package, PackageName},
package::{Package, PackageDirectory, PackageName},
registry::RegistryUri,
ManagedFile,
};
Expand All @@ -41,6 +41,8 @@ pub const LOCKFILE: &str = "Proto.lock";
pub struct LockedPackage {
/// The name of the package
pub name: PackageName,
/// Directory where the package's contents are stored
pub directory: Option<PackageDirectory>,
/// The cryptographic digest of the package contents
pub digest: Digest,
/// The URI of the registry that contains the package
Expand All @@ -67,6 +69,7 @@ impl LockedPackage {
) -> Self {
Self {
name: package.name().to_owned(),
directory: package.directory().cloned(),
registry,
repository,
digest: DigestAlgorithm::SHA256.digest(&package.tgz),
Expand Down
13 changes: 12 additions & 1 deletion src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use tokio::fs;

use crate::{
errors::{DeserializationError, FileExistsError, SerializationError, WriteError},
package::{PackageName, PackageType},
package::{PackageDirectory, PackageName, PackageType},
registry::RegistryUri,
ManagedFile,
};
Expand Down Expand Up @@ -377,12 +377,23 @@ pub struct PackageManifest {
pub kind: PackageType,
/// Name of the package
pub name: PackageName,
/// Directory in which to put the cache. If unset, defaults to the package name
pub directory: Option<PackageDirectory>,
/// Version of the package
pub version: Version,
/// Description of the api package
pub description: Option<String>,
}

impl PackageManifest {
/// Get the directory where the package contents will be stored.
///
/// This fallbacks to `name` if `directory` is unset.
pub fn directory(&self) -> &str {
self.directory.as_deref().unwrap_or(self.name.as_ref())
}
}

/// Represents a single project dependency
#[derive(Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq)]
pub struct Dependency {
Expand Down
27 changes: 27 additions & 0 deletions src/package/compressed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ use crate::{
ManagedFile,
};

use super::PackageDirectory;

/// An in memory representation of a `buffrs` package
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Package {
Expand Down Expand Up @@ -208,6 +210,31 @@ impl Package {
.name
}

/// The directory of this package
#[inline]
pub fn directory(&self) -> Option<&PackageDirectory> {
assert!(self.manifest.package.is_some());

self.manifest
.package
.as_ref()
.expect("compressed package contains invalid manifest (package section missing)")
.directory
.as_ref()
}

/// Directory for this oackage
#[inline]
pub fn directory_str(&self) -> &str {
assert!(self.manifest.package.is_some());

self.manifest
.package
.as_ref()
.expect("compressed package contains invalid manifest (package section missing)")
.directory()
}

/// The version of this package
#[inline]
pub fn version(&self) -> &Version {
Expand Down
172 changes: 172 additions & 0 deletions src/package/directory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Copyright 2023 Helsing GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{fmt, ops::Deref, str::FromStr};

use miette::IntoDiagnostic;
use serde::{Deserialize, Serialize};

/// A `buffrs` package directory for parsing and type safety
#[derive(Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[serde(try_from = "String", into = "String")]
pub struct PackageDirectory(String);

/// Errors that can be generated parsing [`PackageDirectory`], see [`PackageDirectory::new()`].
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum PackageDirectoryError {
/// Empty package directory.
#[error("package directory must be at least one character long, but was empty")]
Empty,
/// Too long.
#[error("package directories must be at most 128 characters long, but was {0:}")]
TooLong(usize),
/// Invalid start character.
#[error("package directory must start with alphabetic character, but was {0:}")]
InvalidStart(char),
/// Invalid character.
#[error("package directory must consist of only ASCII lowercase and dashes (-, _), but contains {0:} at position {1:}")]
InvalidCharacter(char, usize),
}

impl super::ParseError for PackageDirectoryError {
#[inline]
fn empty() -> Self {
Self::Empty
}

#[inline]
fn too_long(current_length: usize) -> Self {
Self::TooLong(current_length)
}

#[inline]
fn invalid_start(first: char) -> Self {
Self::InvalidStart(first)
}

#[inline]
fn invalid_character(found: char, pos: usize) -> Self {
Self::InvalidCharacter(found, pos)
}
}

impl PackageDirectory {
const MAX_LENGTH: usize = 128;

/// New package directory from string.
pub fn new<S: Into<String>>(value: S) -> Result<Self, PackageDirectoryError> {
let value = value.into();
Self::validate(&value)?;
Ok(Self(value))
}

/// Validate a package directory.
pub fn validate(directory: impl AsRef<str>) -> Result<(), PackageDirectoryError> {
super::validate(directory.as_ref(), &[b'-', b'_'], Self::MAX_LENGTH)
}
}

impl TryFrom<String> for PackageDirectory {
type Error = PackageDirectoryError;

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

impl FromStr for PackageDirectory {
type Err = miette::Report;

fn from_str(input: &str) -> Result<Self, Self::Err> {
Self::new(input).into_diagnostic()
}
}

impl From<PackageDirectory> for String {
fn from(s: PackageDirectory) -> Self {
s.to_string()
}
}

impl Deref for PackageDirectory {
type Target = str;

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

impl fmt::Display for PackageDirectory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

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

#[test]
fn ascii_lowercase() {
assert_eq!(
PackageDirectory::new("abc"),
Ok(PackageDirectory("abc".into()))
);
}

#[test]
fn short() {
assert_eq!(PackageDirectory::new("a"), Ok(PackageDirectory("a".into())));
assert_eq!(
PackageDirectory::new("ab"),
Ok(PackageDirectory("ab".into()))
);
}

#[test]
fn long() {
assert_eq!(
PackageDirectory::new("a".repeat(PackageDirectory::MAX_LENGTH)),
Ok(PackageDirectory("a".repeat(PackageDirectory::MAX_LENGTH)))
);

assert_eq!(
PackageDirectory::new("a".repeat(PackageDirectory::MAX_LENGTH + 1)),
Err(PackageDirectoryError::TooLong(
PackageDirectory::MAX_LENGTH + 1
))
);
}

#[test]
fn empty() {
assert_eq!(PackageDirectory::new(""), Err(PackageDirectoryError::Empty));
}

#[test]
fn numeric_start() {
assert_eq!(
PackageDirectory::new("4abc"),
Err(PackageDirectoryError::InvalidStart('4'))
);
}

#[test]
fn underscore_and_dash() {
assert_eq!(
PackageDirectory::new("with_underscore-and-dash"),
Ok(PackageDirectory("with_underscore-and-dash".into())),
);
}
}
6 changes: 5 additions & 1 deletion src/package/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@
// limitations under the License.

mod compressed;
mod directory;
mod name;
mod store;
mod r#type;

pub use self::{compressed::Package, name::PackageName, r#type::PackageType, store::PackageStore};
pub use self::{
compressed::Package, directory::PackageDirectory, name::PackageName, r#type::PackageType,
store::PackageStore,
};

trait ParseError {
fn empty() -> Self;
Expand Down
10 changes: 5 additions & 5 deletions src/package/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl PackageStore {

/// Path to where the package contents are populated.
fn populated_path(&self, manifest: &PackageManifest) -> PathBuf {
self.proto_vendor_path().join(manifest.name.to_string())
self.proto_vendor_path().join(manifest.directory())
}

/// Creates the expected directory structure for `buffrs`
Expand Down Expand Up @@ -96,7 +96,7 @@ impl PackageStore {

/// Unpacks a package into a local directory
pub async fn unpack(&self, package: &Package) -> miette::Result<()> {
let pkg_dir = self.locate(package.name());
let pkg_dir = self.locate(package.directory_str());

package.unpack(&pkg_dir).await?;

Expand Down Expand Up @@ -181,8 +181,8 @@ impl PackageStore {
}

/// Directory for the vendored installation of a package
pub fn locate(&self, package: &PackageName) -> PathBuf {
self.proto_vendor_path().join(&**package)
pub fn locate(&self, directory: &str) -> PathBuf {
self.proto_vendor_path().join(directory)
}

/// Collect .proto files in a given path
Expand Down Expand Up @@ -214,7 +214,7 @@ impl PackageStore {
/// Sync this stores proto files to the vendor directory
pub async fn populate(&self, manifest: &PackageManifest) -> miette::Result<()> {
let source_path = self.proto_path();
let target_dir = self.proto_vendor_path().join(manifest.name.to_string());
let target_dir = self.populated_path(manifest);

if tokio::fs::try_exists(&target_dir)
.await
Expand Down
3 changes: 2 additions & 1 deletion src/registry/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl LocalRegistry {
let path = self.base_dir.join(PathBuf::from(format!(
"{}/{}/{}-{}.tgz",
repository,
package.name(),
package.directory_str(),
package.name(),
package.version(),
)));
Expand Down Expand Up @@ -115,6 +115,7 @@ mod tests {
Some(PackageManifest {
kind: PackageType::Api,
name: "test-api".parse().unwrap(),
directory: None,
version: "0.1.0".parse().unwrap(),
description: None,
}),
Expand Down

0 comments on commit ff3de6d

Please sign in to comment.