Skip to content

Commit b45e6c1

Browse files
committed
Add initial data models, serializers, viewsets
Assisted by: claude
1 parent 164b7e6 commit b45e6c1

8 files changed

Lines changed: 449 additions & 249 deletions

File tree

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Generated by Django 4.2.26 on 2025-12-02 04:28
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import pulpcore.app.util
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
initial = True
11+
12+
dependencies = [
13+
('core', '0145_domainize_import_export'),
14+
]
15+
16+
operations = [
17+
migrations.CreateModel(
18+
name='RustContent',
19+
fields=[
20+
('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')),
21+
('name', models.CharField(db_index=True, max_length=255)),
22+
('vers', models.CharField(max_length=64)),
23+
('cksum', models.CharField(max_length=64)),
24+
('yanked', models.BooleanField(default=False)),
25+
('features', models.JSONField(blank=True, default=dict)),
26+
('features2', models.JSONField(blank=True, default=dict, null=True)),
27+
('links', models.CharField(blank=True, max_length=255, null=True)),
28+
('rust_version', models.CharField(blank=True, max_length=32, null=True)),
29+
('v', models.IntegerField(default=1)),
30+
('_pulp_domain', models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.PROTECT, to='core.domain')),
31+
],
32+
options={
33+
'default_related_name': '%(app_label)s_%(model_name)s',
34+
'unique_together': {('name', 'vers', '_pulp_domain')},
35+
},
36+
bases=('core.content',),
37+
),
38+
migrations.CreateModel(
39+
name='RustDistribution',
40+
fields=[
41+
('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')),
42+
('allow_uploads', models.BooleanField(default=True)),
43+
],
44+
options={
45+
'default_related_name': '%(app_label)s_%(model_name)s',
46+
},
47+
bases=('core.distribution',),
48+
),
49+
migrations.CreateModel(
50+
name='RustRemote',
51+
fields=[
52+
('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')),
53+
],
54+
options={
55+
'default_related_name': '%(app_label)s_%(model_name)s',
56+
},
57+
bases=('core.remote',),
58+
),
59+
migrations.CreateModel(
60+
name='RustRepository',
61+
fields=[
62+
('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')),
63+
],
64+
options={
65+
'default_related_name': '%(app_label)s_%(model_name)s',
66+
},
67+
bases=('core.repository',),
68+
),
69+
migrations.CreateModel(
70+
name='RustDependency',
71+
fields=[
72+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
73+
('name', models.CharField(max_length=255)),
74+
('req', models.CharField(max_length=255)),
75+
('features', models.JSONField(blank=True, default=list)),
76+
('optional', models.BooleanField(default=False)),
77+
('default_features', models.BooleanField(default=True)),
78+
('target', models.CharField(blank=True, max_length=255, null=True)),
79+
('kind', models.CharField(choices=[('normal', 'Normal'), ('dev', 'Development'), ('build', 'Build')], default='normal', max_length=16)),
80+
('registry', models.CharField(blank=True, max_length=512, null=True)),
81+
('package', models.CharField(blank=True, max_length=255, null=True)),
82+
('content', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dependencies', to='rust.rustcontent')),
83+
],
84+
options={
85+
'verbose_name_plural': 'rust dependencies',
86+
'default_related_name': '%(app_label)s_%(model_name)s',
87+
'indexes': [models.Index(fields=['content', 'kind'], name='rust_rustde_content_a46e30_idx'), models.Index(fields=['name'], name='rust_rustde_name_6a2db4_idx')],
88+
},
89+
),
90+
]

pulp_rust/app/models.py

Lines changed: 122 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1-
"""
2-
Check `Plugin Writer's Guide`_ for more details.
3-
4-
.. _Plugin Writer's Guide:
5-
https://pulpproject.org/pulpcore/docs/dev/
6-
"""
7-
81
from logging import getLogger
92

103
from django.db import models
114

125
from pulpcore.plugin.models import (
136
Content,
14-
ContentArtifact,
157
Remote,
168
Repository,
17-
Publication,
189
Distribution,
1910
)
2011
from pulpcore.plugin.util import get_domain_pk
@@ -24,43 +15,144 @@
2415

2516
class RustContent(Content):
2617
"""
27-
The "rust" content type.
18+
The "rust" content type representing a Cargo package version.
19+
20+
This model represents a single version of a Rust crate as defined in the
21+
Cargo registry index specification. Each instance corresponds to one line
22+
in a package's index file.
23+
24+
Fields:
25+
name: The package name (crate name)
26+
vers: The semantic version string (SemVer 2.0.0)
27+
cksum: SHA256 checksum of the .crate file (tarball)
28+
yanked: Whether this version has been yanked (removed from normal use)
29+
features: JSON object mapping feature names to their dependencies
30+
features2: JSON object with extended feature syntax support
31+
links: Value from Cargo.toml manifest 'links' field (for native library linking)
32+
rust_version: Minimum Rust version required to compile this package
33+
v: Schema version of the index format (integer)
34+
"""
2835

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

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

34-
field1 = models.TextField()
35-
field2 = models.IntegerField()
36-
field3 = models.CharField()
42+
# Semantic version string following SemVer 2.0.0 specification
43+
vers = models.CharField(max_length=64, blank=False, null=False)
3744

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

