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
90 changes: 90 additions & 0 deletions pulp_rust/app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Generated by Django 4.2.26 on 2025-12-02 04:28

from django.db import migrations, models
import django.db.models.deletion
import pulpcore.app.util


class Migration(migrations.Migration):

initial = True

dependencies = [
('core', '0106_alter_artifactdistribution_distribution_ptr_and_more'),
]

operations = [
migrations.CreateModel(
name='RustContent',
fields=[
('content_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.content')),
('name', models.CharField(db_index=True, max_length=255)),
('vers', models.CharField(max_length=64)),
('cksum', models.CharField(max_length=64)),
('yanked', models.BooleanField(default=False)),
('features', models.JSONField(blank=True, default=dict)),
('features2', models.JSONField(blank=True, default=dict, null=True)),
('links', models.CharField(blank=True, max_length=255, null=True)),
('rust_version', models.CharField(blank=True, max_length=32, null=True)),
('v', models.IntegerField(default=1)),
('_pulp_domain', models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain')),
],
options={
'default_related_name': '%(app_label)s_%(model_name)s',
'unique_together': {('name', 'vers', '_pulp_domain')},
},
bases=('core.content',),
),
migrations.CreateModel(
name='RustDistribution',
fields=[
('distribution_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.distribution')),
('allow_uploads', models.BooleanField(default=True)),
],
options={
'default_related_name': '%(app_label)s_%(model_name)s',
},
bases=('core.distribution',),
),
migrations.CreateModel(
name='RustRemote',
fields=[
('remote_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.remote')),
],
options={
'default_related_name': '%(app_label)s_%(model_name)s',
},
bases=('core.remote',),
),
migrations.CreateModel(
name='RustRepository',
fields=[
('repository_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.repository')),
],
options={
'default_related_name': '%(app_label)s_%(model_name)s',
},
bases=('core.repository',),
),
migrations.CreateModel(
name='RustDependency',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('req', models.CharField(max_length=255)),
('features', models.JSONField(blank=True, default=list)),
('optional', models.BooleanField(default=False)),
('default_features', models.BooleanField(default=True)),
('target', models.CharField(blank=True, max_length=255, null=True)),
('kind', models.CharField(choices=[('normal', 'Normal'), ('dev', 'Development'), ('build', 'Build')], default='normal', max_length=16)),
('registry', models.CharField(blank=True, max_length=512, null=True)),
('package', models.CharField(blank=True, max_length=255, null=True)),
('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dependencies', to='rust.rustcontent')),
],
options={
'verbose_name_plural': 'rust dependencies',
'default_related_name': '%(app_label)s_%(model_name)s',
'indexes': [models.Index(fields=['content', 'kind'], name='rust_rustde_content_a46e30_idx'), models.Index(fields=['name'], name='rust_rustde_name_6a2db4_idx')],
},
),
]
150 changes: 122 additions & 28 deletions pulp_rust/app/models.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
"""
Check `Plugin Writer's Guide`_ for more details.

.. _Plugin Writer's Guide:
https://pulpproject.org/pulpcore/docs/dev/
"""

from logging import getLogger

from django.db import models

from pulpcore.plugin.models import (
Content,
ContentArtifact,
Remote,
Repository,
Publication,
Distribution,
)
from pulpcore.plugin.util import get_domain_pk
Expand All @@ -24,43 +15,144 @@

class RustContent(Content):
"""
The "rust" content type.
The "rust" content type representing a Cargo package version.

This model represents a single version of a Rust crate as defined in the
Cargo registry index specification. Each instance corresponds to one line
in a package's index file.

Fields:
name: The package name (crate name)
vers: The semantic version string (SemVer 2.0.0)
cksum: SHA256 checksum of the .crate file (tarball)
yanked: Whether this version has been yanked (removed from normal use)
features: JSON object mapping feature names to their dependencies
features2: JSON object with extended feature syntax support
links: Value from Cargo.toml manifest 'links' field (for native library linking)
rust_version: Minimum Rust version required to compile this package
v: Schema version of the index format (integer)
"""

Define fields you need for your new content type and
specify uniqueness constraint to identify unit of this type.
TYPE = "rust"
repo_key_fields = ("name", "vers")

For example::
# Package name - alphanumeric characters, hyphens, and underscores allowed
name = models.CharField(max_length=255, blank=False, null=False, db_index=True)

