Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/main/java/land/oras/Index.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiPredicate;
import land.oras.utils.Const;
import land.oras.utils.JsonUtils;
import org.jspecify.annotations.NonNull;
Expand Down Expand Up @@ -149,8 +150,18 @@ public List<ManifestDescriptor> getManifests() {
* @return The list of manifests that match the platform
*/
public List<ManifestDescriptor> filter(Platform platform) {
return filter(platform, Platform::matches);
}

/**
* Filter the manifests by platform with a custom comparator
* @param platform The platform
* @param comparator The comparator to compare the platform of the manifest descriptor and the given platform. The first parameter is the platform of the manifest descriptor, and the second parameter is the given platform. The comparator should return true if the manifest descriptor matches the given platform, and false otherwise.
* @return The list of manifests that match the platform
*/
public List<ManifestDescriptor> filter(Platform platform, BiPredicate<Platform, Platform> comparator) {
return getManifests().stream()
.filter(descriptor -> descriptor.getPlatform().equals(platform))
.filter(descriptor -> comparator.test(descriptor.getPlatform(), platform))
.toList();
}

Expand All @@ -170,10 +181,7 @@ public List<ManifestDescriptor> unspecifiedPlatforms() {
* @return The manifest that matches the platform, or null if there are multiple matches or no matches
*/
public @Nullable ManifestDescriptor findUnique(Platform platform) {
return getManifests().stream()
.filter(descriptor -> descriptor.getPlatform().equals(platform))
.findFirst()
.orElse(null);
return filter(platform).stream().findFirst().orElse(null);
}

@Override
Expand Down
24 changes: 13 additions & 11 deletions src/main/java/land/oras/ManifestDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public final class ManifestDescriptor {
private final long size;

@Nullable
private final Map<String, String> platform;
private final Platform platform;

@Nullable
private final Map<String, String> annotations;
Expand All @@ -64,7 +64,7 @@ private ManifestDescriptor(
@JsonProperty(Const.JSON_PROPERTY_MEDIA_TYPE) String mediaType,
@JsonProperty(Const.JSON_PROPERTY_DIGEST) String digest,
@JsonProperty(Const.JSON_PROPERTY_SIZE) long size,
@JsonProperty(Const.JSON_PROPERTY_PLATFORM) @Nullable Map<String, String> platform,
@JsonProperty(Const.JSON_PROPERTY_PLATFORM) @Nullable Platform platform,
@JsonProperty(Const.JSON_PROPERTY_ANNOTATIONS) @Nullable Map<String, String> annotations) {
this.artifactType = artifactType;
this.mediaType = mediaType;
Expand Down Expand Up @@ -107,20 +107,22 @@ public long getSize() {
}

/**
* Get the platform as annotations
* Return the platform as a Platform object
* @return The platform
*/
public @Nullable @JsonProperty(Const.JSON_PROPERTY_PLATFORM) Map<String, String> getPlatformAnnotations() {
return platform;
@JsonIgnore
public Platform getPlatform() {
return platform != null ? platform : Platform.empty();
}

/**
* Return the platform as a Platform object
* @return The platform
* Return the platform or null if the platform is not set
* Only use for serialization purposes, as the platform field is nullable in the JSON representation of the manifest descriptor
* @return The platform or null
*/
@JsonIgnore
public Platform getPlatform() {
return Platform.of(platform);
@JsonProperty(Const.JSON_PROPERTY_PLATFORM)
public @Nullable Platform getPlatformOrNull() {
return platform;
}

/**
Expand Down Expand Up @@ -188,7 +190,7 @@ public ManifestDescriptor withArtifactType(@Nullable String artifactType) {
* @return The subject
*/
public ManifestDescriptor withPlatform(Platform platform) {
return new ManifestDescriptor(artifactType, mediaType, digest, size, platform.annotations(), annotations);
return new ManifestDescriptor(artifactType, mediaType, digest, size, platform, annotations);
}

/**
Expand Down
197 changes: 154 additions & 43 deletions src/main/java/land/oras/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,54 +20,118 @@

package land.oras;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.Map;
import java.util.List;
import java.util.Objects;
import land.oras.utils.Const;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Record for platform information
* @param annotations Platform annotations, which can include os and architecture information
* @param os The operating system of the platform
* @param architecture The architecture of the platform
* @param variant The variant of the platform, which is optional and may be null
* @param osVersion The operating system version of the platform, which is optional and may be
* @param features The features of the platform, which is optional and may be null
* @param osFeatures The operating system features of the platform, which is optional and may be
*/
@NullMarked
@OrasModel
@JsonPropertyOrder({Const.PLATFORM_OS, Const.PLATFORM_ARCHITECTURE})
public record Platform(@Nullable @JsonIgnore Map<String, String> annotations) {
@JsonPropertyOrder({
Const.PLATFORM_OS,
Const.PLATFORM_ARCHITECTURE,
Const.PLATFORM_VARIANT,
Const.PLATFORM_OS_VERSION,
Const.PLATFORM_OS_FEATURES
})
public record Platform(
@Nullable @JsonProperty(Const.PLATFORM_OS) String os,
@Nullable @JsonProperty(Const.PLATFORM_ARCHITECTURE) String architecture,
@Nullable @JsonProperty(Const.PLATFORM_OS_VERSION) String osVersion,
@Nullable @JsonProperty(Const.PLATFORM_VARIANT) String variant,
@Nullable @JsonProperty(Const.PLATFORM_FEATURES) List<String> features,
@Nullable @JsonProperty(Const.PLATFORM_OS_FEATURES) List<String> osFeatures) {

/**
* Constructor for JSON deserialization
* @param annotations Platform annotations, which can include os and architecture information
* Create a new platform linux/amd64
* @return The platform
*/
@JsonCreator
public Platform {}
public static Platform linuxAmd64() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_AMD64);
}

/**
* Create a new platform with the given annotations
* @param annotations Platform annotations, which can include os and architecture information
* Create a new platform windows/amd64
* @return The platform
*/
public static Platform of(@Nullable Map<String, String> annotations) {
return new Platform(annotations);
public static Platform windowsAmd64() {
return of(Const.PLATFORM_WINDOWS, Const.PLATFORM_ARCHITECTURE_AMD64);
}

/**
* Create a new platform linux/amd64
* @return The platform
*/
public static Platform linuxAmd64() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_AMD64);
public static Platform linux386() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_386);
}

/**
* Create a new platform linux arm/v6
* @return The platform
*/
public static Platform linuxArmV6() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM, Const.VARIANT_V6);
}

/**
* Create a new platform linux arm/v7
* @return The platform
*/
public static Platform linuxArmV7() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM, Const.VARIANT_V7);
}

