Skip to content

Commit

Permalink
Implements Package::open()
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Jan 27, 2024
1 parent 7bfd0dc commit 8a950de
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 16 deletions.
15 changes: 10 additions & 5 deletions stage0/src/pkg/dep.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::{
Package, PackageName, PackageNameError, PackageOpenError, PackageUnpackError, PackageVersion,
TargetResolver,
};
use serde::Serialize;
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
Expand Down Expand Up @@ -32,7 +33,11 @@ impl DependencyResolver {
}
}

pub fn resolve(&self, id: &Dependency) -> Result<Rc<Package>, DependencyResolveError> {
pub fn resolve(
&self,
id: &Dependency,
targets: &TargetResolver,
) -> Result<Rc<Package>, DependencyResolveError> {
// Check if already loaded.
let mut loaded = self.loaded.borrow_mut();

Expand All @@ -46,7 +51,7 @@ impl DependencyResolver {
let cache = self.cache.join(format!("{}-{}", id.name, id.version));

match cache.symlink_metadata() {
Ok(_) => match Package::open(&cache) {
Ok(_) => match Package::open(&cache, targets) {
Ok(v) => {
let pkg = Rc::new(v);
assert!(loaded.insert(id.clone(), pkg.clone()).is_none());
Expand Down Expand Up @@ -75,7 +80,7 @@ impl DependencyResolver {
Package::unpack(pkg, &cache).map_err(|e| DependencyResolveError::UnpackPackageFailed(e))?;

// Open the package.
match Package::open(&cache) {
match Package::open(&cache, targets) {
Ok(v) => {
let pkg = Rc::new(v);
assert!(loaded.insert(id.clone(), pkg.clone()).is_none());
Expand All @@ -87,7 +92,7 @@ impl DependencyResolver {
}

/// A package dependency.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Dependency {
name: PackageName,
version: PackageVersion,
Expand Down
61 changes: 61 additions & 0 deletions stage0/src/pkg/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,51 @@ impl Library {
Self { bin, types }
}

pub fn open<B, T>(bin: B, types: T) -> Result<Self, LibraryError>
where
B: AsRef<Path>,
T: AsRef<Path>,
{
// Read binary magic.
let bin = bin.as_ref();
let mut file =
File::open(bin).map_err(|e| LibraryError::OpenFileFailed(bin.to_owned(), e))?;
let mut magic = [0u8; 4];

file.read_exact(&mut magic)
.map_err(|e| LibraryError::ReadFileFailed(bin.to_owned(), e))?;

// Check binary type.
let bin = if &magic == b"\x7FNLS" {
let mut name = String::new();
file.read_to_string(&mut name)
.map_err(|e| LibraryError::ReadFileFailed(bin.to_owned(), e))?;
LibraryBinary::System(name)
} else {
LibraryBinary::Bundle(bin.to_owned())
};

// Load types.
let path = types.as_ref();
let mut file =
File::open(path).map_err(|e| LibraryError::OpenFileFailed(path.to_owned(), e))?;
let mut types = HashSet::new();

loop {
let ty = match TypeDeclaration::deserialize(&mut file) {
Ok(v) => v,
Err(TypeDeserializeError::EmptyData) => break,
Err(e) => return Err(LibraryError::ReadTypeFailed(path.to_owned(), e)),
};

if !types.insert(ty) {
return Err(LibraryError::DuplicatedType(path.to_owned()));
}
}

Ok(Self { bin, types })
}

pub fn bin(&self) -> &LibraryBinary {
&self.bin
}
Expand Down Expand Up @@ -161,6 +206,22 @@ pub enum LibraryBinary {
System(String),
}

/// Represents an error when [`Library`] is failed to construct.
#[derive(Debug, Error)]
pub enum LibraryError {
#[error("cannot open {0}")]
OpenFileFailed(PathBuf, #[source] std::io::Error),

#[error("cannot read {0}")]
ReadFileFailed(PathBuf, #[source] std::io::Error),

#[error("cannot read type declaration from {0}")]
ReadTypeFailed(PathBuf, #[source] TypeDeserializeError),

#[error("duplicated type declaration in {0}")]
DuplicatedType(PathBuf),
}

/// Represents an error when [`Library`] is failed to unpack from a serialized data.
#[derive(Debug, Error)]
pub enum LibraryUnpackError {
Expand Down
131 changes: 124 additions & 7 deletions stage0/src/pkg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ pub use self::lib::*;
pub use self::meta::*;
pub use self::target::*;
pub use self::ty::*;

use crate::zstd::{ZstdReader, ZstdWriter};
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Read;
use std::io::{Seek, SeekFrom, Write};
use std::fs::{read_dir, File};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use thiserror::Error;
Expand Down Expand Up @@ -353,8 +351,100 @@ impl Package {
Ok(())
}

pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, PackageOpenError> {
todo!()
pub fn open(
path: impl AsRef<Path>,
targets: &TargetResolver,
) -> Result<Self, PackageOpenError> {
let root = path.as_ref();

// Open metadata.
let path = root.join("meta.yml");
let meta = match File::open(&path) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::OpenFileFailed(path, e)),
};

// Read metadata.
let meta = match serde_yaml::from_reader(meta) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::ReadPackageMetaFailed(path, e)),
};

// Enumerate libraries.
let mut libs = HashMap::new();
let path = root.join("libs");
let items = match read_dir(&path) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::OpenDirectoryFailed(path, e)),
};

for item in items {
let item = match item {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::OpenDirectoryFailed(path, e)),
};

// Check if directory.
let path = item.path();
let meta = match std::fs::metadata(&path) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::GetFileMetaFailed(path, e)),
};

if !meta.is_dir() {
continue;
}

// Check if directory name is UTF-8.
let name = match path.file_name().unwrap().to_str() {
Some(v) => v,
None => return Err(PackageOpenError::InvalidLibraryDirectory(path)),
};

// Get target ID.
let target: Uuid = match name.parse() {
Ok(v) => v,
Err(_) => return Err(PackageOpenError::InvalidLibraryDirectory(path)),
};

// Resolve target.
let target = match targets.resolve(&target) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::ResolveTargetFailed(target, e)),
};

// Load library.
let bin = match Library::open(path.join("bin"), path.join("types")) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::OpenLibraryFailed(e)),
};

// Open dependencies.
let path = path.join("deps.yml");
let deps = match File::open(&path) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::OpenFileFailed(path, e)),
};

// Read dependencies.
let list: Vec<Dependency> = match serde_yaml::from_reader(deps) {
Ok(v) => v,
Err(e) => return Err(PackageOpenError::ReadDependenciesFailed(path, e)),
};

// Build dependency list.
let mut deps = HashSet::with_capacity(list.len());

for dep in list {
if let Some(dep) = deps.replace(dep) {
return Err(PackageOpenError::DuplicatedDependency(path, dep));
}
}

assert!(libs.insert(target, Binary::new(bin, deps)).is_none());
}

Ok(Self::new(meta, HashMap::new(), libs))
}
}

