use serde::{Deserialize, Serialize};
use super::manifest::{DependencyKind, InstallTarget};
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RegistryResolveRequest {
pub manifest_version: u32,
pub dependencies: Vec<RegistryDependencyRequest>,
pub targets: Vec<InstallTarget>,
pub generator_contract: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct RegistryDependencyRequest {
pub kind: DependencyKind,
pub alias: String,
pub package: String,
pub requirement: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub locked_package_release_hash: Option<String>,
#[serde(skip)]
pub locked_programs: Vec<crate::project::lockfile::LockedProgram>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RegistryResolveResponse {
pub resolver_contract: String,
pub dependencies: Vec<ResolvedRegistryDependency>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(
tag = "kind",
rename_all = "lowercase",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
pub enum ResolvedRegistryDependency {
Stack {
alias: String,
package: String,
version: String,
package_release_hash: String,
generator_contract: String,
stack_manifest_hash: String,
stack_manifest: serde_json::Value,
live_specs: Vec<ResolvedLiveSpec>,
programs: Vec<crate::api_client::RegistryProgramInstallResponse>,
sdk_extensions: Vec<ResolvedSdkExtension>,
#[serde(default)]
delivery: Option<Box<ResolvedStackDelivery>>,
},
Program {
alias: String,
package: String,
version: String,
package_release_hash: String,
generator_contract: String,
install: Box<crate::api_client::RegistryProgramInstallResponse>,
sdk_extensions: Vec<ResolvedSdkExtension>,
},
}
impl ResolvedRegistryDependency {
pub fn alias(&self) -> &str {
match self {
Self::Stack { alias, .. } | Self::Program { alias, .. } => alias,
}
}
pub fn package(&self) -> &str {
match self {
Self::Stack { package, .. } | Self::Program { package, .. } => package,
}
}
pub fn package_release_hash(&self) -> &str {
match self {
Self::Stack {
package_release_hash,
..
}
| Self::Program {
package_release_hash,
..
} => package_release_hash,
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResolvedLiveSpec {
pub alias: String,
pub artifact_hash: String,
pub artifact: serde_json::Value,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(
tag = "mode",
rename_all = "kebab-case",
rename_all_fields = "camelCase",
deny_unknown_fields
)]
pub enum ResolvedStackDelivery {
Hosted {
deployment_release_hash: String,
live_bindings: Vec<ResolvedLiveBinding>,
chain_binding: Option<Box<crate::api_client::RegistryCapabilityInstallBinding>>,
transaction_binding: Option<Box<crate::api_client::RegistryCapabilityInstallBinding>>,
#[serde(default)]
served_until: Option<String>,
#[serde(default)]
replacement: Option<ServedVersionReplacement>,
#[serde(default)]
upgrade_command: Option<String>,
},
DefinitionOnly {},
Retired {
retired_at: String,
#[serde(default)]
replacement: Option<ServedVersionReplacement>,
#[serde(default)]
upgrade_command: Option<String>,
live_bindings: Vec<ResolvedLiveBinding>,
chain_binding: Option<Box<crate::api_client::RegistryCapabilityInstallBinding>>,
transaction_binding: Option<Box<crate::api_client::RegistryCapabilityInstallBinding>>,
},
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct ServedVersionReplacement {
pub stack_manifest_hash: String,
#[serde(default)]
pub version: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResolvedLiveBinding {
pub alias: String,
pub live_spec_hash: String,
pub binding: crate::api_client::RegistryLiveSpecInstallBinding,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ResolvedSdkExtension {
pub target: String,
pub content_hash: String,
pub artifact: crate::api_client::RegistrySdkExtensionArtifact,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extension_api: Option<std::num::NonZeroU32>,
}
impl ResolvedSdkExtension {
pub fn artifact_with_contract(
&self,
) -> anyhow::Result<crate::api_client::RegistrySdkExtensionArtifact> {
let mut artifact = self.artifact.clone();
match (artifact.manifest.extension_api, self.extension_api) {
(Some(manifest), Some(declared)) if manifest != declared => anyhow::bail!(
"Registry returned {} SDK extension {} with extension API {declared}, but its manifest declares {manifest}",
self.target,
self.content_hash
),
(None, declared) => artifact.manifest.extension_api = declared,
_ => {}
}
Ok(artifact)
}
}
pub(crate) fn legacy_extension_target(
artifact: &crate::api_client::RegistrySdkExtensionArtifact,
) -> Option<InstallTarget> {
match artifact.manifest.language.as_deref() {
None | Some("typescript") => Some(InstallTarget::TypeScript),
Some("rust") => Some(InstallTarget::Rust),
Some("python") => Some(InstallTarget::Python),
Some(_) => None,
}
}
pub fn program_extension_artifacts<'a>(
program: &'a crate::api_client::RegistryProgramInstallResponse,
targets: &[InstallTarget],
) -> Vec<&'a crate::api_client::RegistrySdkExtensionArtifact> {
match &program.sdk_extensions {
Some(extensions) => extensions
.iter()
.filter(|extension| {
targets
.iter()
.any(|target| target.as_str() == extension.target)
})
.map(|extension| &extension.artifact)
.collect(),
None => program.definition.extensions.iter().collect(),
}
}
pub fn program_extension_for_target(
program: &crate::api_client::RegistryProgramInstallResponse,
target: InstallTarget,
) -> anyhow::Result<Option<crate::api_client::RegistrySdkExtensionArtifact>> {
match &program.sdk_extensions {
Some(extensions) => {
let mut selected = extensions
.iter()
.filter(|extension| extension.target == target.as_str());
let first = selected.next();
if selected.next().is_some() {
anyhow::bail!(
"Registry returned more than one {target} SDK extension for program '{}'",
program.install_name
);
}
first
.map(ResolvedSdkExtension::artifact_with_contract)
.transpose()
}
None => Ok(program
.definition
.extensions
.as_ref()
.filter(|artifact| legacy_extension_target(artifact) == Some(target))
.cloned()),
}
}