field1 = models.TextField()
field2 = models.IntegerField()
field3 = models.CharField()
# Semantic version string following SemVer 2.0.0 specification
vers = models.CharField(max_length=64, blank=False, null=False)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
unique_together = ("field1", "field2")
"""
# SHA256 checksum (hex-encoded) of the .crate tarball file for verification
cksum = models.CharField(max_length=64, blank=False, null=False)

TYPE = "rust"
# Indicates if this version has been yanked (deprecated/removed from use)
# Yanked versions can still be used by existing Cargo.lock files but won't be selected
# for new builds
yanked = models.BooleanField(default=False)

# Feature flags and compatibility
# Maps feature names to lists of features/dependencies they enable
# Example: {"default": ["std"], "std": [], "serde": ["dep:serde"]}
features = models.JSONField(default=dict, blank=True)

# Extended feature syntax introduced in newer registry versions
# Supports more complex feature dependency expressions
features2 = models.JSONField(default=dict, blank=True, null=True)

# Name of native library this package links to (from Cargo.toml 'links' field)
# Used to prevent multiple packages from linking the same native library
links = models.CharField(max_length=255, blank=True, null=True)

# Minimum Rust compiler version required (MSRV - Minimum Supported Rust Version)
# Example: "1.56.0"
rust_version = models.CharField(max_length=32, blank=True, null=True)

# Schema version of the index entry format
# Allows for future format evolution while maintaining backward compatibility
v = models.IntegerField(default=1)

name = models.CharField(blank=False, null=False)
_pulp_domain = models.ForeignKey("core.Domain", default=get_domain_pk, on_delete=models.PROTECT)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
unique_together = ("name", "_pulp_domain")
unique_together = (("name", "vers", "_pulp_domain"),)


class RustPublication(Publication):
class RustDependency(models.Model):
"""
A Publication for RustContent.

Define any additional fields for your new publication if needed.
Represents a dependency of a Cargo package version.

Each RustContent (package version) can have multiple dependencies.
Dependencies are stored as separate records to enable efficient querying
and relationship tracking.

Fields:
content: The package version that has this dependency
name: The dependency name as used in code (may be renamed via 'package')
req: Version requirement string (e.g., "^1.0", ">=0.2.3,<0.3")
features: List of feature flags to enable for this dependency
optional: Whether this is an optional dependency
default_features: Whether to enable the dependency's default features
target: Platform-specific conditional compilation target (e.g., "cfg(unix)")
kind: Dependency type - "normal", "dev", or "build"
registry: Alternative registry URL if dependency is from a different registry
package: Original package name if dependency was renamed in Cargo.toml
"""

TYPE = "rust"
# The package version that declares this dependency
content = models.ForeignKey(RustContent, on_delete=models.CASCADE, related_name="dependencies")

# Name of the dependency as used in the code (may differ from package name if renamed)
name = models.CharField(max_length=255, blank=False, null=False)

# Version requirement string using Cargo's version requirement syntax
# Examples: "1.0", "^1.2.3", ">=1.0.0,<2.0.0", "*"
req = models.CharField(max_length=255, blank=False, null=False)

# List of feature flags to enable for this dependency
# Example: ["serde", "std"]
features = models.JSONField(default=list, blank=True)

# If true, this dependency is only included when explicitly requested via features
# Optional dependencies can be enabled as features themselves
optional = models.BooleanField(default=False)

# Whether to enable the dependency's default feature set
# Setting to false allows for minimal builds
default_features = models.BooleanField(default=True)

# Platform-specific target configuration (cfg expression)
# Example: "cfg(windows)", "cfg(target_arch = \"x86_64\")"
# If set, dependency only applies when the target matches
target = models.CharField(max_length=255, blank=True, null=True)

# Type of dependency - determines when it's required during the build process
kind = models.CharField(
max_length=16,
choices=[
("normal", "Normal"), # Regular runtime dependency
("dev", "Development"), # Development/test-only dependency
("build", "Build"), # Build script dependency
],
default="normal",
)

# @TODO: I suspect this isn't needed
# URL of alternative registry if dependency comes from a non-default registry
# Null means the dependency is from the same registry as the parent package
registry = models.CharField(max_length=512, blank=True, null=True)

# Original crate name if the dependency was renamed
# Example: if 'use foo' but package is 'bar', name='foo', package='bar'
package = models.CharField(max_length=255, blank=True, null=True)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
verbose_name_plural = "rust dependencies"
indexes = [
models.Index(fields=["content", "kind"]),
models.Index(fields=["name"]),
]


class RustRemote(Remote):
Expand Down Expand Up @@ -100,5 +192,7 @@ class RustDistribution(Distribution):

TYPE = "rust"

allow_uploads = models.BooleanField(default=True)

class Meta:
default_related_name = "%(app_label)s_%(model_name)s"
Loading