1
- use std:: collections:: BTreeSet ;
1
+ use std:: collections:: { BTreeMap , BTreeSet } ;
2
2
use std:: str:: FromStr ;
3
3
4
4
use anyhow:: { Context , Result , anyhow} ;
5
+ use itertools:: Itertools ;
5
6
use semver:: Version ;
6
7
use serde:: { Deserialize , Serialize } ;
7
8
use serde_repr:: { Deserialize_repr , Serialize_repr } ;
9
+ use smallvec:: SmallVec ;
8
10
use toml_edit:: DocumentMut ;
9
11
use typed_builder:: TypedBuilder ;
10
12
@@ -23,12 +25,44 @@ pub enum LockVersion {
23
25
24
26
#[ derive( Debug , Clone , Eq , PartialEq , Default , Serialize , Deserialize ) ]
25
27
#[ serde( rename_all = "kebab-case" ) ]
28
+ #[ serde( from = "serdex::Lockfile" , into = "serdex::Lockfile" ) ]
26
29
pub struct Lockfile {
27
- pub version : LockVersion ,
28
- #[ serde( rename = "package" ) ]
29
- #[ serde( default = "BTreeSet::new" ) ]
30
- #[ serde( skip_serializing_if = "BTreeSet::is_empty" ) ]
31
- pub packages : BTreeSet < PackageLock > ,
30
+ version : LockVersion ,
31
+ // Note that the current compilation model will not allow Scarb to emit lock files with multiple
32
+ // entries for the same package name. This structure allows this for the sake of future-proofing
33
+ // and being more defensive against lock files tinkered with by hand. Thus, we use `SmallVec`
34
+ // to optimise for the common case of a single entry only.
35
+ packages : BTreeMap < PackageName , SmallVec < [ PackageLock ; 1 ] > > ,
36
+ }
37
+
38
+ mod serdex {
39
+ use crate :: core:: lockfile:: { LockVersion , PackageLock } ;
40
+ use serde:: { Deserialize , Serialize } ;
41
+ use std:: collections:: BTreeSet ;
42
+
43
+ #[ derive( Serialize , Deserialize ) ]
44
+ pub struct Lockfile {
45
+ version : LockVersion ,
46
+ #[ serde( rename = "package" ) ]
47
+ #[ serde( default = "BTreeSet::new" ) ]
48
+ #[ serde( skip_serializing_if = "BTreeSet::is_empty" ) ]
49
+ packages : BTreeSet < PackageLock > ,
50
+ }
51
+
52
+ impl From < Lockfile > for super :: Lockfile {
53
+ fn from ( value : Lockfile ) -> Self {
54
+ Self :: new ( value. packages ) . with_version ( value. version )
55
+ }
56
+ }
57
+
58
+ impl From < super :: Lockfile > for Lockfile {
59
+ fn from ( value : super :: Lockfile ) -> Self {
60
+ Self {
61
+ version : value. version ,
62
+ packages : value. packages . into_values ( ) . flatten ( ) . collect ( ) ,
63
+ }
64
+ }
65
+ }
32
66
}
33
67
34
68
#[ derive( Clone , Debug , Eq , PartialEq , Ord , PartialOrd , Serialize , Deserialize , TypedBuilder ) ]
@@ -57,12 +91,24 @@ fn skip_path_source_id(sid: &Option<SourceId>) -> bool {
57
91
58
92
impl Lockfile {
59
93
pub fn new ( packages : impl IntoIterator < Item = PackageLock > ) -> Self {
94
+ let packages = packages
95
+ . into_iter ( )
96
+ . map ( |lock| ( lock. name . clone ( ) , lock) )
97
+ . chunk_by ( |( name, _) | name. clone ( ) ) ;
98
+ let packages = packages
99
+ . into_iter ( )
100
+ . map ( |( name, locks) | ( name, locks. into_iter ( ) . map ( |( _, lock) | lock) . collect ( ) ) )
101
+ . collect ( ) ;
60
102
Self {
61
103
version : Default :: default ( ) ,
62
- packages : packages . into_iter ( ) . collect ( ) ,
104
+ packages,
63
105
}
64
106
}
65
107
108
+ fn with_version ( self , version : LockVersion ) -> Self {
109
+ Self { version, ..self }
110
+ }
111
+
66
112
pub fn from_resolve ( resolve : & Resolve ) -> Self {
67
113
let include_package = |package_id : & PackageId | !package_id. source_id . is_std ( ) ;
68
114
let packages = resolve
@@ -87,13 +133,24 @@ impl Lockfile {
87
133
}
88
134
89
135
pub fn packages ( & self ) -> impl Iterator < Item = & PackageLock > {
90
- self . packages . iter ( )
136
+ self . packages . values ( ) . flatten ( )
137
+ }
138
+
139
+ pub fn packages_by_name < ' a > (
140
+ & ' a self ,
141
+ name : & PackageName ,
142
+ ) -> Box < dyn Iterator < Item = & ' a PackageLock > + ' a > {
143
+ if let Some ( locks) = self . packages . get ( name) {
144
+ Box :: new ( locks. iter ( ) )
145
+ } else {
146
+ Box :: new ( std:: iter:: empty ( ) )
147
+ }
91
148
}
92
149
93
- pub fn packages_matching ( & self , dependency : ManifestDependency ) -> Option < Result < PackageId > > {
94
- self . packages ( )
95
- . filter ( |p| dependency. matches_name_and_version ( & p. name , & p. version ) )
96
- . find ( |p| {
150
+ pub fn package_matching ( & self , dependency : ManifestDependency ) -> Option < Result < PackageId > > {
151
+ self . packages_by_name ( & dependency . name )
152
+ . find ( |p| dependency. matches_name_and_version ( & p. name , & p. version ) )
153
+ . filter ( |p| {
97
154
p. source
98
155
. map ( |sid| sid. can_lock_source_id ( dependency. source_id ) )
99
156
// No locking occurs on path sources.
0 commit comments