use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Formatter;
use std::iter::Sum;
use std::ops::Add;
use std::path::Path;
use std::str::FromStr;
use std::{fmt, fs};
use anyhow::{Context, Result};
use cargo_lock::package::GitReference;
use cargo_metadata::Package;
use semver::VersionReq;
use serde::de::value::SeqAccessDeserializer;
use serde::de::{Deserializer, SeqAccess, Unexpected, Visitor};
use serde::{Deserialize, Serialize, Serializer};
use crate::select::{Select, Selectable};
use crate::utils::starlark::Label;
use crate::utils::target_triple::TargetTriple;
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub(crate) enum VendorMode {
Local,
Remote,
}
impl std::fmt::Display for VendorMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(
match self {
VendorMode::Local => "local",
VendorMode::Remote => "remote",
},
f,
)
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct RenderConfig {
pub(crate) repository_name: String,
#[serde(default = "default_build_file_template")]
pub(crate) build_file_template: String,
#[serde(default = "default_crate_label_template")]
pub(crate) crate_label_template: String,
#[serde(default = "default_crate_alias_template")]
pub(crate) crate_alias_template: String,
#[serde(default = "default_crates_module_template")]
pub(crate) crates_module_template: String,
#[serde(default = "default_crate_repository_template")]
pub(crate) crate_repository_template: String,
#[serde(default)]
pub(crate) default_alias_rule: AliasRule,
pub(crate) default_package_name: Option<String>,
#[serde(default = "default_generate_target_compatible_with")]
pub(crate) generate_target_compatible_with: bool,
#[serde(default = "default_platforms_template")]
pub(crate) platforms_template: String,
pub(crate) regen_command: String,
pub(crate) vendor_mode: Option<VendorMode>,
#[serde(default = "default_generate_rules_license_metadata")]
pub(crate) generate_rules_license_metadata: bool,
pub(crate) generate_cargo_toml_env_vars: bool,
}
impl Default for RenderConfig {
fn default() -> Self {
RenderConfig {
repository_name: String::default(),
build_file_template: default_build_file_template(),
crate_label_template: default_crate_label_template(),
crate_alias_template: default_crate_alias_template(),
crates_module_template: default_crates_module_template(),
crate_repository_template: default_crate_repository_template(),
default_alias_rule: AliasRule::default(),
default_package_name: Option::default(),
generate_cargo_toml_env_vars: default_generate_cargo_toml_env_vars(),
generate_target_compatible_with: default_generate_target_compatible_with(),
platforms_template: default_platforms_template(),
regen_command: String::default(),
vendor_mode: Option::default(),
generate_rules_license_metadata: default_generate_rules_license_metadata(),
}
}
}
impl RenderConfig {
pub(crate) fn are_sources_present(&self) -> bool {
self.vendor_mode == Some(VendorMode::Local)
}
}
fn default_build_file_template() -> String {
"//:BUILD.{name}-{version}.bazel".to_owned()
}
fn default_crates_module_template() -> String {
"//:{file}".to_owned()
}
fn default_crate_label_template() -> String {
"@{repository}__{name}-{version}//:{target}".to_owned()
}
fn default_crate_alias_template() -> String {
"//:{name}-{version}".to_owned()
}
fn default_crate_repository_template() -> String {
"{repository}__{name}-{version}".to_owned()
}
fn default_platforms_template() -> String {
"@rules_rust//rust/platform:{triple}".to_owned()
}
fn default_generate_cargo_toml_env_vars() -> bool {
true
}
fn default_generate_target_compatible_with() -> bool {
true
}
fn default_generate_rules_license_metadata() -> bool {
false
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum Commitish {
Tag(String),
Branch(String),
Rev(String),
}
impl From<GitReference> for Commitish {
fn from(git_ref: GitReference) -> Self {
match git_ref {
GitReference::Tag(v) => Self::Tag(v),
GitReference::Branch(v) => Self::Branch(v),
GitReference::Rev(v) => Self::Rev(v),
}
}
}
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Clone)]
pub(crate) enum AliasRule {
#[default]
#[serde(rename = "alias")]
Alias,
#[serde(rename = "dbg")]
Dbg,
#[serde(rename = "fastbuild")]
Fastbuild,
#[serde(rename = "opt")]
Opt,
#[serde(untagged)]
Custom { bzl: String, rule: String },
}
impl AliasRule {
pub(crate) fn bzl(&self) -> Option<String> {
match self {
AliasRule::Alias => None,
AliasRule::Dbg | AliasRule::Fastbuild | AliasRule::Opt => {
Some("//:alias_rules.bzl".to_owned())
}
AliasRule::Custom { bzl, .. } => Some(bzl.clone()),
}
}
pub(crate) fn rule(&self) -> String {
match self {
AliasRule::Alias => "alias".to_owned(),
AliasRule::Dbg => "transition_alias_dbg".to_owned(),
AliasRule::Fastbuild => "transition_alias_fastbuild".to_owned(),
AliasRule::Opt => "transition_alias_opt".to_owned(),
AliasRule::Custom { rule, .. } => rule.clone(),
}
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct CrateAnnotations {
pub(crate) gen_binaries: Option<GenBinaries>,
pub(crate) gen_build_script: Option<bool>,
pub(crate) deps: Option<Select<BTreeSet<Label>>>,
pub(crate) proc_macro_deps: Option<Select<BTreeSet<Label>>>,
pub(crate) crate_features: Option<Select<BTreeSet<String>>>,
pub(crate) data: Option<Select<BTreeSet<Label>>>,
pub(crate) data_glob: Option<BTreeSet<String>>,
pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
pub(crate) compile_data_glob: Option<BTreeSet<String>>,
pub(crate) compile_data_glob_excludes: Option<BTreeSet<String>>,
pub(crate) disable_pipelining: bool,
pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
pub(crate) rustc_flags: Option<Select<Vec<String>>>,
pub(crate) build_script_deps: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_link_deps: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_proc_macro_deps: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_compile_data: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_data: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_tools: Option<Select<BTreeSet<Label>>>,
pub(crate) build_script_data_glob: Option<BTreeSet<String>>,
pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) build_script_exec_properties: Option<Select<BTreeMap<String, String>>>,
pub(crate) build_script_toolchains: Option<BTreeSet<Label>>,
pub(crate) build_script_use_default_shell_env: Option<i32>,
pub(crate) build_script_rundir: Option<Select<String>>,
pub(crate) additive_build_file_content: Option<String>,
pub(crate) shallow_since: Option<String>,
pub(crate) patch_args: Option<Vec<String>>,
pub(crate) patch_tool: Option<String>,
pub(crate) patches: Option<BTreeSet<String>>,
pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
pub(crate) alias_rule: Option<AliasRule>,
pub(crate) override_targets: Option<BTreeMap<String, Label>>,
}
macro_rules! joined_extra_member {
($lhs:expr, $rhs:expr, $fn_new:expr, $fn_extend:expr) => {
if let Some(lhs) = $lhs {
if let Some(rhs) = $rhs {
let mut new = $fn_new();
$fn_extend(&mut new, lhs);
$fn_extend(&mut new, rhs);
Some(new)
} else {
Some(lhs)
}
} else if $rhs.is_some() {
$rhs
} else {
None
}
};
}
impl Add for CrateAnnotations {
type Output = CrateAnnotations;
fn add(self, rhs: Self) -> Self::Output {
fn select_merge<T>(lhs: Option<Select<T>>, rhs: Option<Select<T>>) -> Option<Select<T>>
where
T: Selectable,
{
match (lhs, rhs) {
(Some(lhs), Some(rhs)) => Some(Select::merge(lhs, rhs)),
(Some(lhs), None) => Some(lhs),
(None, Some(rhs)) => Some(rhs),
(None, None) => None,
}
}
let concat_string = |lhs: &mut String, rhs: String| {
*lhs = format!("{lhs}{rhs}");
};
#[rustfmt::skip]
let output = CrateAnnotations {
gen_binaries: self.gen_binaries.or(rhs.gen_binaries),
gen_build_script: self.gen_build_script.or(rhs.gen_build_script),
deps: select_merge(self.deps, rhs.deps),
proc_macro_deps: select_merge(self.proc_macro_deps, rhs.proc_macro_deps),
crate_features: select_merge(self.crate_features, rhs.crate_features),
data: select_merge(self.data, rhs.data),
data_glob: joined_extra_member!(self.data_glob, rhs.data_glob, BTreeSet::new, BTreeSet::extend),
disable_pipelining: self.disable_pipelining || rhs.disable_pipelining,
compile_data: select_merge(self.compile_data, rhs.compile_data),
compile_data_glob: joined_extra_member!(self.compile_data_glob, rhs.compile_data_glob, BTreeSet::new, BTreeSet::extend),
compile_data_glob_excludes: joined_extra_member!(self.compile_data_glob_excludes, rhs.compile_data_glob_excludes, BTreeSet::new, BTreeSet::extend),
rustc_env: select_merge(self.rustc_env, rhs.rustc_env),
rustc_env_files: select_merge(self.rustc_env_files, rhs.rustc_env_files),
rustc_flags: select_merge(self.rustc_flags, rhs.rustc_flags),
build_script_deps: select_merge(self.build_script_deps, rhs.build_script_deps),
build_script_link_deps: select_merge(self.build_script_link_deps, rhs.build_script_link_deps),
build_script_proc_macro_deps: select_merge(self.build_script_proc_macro_deps, rhs.build_script_proc_macro_deps),
build_script_compile_data: select_merge(self.build_script_compile_data, rhs.build_script_compile_data),
build_script_data: select_merge(self.build_script_data, rhs.build_script_data),
build_script_tools: select_merge(self.build_script_tools, rhs.build_script_tools),
build_script_data_glob: joined_extra_member!(self.build_script_data_glob, rhs.build_script_data_glob, BTreeSet::new, BTreeSet::extend),
build_script_env: select_merge(self.build_script_env, rhs.build_script_env),
build_script_rustc_env: select_merge(self.build_script_rustc_env, rhs.build_script_rustc_env),
build_script_exec_properties: select_merge(self.build_script_exec_properties, rhs.build_script_exec_properties),
build_script_toolchains: joined_extra_member!(self.build_script_toolchains, rhs.build_script_toolchains, BTreeSet::new, BTreeSet::extend),
build_script_use_default_shell_env: self.build_script_use_default_shell_env.or(rhs.build_script_use_default_shell_env),
build_script_rundir: self.build_script_rundir.or(rhs.build_script_rundir),
additive_build_file_content: joined_extra_member!(self.additive_build_file_content, rhs.additive_build_file_content, String::new, concat_string),
shallow_since: self.shallow_since.or(rhs.shallow_since),
patch_args: joined_extra_member!(self.patch_args, rhs.patch_args, Vec::new, Vec::extend),
patch_tool: self.patch_tool.or(rhs.patch_tool),
patches: joined_extra_member!(self.patches, rhs.patches, BTreeSet::new, BTreeSet::extend),
extra_aliased_targets: joined_extra_member!(self.extra_aliased_targets, rhs.extra_aliased_targets, BTreeMap::new, BTreeMap::extend),
alias_rule: self.alias_rule.or(rhs.alias_rule),
override_targets: self.override_targets.or(rhs.override_targets),
};
output
}
}
impl Sum for CrateAnnotations {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(CrateAnnotations::default(), |a, b| a + b)
}
}
#[derive(Debug, Deserialize)]
pub(crate) struct AnnotationsProvidedByPackage {
pub(crate) gen_build_script: Option<bool>,
pub(crate) data: Option<Select<BTreeSet<Label>>>,
pub(crate) data_glob: Option<BTreeSet<String>>,
pub(crate) deps: Option<Select<BTreeSet<Label>>>,
pub(crate) compile_data: Option<Select<BTreeSet<Label>>>,
pub(crate) compile_data_glob: Option<BTreeSet<String>>,
pub(crate) compile_data_glob_excludes: Option<BTreeSet<String>>,
pub(crate) rustc_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) rustc_env_files: Option<Select<BTreeSet<String>>>,
pub(crate) rustc_flags: Option<Select<Vec<String>>>,
pub(crate) build_script_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) build_script_rustc_env: Option<Select<BTreeMap<String, String>>>,
pub(crate) build_script_rundir: Option<Select<String>>,
pub(crate) additive_build_file_content: Option<String>,
pub(crate) extra_aliased_targets: Option<BTreeMap<String, String>>,
}
impl CrateAnnotations {
pub(crate) fn apply_defaults_from_package_metadata(
&mut self,
pkg_metadata: &serde_json::Value,
) {
#[deny(unused_variables)]
let AnnotationsProvidedByPackage {
gen_build_script,
data,
data_glob,
deps,
compile_data,
compile_data_glob,
compile_data_glob_excludes,
rustc_env,
rustc_env_files,
rustc_flags,
build_script_env,
build_script_rustc_env,
build_script_rundir,
additive_build_file_content,
extra_aliased_targets,
} = match AnnotationsProvidedByPackage::deserialize(&pkg_metadata["bazel"]) {
Ok(annotations) => annotations,
Err(_) => return,
};
fn default<T>(workspace_value: &mut Option<T>, default_value: Option<T>) {
if workspace_value.is_none() {
*workspace_value = default_value;
}
}
default(&mut self.gen_build_script, gen_build_script);
default(&mut self.gen_build_script, gen_build_script);
default(&mut self.data, data);
default(&mut self.data_glob, data_glob);
default(&mut self.deps, deps);
default(&mut self.compile_data, compile_data);
default(&mut self.compile_data_glob, compile_data_glob);
default(
&mut self.compile_data_glob_excludes,
compile_data_glob_excludes,
);
default(&mut self.rustc_env, rustc_env);
default(&mut self.rustc_env_files, rustc_env_files);
default(&mut self.rustc_flags, rustc_flags);
default(&mut self.build_script_env, build_script_env);
default(&mut self.build_script_rustc_env, build_script_rustc_env);
default(&mut self.build_script_rundir, build_script_rundir);
default(
&mut self.additive_build_file_content,
additive_build_file_content,
);
default(&mut self.extra_aliased_targets, extra_aliased_targets);
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct CrateId {
pub name: String,
pub version: semver::Version,
}
impl CrateId {
pub(crate) fn new(name: String, version: semver::Version) -> Self {
Self { name, version }
}
}
impl From<&Package> for CrateId {
fn from(package: &Package) -> Self {
Self {
name: package.name.clone(),
version: package.version.clone(),
}
}
}
impl Serialize for CrateId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{} {}", self.name, self.version))
}
}
struct CrateIdVisitor;
impl Visitor<'_> for CrateIdVisitor {
type Value = CrateId;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Expected string value of `{name} {version}`.")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let (name, version_str) = v.rsplit_once(' ').ok_or_else(|| {
E::custom(format!(
"Expected string value of `{{name}} {{version}}`. Got '{v}'"
))
})?;
let version = semver::Version::parse(version_str).map_err(|err| {
E::custom(format!(
"Couldn't parse {version_str} as a semver::Version: {err}"
))
})?;
Ok(CrateId {
name: name.to_string(),
version,
})
}
}
impl<'de> Deserialize<'de> for CrateId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(CrateIdVisitor)
}
}
impl std::fmt::Display for CrateId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&format!("{} {}", self.name, self.version), f)
}
}
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub(crate) enum GenBinaries {
All,
Some(BTreeSet<String>),
}
impl Default for GenBinaries {
fn default() -> Self {
GenBinaries::Some(BTreeSet::new())
}
}
impl Serialize for GenBinaries {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
GenBinaries::All => serializer.serialize_bool(true),
GenBinaries::Some(set) if set.is_empty() => serializer.serialize_bool(false),
GenBinaries::Some(set) => serializer.collect_seq(set),
}
}
}
impl<'de> Deserialize<'de> for GenBinaries {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(GenBinariesVisitor)
}
}
struct GenBinariesVisitor;
impl<'de> Visitor<'de> for GenBinariesVisitor {
type Value = GenBinaries;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("boolean, or array of bin names")
}
fn visit_bool<E>(self, gen_binaries: bool) -> Result<Self::Value, E> {
if gen_binaries {
Ok(GenBinaries::All)
} else {
Ok(GenBinaries::Some(BTreeSet::new()))
}
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
BTreeSet::deserialize(SeqAccessDeserializer::new(seq)).map(GenBinaries::Some)
}
}
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(deny_unknown_fields)]
pub(crate) struct Config {
pub(crate) generate_binaries: bool,
pub(crate) generate_build_scripts: bool,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub(crate) annotations: BTreeMap<CrateNameAndVersionReq, CrateAnnotations>,
pub(crate) rendering: RenderConfig,
#[serde(default, deserialize_with = "deserialize_cargo_config")]
pub(crate) cargo_config: Option<toml::Value>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub(crate) supported_platform_triples: BTreeSet<TargetTriple>,
}
fn deserialize_cargo_config<'de, D>(deserializer: D) -> Result<Option<toml::Value>, D::Error>
where
D: serde::Deserializer<'de>,
{
let value: Option<serde_json::Value> = Option::deserialize(deserializer)?;
match value {
None | Some(serde_json::Value::Null) => Ok(None),
Some(serde_json::Value::String(s)) => {
let normalized = s.replace("\r\n", "\n");
toml::from_str::<toml::Value>(&normalized)
.map(Some)
.map_err(serde::de::Error::custom)
}
Some(other) => serde_json::from_value(other)
.map(Some)
.map_err(serde::de::Error::custom),
}
}
impl Config {
pub(crate) fn try_from_path<T: AsRef<Path>>(path: T) -> Result<Self> {
let data = fs::read_to_string(path)?;
Ok(serde_json::from_str(&data)?)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct CrateNameAndVersionReq {
pub name: String,
version_req_string: VersionReqString,
}
impl Serialize for CrateNameAndVersionReq {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!(
"{} {}",
self.name, self.version_req_string.original
))
}
}
struct CrateNameAndVersionReqVisitor;
impl Visitor<'_> for CrateNameAndVersionReqVisitor {
type Value = CrateNameAndVersionReq;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Expected string value of `{name} {version}`.")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let (name, version) = v.rsplit_once(' ').ok_or_else(|| {
E::custom(format!(
"Expected string value of `{{name}} {{version}}`. Got '{v}'"
))
})?;
version
.parse()
.map(|version| CrateNameAndVersionReq {
name: name.to_string(),
version_req_string: version,
})
.map_err(|err| E::custom(err.to_string()))
}
}
impl<'de> Deserialize<'de> for CrateNameAndVersionReq {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(CrateNameAndVersionReqVisitor)
}
}
#[derive(Clone, Debug)]
pub struct VersionReqString {
original: String,
parsed: VersionReq,
}
impl FromStr for VersionReqString {
type Err = anyhow::Error;
fn from_str(original: &str) -> Result<Self, Self::Err> {
let parsed = VersionReq::parse(original).with_context(|| {
format!(
"VersionReqString must be a valid semver requirement: '{}'",
original
)
})?;
Ok(VersionReqString {
original: original.to_owned(),
parsed,
})
}
}
impl PartialEq for VersionReqString {
fn eq(&self, other: &Self) -> bool {
self.original == other.original
}
}
impl Eq for VersionReqString {}
impl PartialOrd for VersionReqString {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VersionReqString {
fn cmp(&self, other: &Self) -> Ordering {
Ord::cmp(&self.original, &other.original)
}
}
impl Serialize for VersionReqString {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.original)
}
}
impl<'de> Deserialize<'de> for VersionReqString {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StringVisitor;
impl Visitor<'_> for StringVisitor {
type Value = String;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("string of a semver requirement")
}
}
let original = deserializer.deserialize_str(StringVisitor)?;
let parsed = VersionReq::parse(&original).map_err(|_| {
serde::de::Error::invalid_value(
Unexpected::Str(&original),
&"a valid semver requirement",
)
})?;
Ok(VersionReqString { original, parsed })
}
}
impl CrateNameAndVersionReq {
#[cfg(test)]
pub fn new(name: String, version_req_string: VersionReqString) -> CrateNameAndVersionReq {
CrateNameAndVersionReq {
name,
version_req_string,
}
}
pub fn matches(&self, package: &Package) -> bool {
if self.name != "*" && self.name != package.name {
return false;
}
if package.version.to_string() == self.version_req_string.original {
return true;
}
if self.version_req_string.original == "*" {
return true;
}
self.version_req_string.parsed.matches(&package.version)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::test::*;
#[test]
fn test_crate_id_serde() {
let id: CrateId = serde_json::from_str("\"crate 0.1.0\"").unwrap();
assert_eq!(
id,
CrateId::new("crate".to_owned(), semver::Version::new(0, 1, 0))
);
assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
}
#[test]
fn test_crate_id_matches() {
let mut package = mock_cargo_metadata_package();
let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "0.1.0".parse().unwrap());
package.version = cargo_metadata::semver::Version::new(0, 1, 0);
assert!(id.matches(&package));
package.version = cargo_metadata::semver::Version::new(1, 0, 0);
assert!(!id.matches(&package));
}
#[test]
fn test_crate_name_and_version_req_serde() {
let id: CrateNameAndVersionReq = serde_json::from_str("\"crate 0.1.0\"").unwrap();
assert_eq!(
id,
CrateNameAndVersionReq::new(
"crate".to_owned(),
VersionReqString::from_str("0.1.0").unwrap()
)
);
assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate 0.1.0\"");
}
#[test]
fn test_crate_name_and_version_req_serde_semver() {
let id: CrateNameAndVersionReq = serde_json::from_str("\"crate *\"").unwrap();
assert_eq!(
id,
CrateNameAndVersionReq::new(
"crate".to_owned(),
VersionReqString::from_str("*").unwrap()
)
);
assert_eq!(serde_json::to_string(&id).unwrap(), "\"crate *\"");
}
#[test]
fn test_crate_name_and_version_req_semver_matches() {
let mut package = mock_cargo_metadata_package();
package.version = cargo_metadata::semver::Version::new(1, 0, 0);
let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "*".parse().unwrap());
assert!(id.matches(&package));
let mut prerelease = mock_cargo_metadata_package();
prerelease.version = cargo_metadata::semver::Version::parse("1.0.0-pre.0").unwrap();
assert!(id.matches(&prerelease));
let id = CrateNameAndVersionReq::new("mock-pkg".to_owned(), "<1".parse().unwrap());
assert!(!id.matches(&package));
}
#[test]
fn deserialize_config() {
let runfiles = runfiles::Runfiles::create().unwrap();
let path = runfiles::rlocation!(
runfiles,
"rules_rust/crate_universe/test_data/serialized_configs/config.json"
)
.unwrap();
let content = std::fs::read_to_string(path).unwrap();
let config: Config = serde_json::from_str(&content).unwrap();
let annotation = config
.annotations
.get(&CrateNameAndVersionReq::new(
"rand".to_owned(),
"0.8.5".parse().unwrap(),
))
.unwrap();
assert_eq!(
annotation.crate_features,
Some(Select::from_value(BTreeSet::from(["small_rng".to_owned()])))
);
assert!(config.cargo_config.is_none());
assert!(!config.generate_binaries);
assert!(!config.generate_build_scripts);
assert_eq!(
config.rendering.platforms_template,
"//custom/platform:{triple}"
);
}
}