1
1
use crate :: { PackageHashes , UrlOrPath } ;
2
2
use pep440_rs:: VersionSpecifiers ;
3
3
use pep508_rs:: { ExtraName , PackageName , Requirement } ;
4
+ use rattler_digest:: { digest:: Digest , Sha256 } ;
4
5
use serde:: { Deserialize , Serialize } ;
5
6
use serde_with:: { serde_as, skip_serializing_none} ;
6
7
use std:: cmp:: Ordering ;
7
8
use std:: collections:: BTreeSet ;
9
+ use std:: fs;
10
+ use std:: path:: Path ;
8
11
9
12
/// A pinned Pypi package
10
13
#[ serde_as]
@@ -31,6 +34,10 @@ pub struct PypiPackageData {
31
34
32
35
/// The python version that this package requires.
33
36
pub requires_python : Option < VersionSpecifiers > ,
37
+
38
+ /// Whether the projects should be installed in editable mode or not.
39
+ #[ serde( default , skip_serializing_if = "should_skip_serializing_editable" ) ]
40
+ pub editable : bool ,
34
41
}
35
42
36
43
/// Additional runtime configuration of a package. Multiple environments/platforms might refer to
@@ -77,3 +84,73 @@ impl PypiPackageData {
77
84
true
78
85
}
79
86
}
87
+
88
+ /// Used in `skip_serializing_if` to skip serializing the `editable` field if it is `false`.
89
+ fn should_skip_serializing_editable ( editable : & bool ) -> bool {
90
+ !* editable
91
+ }
92
+
93
+ /// A struct that wraps the hashable part of a source package.
94
+ ///
95
+ /// This struct the relevant parts of a source package that are used to compute a [`PackageHashes`].
96
+ pub struct PypiSourceTreeHashable {
97
+ /// The contents of an optional pyproject.toml file.
98
+ pub pyproject_toml : Option < String > ,
99
+
100
+ /// The contents of an optional setup.py file.
101
+ pub setup_py : Option < String > ,
102
+
103
+ /// The contents of an optional setup.cfg file.
104
+ pub setup_cfg : Option < String > ,
105
+ }
106
+
107
+ fn ignore_not_found < C > ( result : std:: io:: Result < C > ) -> std:: io:: Result < Option < C > > {
108
+ match result {
109
+ Ok ( content) => Ok ( Some ( content) ) ,
110
+ Err ( err) if err. kind ( ) == std:: io:: ErrorKind :: NotFound => Ok ( None ) ,
111
+ Err ( err) => Err ( err) ,
112
+ }
113
+ }
114
+
115
+ /// Ensure that line endings are normalized to `\n` this ensures that if files are checked out on
116
+ /// windows through git they still have the same hash as on linux.
117
+ fn normalize_file_contents ( contents : & str ) -> String {
118
+ contents. replace ( "\r \n " , "\n " )
119
+ }
120
+
121
+ impl PypiSourceTreeHashable {
122
+ /// Creates a new [`PypiSourceTreeHashable`] from a directory containing a source package.
123
+ pub fn from_directory ( directory : impl AsRef < Path > ) -> std:: io:: Result < Self > {
124
+ let directory = directory. as_ref ( ) ;
125
+
126
+ let pyproject_toml =
127
+ ignore_not_found ( fs:: read_to_string ( directory. join ( "pyproject.toml" ) ) ) ?;
128
+ let setup_py = ignore_not_found ( fs:: read_to_string ( directory. join ( "setup.py" ) ) ) ?;
129
+ let setup_cfg = ignore_not_found ( fs:: read_to_string ( directory. join ( "setup.cfg" ) ) ) ?;
130
+
131
+ Ok ( Self {
132
+ pyproject_toml : pyproject_toml. as_deref ( ) . map ( normalize_file_contents) ,
133
+ setup_py : setup_py. as_deref ( ) . map ( normalize_file_contents) ,
134
+ setup_cfg : setup_cfg. as_deref ( ) . map ( normalize_file_contents) ,
135
+ } )
136
+ }
137
+
138
+ /// Determine the [`PackageHashes`] of this source package.
139
+ pub fn hash ( & self ) -> PackageHashes {
140
+ let mut hasher = Sha256 :: new ( ) ;
141
+
142
+ if let Some ( pyproject_toml) = & self . pyproject_toml {
143
+ hasher. update ( pyproject_toml. as_bytes ( ) ) ;
144
+ }
145
+
146
+ if let Some ( setup_py) = & self . setup_py {
147
+ hasher. update ( setup_py. as_bytes ( ) ) ;
148
+ }
149
+
150
+ if let Some ( setup_cfg) = & self . setup_cfg {
151
+ hasher. update ( setup_cfg. as_bytes ( ) ) ;
152
+ }
153
+
154
+ PackageHashes :: Sha256 ( hasher. finalize ( ) )
155
+ }
156
+ }
0 commit comments