Repository navigation
Describe each format in Rust and remove plugin.toml #444
Description
Part of #442.
Where things stand (after #437)
Each format crate has a plugin.toml next to its Cargo.toml:
[plugin]
id = "nuget"
name = "NuGet"
version = "1.0.0"
api = "0.3.0"
description = "NuGet V3 feeds: …"
mount = "/v3"
kinds = ["hosted", "proxy", "group"]
[assets]
package = "package.nupkg"
package_type = "application/octet-stream"
manifest = "package.nuspec"
manifest_type = "application/xml"The crate hands this file to the server as text: pub const PLUGIN_TOML: &str = forge_plugin_api::plugin_toml!();, which is an include_str!. The server parses it at startup:
Plugin::builtin(PLUGIN_TOML, Nuget::with_limits(…)).expect("plugins/nuget/plugin.toml is valid");Manifest::parsewith thetomlcrate;Box::leakon the id to get a&'static str.
The file was the manifest an operator installed next to a WebAssembly component. Now that only built-in formats remain, it holds values the compiler could check, written in a second language:
- a typo in a key or a kind fails at startup instead of at build time;
versionrepeats the crate'sversion, and a unit test in each crate keeps the two equal;apican only beforge_plugin_api::API, because the contract and the formats build together and the compiler already checks the trait;- every format crate repeats the same
plugin_toml_names_this_cratetest.
Proposal
Each format describes itself in Rust, as a 'static value that the trait returns:
pub const DESCRIPTOR: Descriptor = Descriptor {
id: "nuget",
name: "NuGet",
version: env!("CARGO_PKG_VERSION"),
description: "NuGet V3 feeds: …",
kinds: &[RepositoryKind::Hosted, RepositoryKind::Proxy, RepositoryKind::Group],
assets: Assets {
package: "package.nupkg",
package_type: "application/octet-stream",
manifest: "package.nuspec",
manifest_type: "application/xml",
},
};
impl FormatPlugin for Nuget {
fn descriptor(&self) -> &'static Descriptor {
&DESCRIPTOR
}
// …
}Descriptor(the name is open) goes inforge-plugin-api, next toRepositoryKindandAssets. The fields ofAssetsbecome&'static str, and anAssets::DEFAULTreplaces the serde default. The descriptor comes from a trait method rather than an associated const because the server holds formats asArc<dyn FormatPlugin>.Plugin::builtin(implementation)takes only the format.Plugin::manifest()becomesdescriptor(), andPlugin::id()returnsdescriptor().idwith no leak.- The startup parser validated the id's spelling, the asset names and the reserved ids (
RESERVED_IDS:dockerand the server's routes). Those checks move into a test overplugins::builtin()inforge-server, together with a check that no two formats share an id. A bad descriptor then failscargo testinstead of startup. storagefrom Raw repositories as a format plugin: a file store in the plugin contract #431 becomes a descriptor field like the others, and so doesmountuntil Serve every repository at /repository/{name}/, with no format in the URL (breaking) #438 removes it. If Serve every repository at /repository/{name}/, with no format in the URL (breaking) #438 lands after this, it removes themountfield instead of the manifest key, and its criterion that aplugin.tomlwithmountis refused no longer applies.- Remove:
plugin.tomlfrom every format crate;plugin_toml!andPLUGIN_TOML;Manifest::parseandManifest::load,PluginError::ManifestandSUPPORTED_APIS;forge_plugin_api::APIand theapikey;- the
plugin_toml_names_this_cratetests; - the "keep it equal to
versionin plugin.toml" comments in the crates'Cargo.toml; forge-plugin'stomldependency.
- Update the docs: the crates' README and Writing a plugin in
docs/format-plugins.mdshow the descriptor instead ofplugin.toml.
Alternatives considered
- Keep
plugin.toml. Only the crate it sits in reads it. Keeping it keeps the startup parse and the duplicated version. - Use
[package.metadata.forge]inCargo.toml. This saves one file, but the values are still TOML that something parses at runtime or in a build script, with no type checking.
No changelog fragment is needed, since nothing changes for anyone running Forge. GET /admin/formats answers as before.
Acceptance criteria
- There is no
plugin.tomlin the repository, andgit grep -n 'plugin\.toml\|plugin_toml\|PLUGIN_TOML'finds nothing outsideCHANGELOG.mdanddocs/changes/. -
cargo tree -p forge-pluginshows notoml. -
GET /admin/formatsreturns the same ids, names, versions, descriptions and kinds as before, and the existing admin tests pass unchanged. - A test over the built-in formats refuses a malformed or reserved id, two formats with the same id, and invalid asset names.
- The docs show a format's descriptor, not
plugin.toml.
Blocked by #437: a WebAssembly component needs its manifest on disk, so the file has to stay for as long as components can load. This can land before or after #443, but not at the same time, because both change the same files.
Activity