diff --git a/scarb/src/core/checksum.rs b/scarb/src/core/checksum.rs index 9ecb60230..41c008265 100644 --- a/scarb/src/core/checksum.rs +++ b/scarb/src/core/checksum.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use sha2::Digest as _; use tokio::io::{AsyncRead, AsyncReadExt}; -#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub struct Checksum([u8; 32]); diff --git a/scarb/src/core/lockfile.rs b/scarb/src/core/lockfile.rs index 2d81a3024..13fde1f97 100644 --- a/scarb/src/core/lockfile.rs +++ b/scarb/src/core/lockfile.rs @@ -1,12 +1,14 @@ +use std::collections::BTreeSet; +use std::str::FromStr; + use anyhow::{anyhow, Context, Result}; use semver::Version; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; -use std::collections::BTreeSet; -use std::str::FromStr; use toml_edit::Document; +use typed_builder::TypedBuilder; -use crate::core::{ManifestDependency, PackageId, PackageName, Resolve, SourceId}; +use crate::core::{Checksum, ManifestDependency, PackageId, PackageName, Resolve, SourceId}; const HEADER: &str = "# Code generated by scarb DO NOT EDIT."; @@ -29,13 +31,21 @@ pub struct Lockfile { pub packages: BTreeSet, } -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, TypedBuilder)] #[serde(rename_all = "kebab-case")] +#[non_exhaustive] pub struct PackageLock { pub name: PackageName, pub version: Version, + + #[builder(default)] #[serde(skip_serializing_if = "skip_path_source_id")] pub source: Option, + + #[builder(default)] + pub checksum: Option, + + #[builder(default, setter(into))] #[serde(default = "BTreeSet::new")] #[serde(skip_serializing_if = "BTreeSet::is_empty")] pub dependencies: BTreeSet, @@ -62,8 +72,12 @@ impl Lockfile { let deps = resolve .package_dependencies(package) .filter(include_package) - .map(|dep| dep.name.clone()); - PackageLock::new(&package, deps) + .map(|dep| dep.name.clone()) + .collect::>(); + PackageLock::builder() + .use_package_id(package) + .dependencies(deps) + .build() }); Self::new(packages) } @@ -126,14 +140,12 @@ impl FromStr for Lockfile { } } -impl PackageLock { - pub fn new(package: &PackageId, dependencies: impl Iterator) -> Self { - Self { - name: package.name.clone(), - version: package.version.clone(), - source: Some(package.source_id), - dependencies: dependencies.collect(), - } +type UsePackageIdFields = ((PackageName,), (Version,), (Option,), (), ()); +impl PackageLockBuilder { + pub fn use_package_id(self, package_id: PackageId) -> PackageLockBuilder { + self.name(package_id.name.clone()) + .version(package_id.version.clone()) + .source(Some(package_id.source_id)) } } @@ -153,53 +165,51 @@ impl TryFrom for PackageId { #[cfg(test)] mod tests { - use crate::core::lockfile::{Lockfile, PackageLock}; - use crate::core::{PackageId, PackageName, SourceId}; + use std::str::FromStr; - use indoc::indoc; + use expect_test::expect; use semver::Version; - use snapbox::assert_eq; - use std::str::FromStr; + + use crate::core::lockfile::{Lockfile, PackageLock}; + use crate::core::{Checksum, PackageName, SourceId}; #[test] fn simple() { - let pkg1 = PackageLock::new( - &PackageId::new( - "first".try_into().unwrap(), - Version::parse("1.0.0").unwrap(), - SourceId::default_registry(), - ), - vec![PackageName::new("fourth")].into_iter(), - ); - - let pkg2 = PackageLock { - name: "second".try_into().unwrap(), - version: Version::parse("1.0.0").unwrap(), - source: None, - dependencies: vec![PackageName::new("fourth")].into_iter().collect(), - }; - - let pkg3 = PackageLock::new( - &PackageId::new( - PackageName::new("third"), - Version::parse("2.1.0").unwrap(), - SourceId::mock_git(), - ), - vec![].into_iter(), - ); - - let pkg4 = PackageLock::new( - &PackageId::new( - PackageName::new("fourth"), - Version::parse("80.0.85").unwrap(), - SourceId::default_registry(), - ), - vec![PackageName::new("third")].into_iter(), - ); + let checksum = Checksum::parse( + "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + ) + .unwrap(); + + let pkg1 = PackageLock::builder() + .name(PackageName::new("first")) + .version(Version::parse("1.0.0").unwrap()) + .source(Some(SourceId::default_registry())) + .checksum(Some(checksum)) + .dependencies([PackageName::new("fourth")]) + .build(); + + let pkg2 = PackageLock::builder() + .name(PackageName::new("second")) + .version(Version::parse("1.0.0").unwrap()) + .dependencies([PackageName::new("fourth")]) + .build(); + + let pkg3 = PackageLock::builder() + .name(PackageName::new("third")) + .version(Version::parse("2.1.0").unwrap()) + .source(Some(SourceId::mock_git())) + .build(); + + let pkg4 = PackageLock::builder() + .name(PackageName::new("fourth")) + .version(Version::parse("80.0.85").unwrap()) + .source(Some(SourceId::default_registry())) + .dependencies([PackageName::new("third")]) + .build(); let lock = Lockfile::new(vec![pkg1, pkg2, pkg3, pkg4]); - let serialized = indoc! {r#" + let serialized = expect![[r#" # Code generated by scarb DO NOT EDIT. version = 1 @@ -207,6 +217,7 @@ mod tests { name = "first" version = "1.0.0" source = "registry+https://there-is-no-default-registry-yet.com/" + checksum = "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" dependencies = [ "fourth", ] @@ -230,10 +241,10 @@ mod tests { name = "third" version = "2.1.0" source = "git+https://github.com/starkware-libs/cairo.git?tag=test" - "#}; + "#]]; - assert_eq(serialized, lock.render().unwrap()); - let deserialized = Lockfile::from_str(serialized).unwrap(); + serialized.assert_eq(&lock.render().unwrap()); + let deserialized = Lockfile::from_str(serialized.data()).unwrap(); assert_eq!(lock, deserialized); } diff --git a/scarb/src/core/registry/mod.rs b/scarb/src/core/registry/mod.rs index 428d14e28..cbf41a3ba 100644 --- a/scarb/src/core/registry/mod.rs +++ b/scarb/src/core/registry/mod.rs @@ -250,8 +250,13 @@ pub(crate) mod mock { #[allow(unused_imports)] use $crate::core::registry::mock; let package_id = $crate::core::PackageId::from_display_str($p).unwrap(); - let dependencies: Vec<$crate::core::PackageName> = mock::pkg_names![$($d),*].iter().cloned().collect(); - $crate::core::lockfile::PackageLock::new(&package_id, dependencies.into_iter()) + let dependencies: ::std::collections::BTreeSet<$crate::core::PackageName> = ( + mock::pkg_names![$($d),*].iter().cloned().collect() + ); + $crate::core::lockfile::PackageLock::builder() + .use_package_id(package_id) + .dependencies(dependencies) + .build() }}; }