/**
* Create a new platform linux arm64/v8
* @return The platform
*/
public static Platform linuxArm64V8() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_ARM64, Const.VARIANT_V8);
}

/**
* Create a new platform ppc64le
* @return The platform
*/
public static Platform linuxPpc64le() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_PPC64LE, null);
}

/**
* Create a new platform riscv64
* @return The platform
*/
public static Platform linuxRiscv64() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_RISCV64, null);
}

/**
* Create a new platform s390x
* @return The platform
*/
public static Platform linuxS390x() {
return of(Const.PLATFORM_LINUX, Const.PLATFORM_ARCHITECTURE_S390X, null);
}

/**
* Create a new platform with empty os and architecture
* @return The platform
*/
public static Platform empty() {
return new Platform(null);
return new Platform(null, null, null, null, null, null);
}

/**
Expand All @@ -85,7 +149,7 @@ public static Platform unknown() {
* @return The platform
*/
public static Platform of(String os, String architecture) {
return new Platform(Map.of(Const.PLATFORM_OS, os, Const.PLATFORM_ARCHITECTURE, architecture));
return new Platform(os, architecture, null, null, null, null);
}

/**
Expand All @@ -96,44 +160,61 @@ public static Platform of(String os, String architecture) {
* @return The platform
*/
public static Platform of(String os, String architecture, @Nullable String variant) {
if (variant == null) {
return of(os, architecture);
}
return new Platform(Map.of(
Const.PLATFORM_OS, os,
Const.PLATFORM_ARCHITECTURE, architecture,
Const.PLATFORM_VARIANT, variant));
return new Platform(os, architecture, null, variant, null, null);
}

/**
* Return the architecture of the platform, or "unknown" if not specified
* @return The architecture of the platform
* Return the os of the platform, or "unknown" if the os is null
* @return The os of the platform
*/
@JsonGetter
@Override
public String os() {
return annotations != null
? annotations.getOrDefault(Const.PLATFORM_OS, Const.PLATFORM_UNKNOWN)
: Const.PLATFORM_UNKNOWN;
return os != null ? os : Const.PLATFORM_UNKNOWN;
}

/**
* Return the architecture of the platform, or "unknown" if not specified
* @return The architecture of the platform
* Return the os of the platform, or "unknown" if the os is null
* @return The os of the platform
*/
@JsonGetter
@Override
public String architecture() {
return annotations != null
? annotations.getOrDefault(Const.PLATFORM_ARCHITECTURE, Const.PLATFORM_UNKNOWN)
: Const.PLATFORM_UNKNOWN;
return architecture != null ? architecture : Const.PLATFORM_UNKNOWN;
}

/**
* Create a new platform with the given features
* @param features The features of the platform
* @return The platform
*/
public Platform withFeatures(List<String> features) {
return new Platform(os, architecture, osVersion, variant, features, osFeatures);
}

/**
* Create a new platform with the given variant
* @param variant The variant of the platform
* @return The platform
*/
public Platform withVariant(String variant) {
return new Platform(os, architecture, osVersion, variant, features, osFeatures);
}

/**
* Create a new platform with the given os version
* @param osVersion The os version of the platform
* @return The platform
*/
public Platform withOsVersion(String osVersion) {
return new Platform(os, architecture, osVersion, variant, features, osFeatures);
}

/**
* Return the variant of the platform, or null if not specified
* @return The variant of the platform
* Create a new platform with the given os features
* @param osFeatures The os features of the platform
* @return The platform
*/
@JsonGetter
public @Nullable String variant() {
return annotations != null ? annotations.get(Const.PLATFORM_VARIANT) : null;
public Platform withOsFeatures(List<String> osFeatures) {
return new Platform(os, architecture, osVersion, variant, features, osFeatures);
}

/**
Expand All @@ -144,4 +225,34 @@ public String architecture() {
public static boolean unspecified(Platform platform) {
return platform.equals(Platform.empty()) || platform.equals(Platform.unknown());
}

/**
* Check if 2 platform are matching, which means the os and architecture are the same (including variant)
* @param platform The platform to check
* @param target The target platform to match
* @return True if the platform is matching, false otherwise
*/
public static boolean matches(Platform platform, Platform target) {
return matches(platform, target, false);
}

/**
* Check if 2 platform are matching, which means the os and architecture are the same (including variant)
* @param platform The platform to check
* @param target The target platform to match
* @param includeVersion Whether to include os version
* @return True if the platform is matching, false otherwise
*/
public static boolean matches(Platform platform, Platform target, boolean includeVersion) {
if (!platform.os().equals(target.os()) || !platform.architecture().equals(target.architecture())) {
return false;
}
if (!Objects.equals(platform.variant(), target.variant())) {
return false;
}
if (includeVersion) {
return Objects.equals(platform.osVersion(), target.osVersion());
}
return true;
}
}
Loading