Expand All @@ -376,7 +466,34 @@ impl<T> Binary<T> {

/// Represents an error when a package is failed to open.
#[derive(Debug, Error)]
pub enum PackageOpenError {}
pub enum PackageOpenError {
#[error("cannot open {0}")]
OpenFileFailed(PathBuf, #[source] std::io::Error),

#[error("cannot open {0}")]
OpenDirectoryFailed(PathBuf, #[source] std::io::Error),

#[error("cannot get metadata of {0}")]
GetFileMetaFailed(PathBuf, #[source] std::io::Error),

#[error("cannot read package metadata from {0}")]
ReadPackageMetaFailed(PathBuf, #[source] serde_yaml::Error),

#[error("name of {0} is not a valid name for library directory")]
InvalidLibraryDirectory(PathBuf),

#[error("cannot resolve target {0}")]
ResolveTargetFailed(Uuid, #[source] TargetResolveError),

#[error("cannot open library")]
OpenLibraryFailed(#[source] LibraryError),

#[error("cannot read dependencies from {0}")]
ReadDependenciesFailed(PathBuf, #[source] serde_yaml::Error),

#[error("multiple definition of {1} in {0}")]
DuplicatedDependency(PathBuf, Dependency),
}

/// Represents an error when a package is failed to pack.
#[derive(Debug, Error)]
Expand Down
11 changes: 10 additions & 1 deletion stage0/src/pkg/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@ use std::str::FromStr;
use thiserror::Error;
use uuid::{uuid, Uuid};

/// A struct to resolve primitive target.
/// Struct to resolve [`Target`] from identifier.
pub struct TargetResolver {}

impl TargetResolver {
pub fn new() -> Self {
Self {}
}

pub fn resolve(&self, id: &Uuid) -> Result<Target, TargetResolveError> {
// Check if primitive target.
if let Some(v) = PrimitiveTarget::ALL.iter().find(|&t| t.id == *id) {
return Ok(Target::Primitive(v));
}

todo!()
}

pub fn primitive(
&self,
target: &Target,
Expand Down
20 changes: 18 additions & 2 deletions stage0/src/pkg/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,21 @@ impl TypeDeclaration {
let mut struc = false;
let mut class = false;
let mut funcs = HashSet::new();
let mut entries = 0;

loop {
// Read entry type.
let mut entry = 0;
let mut entry = 0u8;

r.read_exact(std::slice::from_mut(&mut entry))?;
if let Err(e) = r.read_exact(std::slice::from_mut(&mut entry)) {
if e.kind() == std::io::ErrorKind::UnexpectedEof {
break;
} else {
return Err(TypeDeserializeError::ReadDataFailed(e));
}
}

entries += 1;

// Process the entry.
match entry {
Expand Down Expand Up @@ -110,6 +119,10 @@ impl TypeDeclaration {
}
}

if entries == 0 {
return Err(TypeDeserializeError::EmptyData);
}

// Construct type.
let name = name.ok_or(TypeDeserializeError::TypeNameNotFound)?;
let ty = match (struc, class) {
Expand Down Expand Up @@ -704,6 +717,9 @@ pub enum Representation {
/// Represents an error when [`TypeDeclaration`] is failed to deserialize from the data.
#[derive(Debug, Error)]
pub enum TypeDeserializeError {
#[error("no data to read")]
EmptyData,

#[error("cannot read data")]
ReadDataFailed(#[source] std::io::Error),

Expand Down
2 changes: 1 addition & 1 deletion stage0/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl<'a> Project<'a> {
let version = env!("CARGO_PKG_VERSION").parse().unwrap();
let id = Dependency::new("nitro".parse().unwrap(), version);

match self.deps.resolve(&id) {
match self.deps.resolve(&id, self.targets) {
Ok(v) => deps.push(v),
Err(e) => return Err(ProjectBuildError::ResolveDependencyFailed(id, e)),
};
Expand Down

0 comments on commit 8a950de

Please sign in to comment.