43-
TYPE = "rust"
48+
# Indicates if this version has been yanked (deprecated/removed from use)
49+
# Yanked versions can still be used by existing Cargo.lock files but won't be selected
50+
# for new builds
51+
yanked = models.BooleanField(default=False)
52+
53+
# Feature flags and compatibility
54+
# Maps feature names to lists of features/dependencies they enable
55+
# Example: {"default": ["std"], "std": [], "serde": ["dep:serde"]}
56+
features = models.JSONField(default=dict, blank=True)
57+
58+
# Extended feature syntax introduced in newer registry versions
59+
# Supports more complex feature dependency expressions
60+
features2 = models.JSONField(default=dict, blank=True, null=True)
61+
62+
# Name of native library this package links to (from Cargo.toml 'links' field)
63+
# Used to prevent multiple packages from linking the same native library
64+
links = models.CharField(max_length=255, blank=True, null=True)
65+
66+
# Minimum Rust compiler version required (MSRV - Minimum Supported Rust Version)
67+
# Example: "1.56.0"
68+
rust_version = models.CharField(max_length=32, blank=True, null=True)
69+
70+
# Schema version of the index entry format
71+
# Allows for future format evolution while maintaining backward compatibility
72+
v = models.IntegerField(default=1)
4473

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

4876
class Meta:
4977
default_related_name = "%(app_label)s_%(model_name)s"
50-
unique_together = ("name", "_pulp_domain")
78+
unique_together = (("name", "vers", "_pulp_domain"),)
5179

5280

53-
class RustPublication(Publication):
81+
class RustDependency(models.Model):
5482
"""
55-
A Publication for RustContent.
56-
57-
Define any additional fields for your new publication if needed.
83+
Represents a dependency of a Cargo package version.
84+
85+
Each RustContent (package version) can have multiple dependencies.
86+
Dependencies are stored as separate records to enable efficient querying
87+
and relationship tracking.
88+
89+
Fields:
90+
content: The package version that has this dependency
91+
name: The dependency name as used in code (may be renamed via 'package')
92+
req: Version requirement string (e.g., "^1.0", ">=0.2.3,<0.3")
93+
features: List of feature flags to enable for this dependency
94+
optional: Whether this is an optional dependency
95+
default_features: Whether to enable the dependency's default features
96+
target: Platform-specific conditional compilation target (e.g., "cfg(unix)")
97+
kind: Dependency type - "normal", "dev", or "build"
98+
registry: Alternative registry URL if dependency is from a different registry
99+
package: Original package name if dependency was renamed in Cargo.toml
58100
"""
59101

60-
TYPE = "rust"
102+
# The package version that declares this dependency
103+
content = models.ForeignKey(RustContent, on_delete=models.CASCADE, related_name="dependencies")
104+
105+
# Name of the dependency as used in the code (may differ from package name if renamed)
106+
name = models.CharField(max_length=255, blank=False, null=False)
107+
108+
# Version requirement string using Cargo's version requirement syntax
109+
# Examples: "1.0", "^1.2.3", ">=1.0.0,<2.0.0", "*"
110+
req = models.CharField(max_length=255, blank=False, null=False)
111+
112+
# List of feature flags to enable for this dependency
113+
# Example: ["serde", "std"]
114+
features = models.JSONField(default=list, blank=True)
115+
116+
# If true, this dependency is only included when explicitly requested via features
117+
# Optional dependencies can be enabled as features themselves
118+
optional = models.BooleanField(default=False)
119+
120+
# Whether to enable the dependency's default feature set
121+
# Setting to false allows for minimal builds
122+
default_features = models.BooleanField(default=True)
123+
124+
# Platform-specific target configuration (cfg expression)
125+
# Example: "cfg(windows)", "cfg(target_arch = \"x86_64\")"
126+
# If set, dependency only applies when the target matches
127+
target = models.CharField(max_length=255, blank=True, null=True)
128+
129+
# Type of dependency - determines when it's required during the build process
130+
kind = models.CharField(
131+
max_length=16,
132+
choices=[
133+
("normal", "Normal"), # Regular runtime dependency
134+
("dev", "Development"), # Development/test-only dependency
135+
("build", "Build"), # Build script dependency
136+
],
137+
default="normal",
138+
)
139+
140+
# @TODO: I suspect this isn't needed
141+
# URL of alternative registry if dependency comes from a non-default registry
142+
# Null means the dependency is from the same registry as the parent package
143+
registry = models.CharField(max_length=512, blank=True, null=True)
144+
145+
# Original crate name if the dependency was renamed
146+
# Example: if 'use foo' but package is 'bar', name='foo', package='bar'
147+
package = models.CharField(max_length=255, blank=True, null=True)
61148

62149
class Meta:
63150
default_related_name = "%(app_label)s_%(model_name)s"
151+
verbose_name_plural = "rust dependencies"
152+
indexes = [
153+
models.Index(fields=["content", "kind"]),
154+
models.Index(fields=["name"]),
155+
]
64156

65157

66158
class RustRemote(Remote):
@@ -100,5 +192,7 @@ class RustDistribution(Distribution):
100192

101193
TYPE = "rust"
102194

195+
allow_uploads = models.BooleanField(default=True)
196+
103197
class Meta:
104198
default_related_name = "%(app_label)s_%(model_name)s"

0 commit comments

Comments
 (0)