-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
! lib_compiled: Reorganize Pog.Package class hierarchy in preparation…
… for remote repository
1 parent
69116c8
commit c715cf3
Showing
12 changed files
with
412 additions
and
333 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using JetBrains.Annotations; | ||
using Pog.Utils; | ||
using IOPath = System.IO.Path; | ||
using PPaths = Pog.PathConfig.PackagePaths; | ||
|
||
namespace Pog; | ||
|
||
[PublicAPI] | ||
public sealed class LocalRepository(string manifestRepositoryDirPath) : IRepository { | ||
public readonly string Path = manifestRepositoryDirPath; | ||
public bool Exists => Directory.Exists(Path); | ||
|
||
public IEnumerable<string> EnumeratePackageNames(string searchPattern = "*") { | ||
try { | ||
return FsUtils.EnumerateNonHiddenDirectoryNames(Path, searchPattern); | ||
} catch (DirectoryNotFoundException) { | ||
throw new RepositoryNotFoundException($"Package repository does not seem to exist: {Path}"); | ||
} | ||
} | ||
|
||
public IEnumerable<RepositoryVersionedPackage> Enumerate(string searchPattern = "*") { | ||
var repo = this; | ||
return EnumeratePackageNames(searchPattern).Select(p => new LocalRepositoryVersionedPackage(repo, p)); | ||
} | ||
|
||
public RepositoryVersionedPackage GetPackage(string packageName, bool resolveName, bool mustExist) { | ||
Verify.PackageName(packageName); | ||
if (resolveName) { | ||
packageName = FsUtils.GetResolvedChildName(Path, packageName); | ||
} | ||
var package = new LocalRepositoryVersionedPackage(this, packageName); | ||
if (mustExist && !package.Exists) { | ||
throw new RepositoryPackageNotFoundException( | ||
$"Package '{package.PackageName}' does not exist in the repository, expected path: {package.Path}"); | ||
} | ||
return package; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Class representing a repository directory containing different versions of a RepositoryPackage. | ||
/// </summary> | ||
/// The backing directory may not exist, in which case the enumeration methods behave as if it was empty. | ||
[PublicAPI] | ||
public sealed class LocalRepositoryVersionedPackage : RepositoryVersionedPackage { | ||
public override IRepository Repository {get;} | ||
public readonly string Path; | ||
public override bool Exists => Directory.Exists(Path); | ||
public bool IsTemplated => Directory.Exists(TemplateDirPath); | ||
|
||
public string TemplateDirPath => $"{Path}\\{PPaths.RepositoryTemplateDirName}"; | ||
internal string TemplatePath => $"{TemplateDirPath}\\{PPaths.ManifestRelPath}"; | ||
protected override string ExpectedPathStr => $"expected path: {Path}"; | ||
|
||
internal LocalRepositoryVersionedPackage(LocalRepository repository, string packageName) : base(packageName) { | ||
Repository = repository; | ||
Path = $"{repository.Path}\\{packageName}"; | ||
} | ||
|
||
public override IEnumerable<string> EnumerateVersionStrings(string searchPattern = "*") { | ||
try { | ||
if (IsTemplated) { | ||
return FsUtils.EnumerateNonHiddenFileNames(Path, searchPattern + ".psd1") | ||
.Select(IOPath.GetFileNameWithoutExtension); | ||
} else { | ||
return FsUtils.EnumerateNonHiddenDirectoryNames(Path, searchPattern); | ||
} | ||
} catch (DirectoryNotFoundException) { | ||
return Enumerable.Empty<string>(); | ||
} | ||
} | ||
|
||
protected override RepositoryPackage GetPackageUnchecked(PackageVersion version) { | ||
return this.IsTemplated | ||
? new TemplatedLocalRepositoryPackage(this, version) | ||
: new DirectLocalRepositoryPackage(this, version); | ||
} | ||
} | ||
|
||
[PublicAPI] | ||
public abstract class LocalRepositoryPackage(LocalRepositoryVersionedPackage parent, PackageVersion version, string path) | ||
: RepositoryPackage(parent, version), ILocalPackage { | ||
public string Path {get; init;} = path; | ||
public abstract string ManifestPath {get;} | ||
protected abstract string ManifestResourceDirPath {get;} | ||
protected abstract void ImportManifestTo(string targetManifestPath); | ||
|
||
public override void ImportTo(ImportedPackage target) { | ||
// remove any previous manifest | ||
target.RemoveManifest(); | ||
// ensure target directory exists | ||
Directory.CreateDirectory(target.Path); | ||
|
||
// copy the resource directory | ||
var resDir = new DirectoryInfo(ManifestResourceDirPath); | ||
if (resDir.Exists) { | ||
FsUtils.CopyDirectory(resDir, target.ManifestResourceDirPath); | ||
} | ||
|
||
// write the manifest | ||
ImportManifestTo(target.ManifestPath); | ||
|
||
Debug.Assert(MatchesImportedManifest(target)); | ||
} | ||
|
||
public override bool MatchesImportedManifest(ImportedPackage p) { | ||
// compare resource dirs | ||
if (!FsUtils.DirectoryTreeEqual(ManifestResourceDirPath, p.ManifestResourceDirPath)) { | ||
return false; | ||
} | ||
|
||
// compare manifest | ||
var importedManifest = new FileInfo(p.ManifestPath); | ||
if (!importedManifest.Exists) { | ||
return false; | ||
} else if (this is DirectLocalRepositoryPackage dp) { | ||
return importedManifest.Exists && FsUtils.FileContentEqual(new FileInfo(dp.ManifestPath), importedManifest); | ||
} else if (this is TemplatedLocalRepositoryPackage tp) { | ||
var repoManifest = Encoding.UTF8.GetBytes(tp.GetManifestString()); | ||
return FsUtils.FileContentEqual(repoManifest, importedManifest); | ||
} else { | ||
throw new UnreachableException(); | ||
} | ||
} | ||
} | ||
|
||
[PublicAPI] | ||
public sealed class DirectLocalRepositoryPackage(LocalRepositoryVersionedPackage parent, PackageVersion version) | ||
: LocalRepositoryPackage(parent, version, $"{parent.Path}\\{version}") { | ||
public override string ManifestPath => $"{Path}\\{PPaths.ManifestRelPath}"; | ||
protected override string ManifestResourceDirPath => $"{Path}\\{PPaths.ManifestResourceRelPath}"; | ||
public override bool Exists => Directory.Exists(Path); | ||
|
||
protected override void ImportManifestTo(string targetManifestPath) { | ||
File.Copy(ManifestPath, targetManifestPath); | ||
} | ||
|
||
protected override PackageManifest LoadManifest() { | ||
if (!Exists) { | ||
throw new PackageNotFoundException($"Tried to read the package manifest of a non-existent package at '{Path}'."); | ||
} | ||
return new PackageManifest(ManifestPath, owningPackage: this); | ||
} | ||
} | ||
|
||
[PublicAPI] | ||
public sealed class TemplatedLocalRepositoryPackage(LocalRepositoryVersionedPackage parent, PackageVersion version) | ||
: LocalRepositoryPackage(parent, version, $"{parent.Path}\\{version}.psd1") { | ||
public override string ManifestPath => Path; | ||
protected override string ManifestResourceDirPath => $"{TemplateDirPath}\\{PPaths.ManifestResourceRelPath}"; | ||
public override bool Exists => File.Exists(Path) && Directory.Exists(TemplateDirPath); | ||
|
||
private string TemplateDirPath => ((LocalRepositoryVersionedPackage) Container).TemplateDirPath; | ||
private string TemplatePath => $"{TemplateDirPath}\\{PPaths.ManifestRelPath}"; | ||
|
||
protected override void ImportManifestTo(string targetManifestPath) { | ||
// TODO: figure out how to avoid calling .Substitute twice when first validating, and then importing the package | ||
ManifestTemplateFile.Substitute(TemplatePath, ManifestPath, targetManifestPath); | ||
} | ||
|
||
protected override PackageManifest LoadManifest() { | ||
if (!Exists) { | ||
throw new PackageNotFoundException($"Tried to read the package manifest of a non-existent package at '{Path}'."); | ||
} | ||
return new PackageManifest(GetManifestString(), ManifestPath, owningPackage: this); | ||
} | ||
|
||
internal string GetManifestString() { | ||
return ManifestTemplateFile.Substitute(TemplatePath, ManifestPath); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,52 @@ | ||
using System.IO; | ||
using JetBrains.Annotations; | ||
using IOPath = System.IO.Path; | ||
|
||
namespace Pog; | ||
|
||
public class PackageNotFoundException(string message) : DirectoryNotFoundException(message); | ||
|
||
[PublicAPI] | ||
public abstract class Package { | ||
public readonly string PackageName; | ||
public readonly string Path; | ||
public string ManifestPath => _manifest != null ? Manifest.Path : GetManifestPath(); | ||
|
||
public virtual bool Exists => Directory.Exists(Path); | ||
|
||
// TODO: make this public? | ||
internal string ManifestResourceDirPath => IOPath.Combine(Path, PathConfig.PackagePaths.ManifestResourceRelPath); | ||
public abstract bool Exists {get;} | ||
|
||
private PackageManifest? _manifest; | ||
public PackageManifest Manifest => EnsureManifestIsLoaded(); | ||
|
||
/// Only used for asserting that the caller called `EnsureManifestIsLoaded()` | ||
internal bool ManifestLoaded => _manifest != null; | ||
|
||
protected Package(string packageName, string packagePath, PackageManifest? manifest = null) { | ||
protected Package(string packageName, PackageManifest? manifest) { | ||
Verify.Assert.PackageName(packageName); | ||
Verify.Assert.FilePath(packagePath); | ||
PackageName = packageName; | ||
Path = packagePath; | ||
_manifest = manifest; | ||
} | ||
|
||
/// <inheritdoc cref="ReloadManifest"/> | ||
protected void InvalidateManifest() { | ||
_manifest = null; | ||
} | ||
|
||
/// <inheritdoc cref="LoadManifest"/> | ||
public PackageManifest EnsureManifestIsLoaded() { | ||
return _manifest ?? ReloadManifest(); | ||
} | ||
|
||
protected void InvalidateManifest() { | ||
_manifest = null; | ||
/// <inheritdoc cref="LoadManifest"/> | ||
public PackageManifest ReloadManifest() { | ||
return _manifest = LoadManifest(); | ||
} | ||
|
||
/// <exception cref="PackageNotFoundException">The package directory does not exist.</exception> | ||
/// <exception cref="PackageManifestNotFoundException">The package manifest file does not exist.</exception> | ||
/// <exception cref="PackageManifestParseException">The package manifest file is not a valid PowerShell data file (.psd1).</exception> | ||
/// <exception cref="InvalidPackageManifestStructureException">The package manifest is a valid data file, but the structure is not valid.</exception> | ||
public PackageManifest ReloadManifest() { | ||
if (!Exists) { | ||
throw new PackageNotFoundException($"Tried to read the package manifest of a non-existent package at '{Path}'."); | ||
} | ||
return _manifest = LoadManifest(); | ||
} | ||
protected abstract PackageManifest LoadManifest(); | ||
|
||
protected virtual string GetManifestPath() { | ||
return IOPath.Combine(Path, PathConfig.PackagePaths.ManifestRelPath); | ||
} | ||
public abstract string GetDescriptionString(); | ||
} | ||
|
||
protected abstract PackageManifest LoadManifest(); | ||
public interface ILocalPackage { | ||
string Path {get;} | ||
string ManifestPath {get;} | ||
} | ||
|
||
public interface IRemotePackage { | ||
string Url {get;} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using JetBrains.Annotations; | ||
using PPaths = Pog.PathConfig.PackagePaths; | ||
|
||
namespace Pog; | ||
|
||
public class RepositoryNotFoundException(string message) : Exception(message); | ||
|
||
public class RepositoryPackageNotFoundException(string message) : PackageNotFoundException(message); | ||
|
||
public class RepositoryPackageVersionNotFoundException(string message) : PackageNotFoundException(message); | ||
|
||
// it would be nice if we could express in the type system that we have 2 separate hierarchies | ||
// (e.g. RemoteRepository always returns RemoteRepositoryVersionedPackage), but since .netstandard2.0 does not support | ||
// covariant return types, we would have to express it with generics, which would prevent us from having a list of repositories | ||
[PublicAPI] | ||
public interface IRepository { | ||
public bool Exists {get;} | ||
public IEnumerable<string> EnumeratePackageNames(string searchPattern = "*"); | ||
public IEnumerable<RepositoryVersionedPackage> Enumerate(string searchPattern = "*"); | ||
public RepositoryVersionedPackage GetPackage(string packageName, bool resolveName, bool mustExist); | ||
} | ||
|
||
/// <summary> | ||
/// Class representing a package with multiple available versions. Each version is represented by an `IRepositoryPackage`. | ||
/// </summary> | ||
/// The backing directory may not exist, in which case the enumeration methods behave as if there were no existing versions. | ||
[PublicAPI] | ||
public abstract class RepositoryVersionedPackage { | ||
public readonly string PackageName; | ||
public abstract IRepository Repository {get;} | ||
public abstract bool Exists {get;} | ||
|
||
protected RepositoryVersionedPackage(string packageName) { | ||
Verify.Assert.PackageName(packageName); | ||
PackageName = packageName; | ||
} | ||
|
||
protected abstract string ExpectedPathStr {get;} | ||
protected abstract RepositoryPackage GetPackageUnchecked(PackageVersion version); | ||
|
||
/// Enumerate UNORDERED versions of the package. | ||
public abstract IEnumerable<string> EnumerateVersionStrings(string searchPattern = "*"); | ||
|
||
/// Enumerate parsed versions of the package, in a DESCENDING order. | ||
public virtual IEnumerable<PackageVersion> EnumerateVersions(string searchPattern = "*") { | ||
return EnumerateVersionStrings(searchPattern) | ||
.Select(v => new PackageVersion(v)) | ||
.OrderByDescending(v => v); | ||
} | ||
|
||
/// Enumerate packages for versions matching the pattern, in a DESCENDING order according to the version. | ||
public IEnumerable<RepositoryPackage> Enumerate(string searchPattern = "*") { | ||
return EnumerateVersions(searchPattern).Select(GetPackageUnchecked); | ||
} | ||
|
||
public RepositoryPackage? TryGetLatestPackage() { | ||
var latestVersion = EnumerateVersionStrings().Select(v => new PackageVersion(v)).Max(); | ||
return latestVersion == null ? null : GetPackageUnchecked(latestVersion); | ||
} | ||
|
||
public RepositoryPackage GetLatestPackage() { | ||
return TryGetLatestPackage() ?? throw new RepositoryPackageVersionNotFoundException( | ||
$"Package '{PackageName}' in the repository does not have any versions, {ExpectedPathStr}"); | ||
} | ||
|
||
public RepositoryPackage GetVersionPackage(string version, bool mustExist) { | ||
return GetVersionPackage(new PackageVersion(version), mustExist); | ||
} | ||
|
||
public RepositoryPackage GetVersionPackage(PackageVersion version, bool mustExist) { | ||
if (version.ToString() == PPaths.RepositoryTemplateDirName) { | ||
// disallow creating this version, otherwise we couldn't distinguish between a templated and direct package types | ||
throw new InvalidPackageVersionException( | ||
$"Version of a package in the repository must not be '{PPaths.RepositoryTemplateDirName}'."); | ||
} | ||
|
||
var package = GetPackageUnchecked(version); | ||
if (mustExist && !package.Exists) { | ||
throw new RepositoryPackageVersionNotFoundException( | ||
$"Package '{PackageName}' in the repository does not have version '{version}', {ExpectedPathStr}"); | ||
} | ||
return package; | ||
} | ||
} | ||
|
||
[PublicAPI] | ||
public abstract class RepositoryPackage(RepositoryVersionedPackage parent, PackageVersion version) | ||
: Package(parent.PackageName, null) { | ||
public readonly RepositoryVersionedPackage Container = parent; | ||
public readonly PackageVersion Version = version; | ||
|
||
public override string GetDescriptionString() => $"package '{PackageName}', version '{Version}'"; | ||
|
||
public abstract bool MatchesImportedManifest(ImportedPackage p); | ||
public abstract void ImportTo(ImportedPackage target); | ||
} |