@@ -6,6 +6,8 @@ use std::path::{Path, PathBuf};
66use fs_extra:: dir:: { copy, CopyOptions } ;
77use git2:: { BranchType , ObjectType , Repository as GitRepository , ResetType } ;
88use remove_dir_all:: remove_dir_all;
9+ use serde:: de:: { self , Deserializer , Unexpected , Visitor } ;
10+ use serde:: ser:: Serializer ;
911use serde:: { Deserialize , Serialize } ;
1012
1113use crate :: prelude:: * ;
@@ -103,25 +105,57 @@ impl<'a> Repository<'a> {
103105pub struct Manifest {
104106 name : String ,
105107 work_dir : Option < PathBuf > ,
106- executable : Vec < FileEntry > ,
107- readonly : Vec < FileEntry > ,
108108 binary : BinaryConfig ,
109109}
110110
111111#[ derive( Serialize ) ]
112112pub struct SolidManifest {
113113 name : String ,
114114 work_dir : PathBuf ,
115- executable : Vec < SolidFileEntry > ,
116- readonly : Vec < SolidFileEntry > ,
117- binary : BinaryConfig ,
115+ binary : SolidBinaryConfig ,
118116}
119117
120118impl Manifest {
121119 pub fn name ( & self ) -> & String {
122120 & self . name
123121 }
124122
123+ pub fn binary ( & self ) -> & BinaryConfig {
124+ & self . binary
125+ }
126+
127+ pub fn solidify ( & self ) -> SomaResult < SolidManifest > {
128+ let work_dir = match & self . work_dir {
129+ Some ( path) => path. clone ( ) ,
130+ None => PathBuf :: from ( format ! ( "/home/{}" , self . name) ) ,
131+ } ;
132+
133+ let binary = self . binary . solidify ( & work_dir) ?;
134+
135+ Ok ( SolidManifest {
136+ name : self . name . clone ( ) ,
137+ work_dir,
138+ binary,
139+ } )
140+ }
141+ }
142+
143+ #[ derive( Deserialize ) ]
144+ pub struct BinaryConfig {
145+ os : String ,
146+ cmd : String ,
147+ executable : Vec < FileEntry > ,
148+ readonly : Vec < FileEntry > ,
149+ }
150+
151+ #[ derive( Serialize ) ]
152+ struct SolidBinaryConfig {
153+ os : String ,
154+ cmd : String ,
155+ file_entries : Vec < SolidFileEntry > ,
156+ }
157+
158+ impl BinaryConfig {
125159 pub fn executable ( & self ) -> & Vec < FileEntry > {
126160 & self . executable
127161 }
@@ -130,32 +164,81 @@ impl Manifest {
130164 & self . readonly
131165 }
132166
133- pub fn solidify ( & self ) -> SomaResult < SolidManifest > {
134- let work_dir = match & self . work_dir {
135- Some ( path) => path. clone ( ) ,
136- None => PathBuf :: from ( format ! ( "/home/{}" , self . name) ) ,
137- } ;
167+ fn solidify ( & self , work_dir : impl AsRef < Path > ) -> SomaResult < SolidBinaryConfig > {
138168 let executable = self
139169 . executable
140170 . iter ( )
141- . map ( |file| file. solidify ( & work_dir) )
142- . collect :: < SomaResult < Vec < _ > > > ( ) ?;
171+ . map ( |file| file. solidify ( & work_dir, FilePermissions :: Executable ) ) ;
143172 let readonly = self
144173 . readonly
145174 . iter ( )
146- . map ( |file| file. solidify ( & work_dir) )
147- . collect :: < SomaResult < Vec < _ > > > ( ) ?;
175+ . map ( |file| file. solidify ( & work_dir, FilePermissions :: ReadOnly ) ) ;
176+ let file_entries = executable . chain ( readonly ) . collect :: < SomaResult < Vec < _ > > > ( ) ?;
148177
149- Ok ( SolidManifest {
150- name : self . name . clone ( ) ,
151- work_dir,
152- executable,
153- readonly,
154- binary : self . binary . clone ( ) ,
178+ Ok ( SolidBinaryConfig {
179+ os : self . os . clone ( ) ,
180+ cmd : self . cmd . clone ( ) ,
181+ file_entries,
155182 } )
156183 }
157184}
158185
186+ #[ derive( Debug , PartialEq ) ]
187+ enum FilePermissions {
188+ Custom ( u16 ) ,
189+ Executable ,
190+ ReadOnly ,
191+ // Reserved: FetchOnly, ReadWrite
192+ }
193+
194+ impl Serialize for FilePermissions {
195+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
196+ where
197+ S : Serializer ,
198+ {
199+ let permissions_string = match self {
200+ FilePermissions :: Custom ( permissions) => format ! ( "{:o}" , permissions) ,
201+ FilePermissions :: Executable => "550" . to_owned ( ) ,
202+ FilePermissions :: ReadOnly => "440" . to_owned ( ) ,
203+ } ;
204+ serializer. serialize_str ( & permissions_string)
205+ }
206+ }
207+
208+ impl < ' de > Deserialize < ' de > for FilePermissions {
209+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
210+ where
211+ D : Deserializer < ' de > ,
212+ {
213+ deserializer. deserialize_str ( PermissionsString )
214+ }
215+ }
216+
217+ struct PermissionsString ;
218+
219+ impl < ' de > Visitor < ' de > for PermissionsString {
220+ type Value = FilePermissions ;
221+
222+ fn expecting ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
223+ write ! (
224+ formatter,
225+ "a file permissions string in octal number format"
226+ )
227+ }
228+
229+ fn visit_str < E > ( self , s : & str ) -> Result < Self :: Value , E >
230+ where
231+ E : de:: Error ,
232+ {
233+ let permissions = u16:: from_str_radix ( s, 8 ) ;
234+ match permissions {
235+ // Support sticky bits later
236+ Ok ( permissions) if permissions <= 0o777 => Ok ( FilePermissions :: Custom ( permissions) ) ,
237+ _ => Err ( de:: Error :: invalid_value ( Unexpected :: Str ( s) , & self ) ) ,
238+ }
239+ }
240+ }
241+
159242// target_path is defined as String instead of PathBuf to support Windows
160243#[ derive( Deserialize ) ]
161244pub struct FileEntry {
@@ -165,10 +248,11 @@ pub struct FileEntry {
165248}
166249
167250#[ derive( Serialize ) ]
168- pub struct SolidFileEntry {
251+ struct SolidFileEntry {
169252 path : PathBuf ,
170253 public : bool ,
171254 target_path : String ,
255+ permissions : FilePermissions ,
172256}
173257
174258impl FileEntry {
@@ -180,11 +264,11 @@ impl FileEntry {
180264 self . public . unwrap_or ( false )
181265 }
182266
183- pub fn target_path ( & self ) -> & Option < String > {
184- & self . target_path
185- }
186-
187- pub fn solidify ( & self , work_dir : impl AsRef < Path > ) -> SomaResult < SolidFileEntry > {
267+ fn solidify (
268+ & self ,
269+ work_dir : impl AsRef < Path > ,
270+ permissions : FilePermissions ,
271+ ) -> SomaResult < SolidFileEntry > {
188272 let target_path = match & self . target_path {
189273 Some ( path) => path. clone ( ) ,
190274 None => {
@@ -206,16 +290,11 @@ impl FileEntry {
206290 path : self . path . clone ( ) ,
207291 public : self . public . unwrap_or ( false ) ,
208292 target_path,
293+ permissions,
209294 } )
210295 }
211296}
212297
213- #[ derive( Clone , Deserialize , Serialize ) ]
214- struct BinaryConfig {
215- os : String ,
216- entry : String ,
217- }
218-
219298fn read_file_contents ( path : impl AsRef < Path > ) -> SomaResult < Vec < u8 > > {
220299 let mut file = File :: open ( path) ?;
221300 let mut contents = Vec :: new ( ) ;
@@ -226,3 +305,27 @@ fn read_file_contents(path: impl AsRef<Path>) -> SomaResult<Vec<u8>> {
226305pub fn load_manifest ( manifest_path : impl AsRef < Path > ) -> SomaResult < Manifest > {
227306 Ok ( toml:: from_slice ( & read_file_contents ( manifest_path) ?) ?)
228307}
308+
309+ #[ cfg( test) ]
310+ mod tests {
311+ use super :: * ;
312+ use serde_test:: { assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, Token } ;
313+
314+ #[ test]
315+ fn test_file_permissions_ser ( ) {
316+ assert_ser_tokens ( & FilePermissions :: Executable , & [ Token :: Str ( "550" ) ] ) ;
317+ assert_ser_tokens ( & FilePermissions :: ReadOnly , & [ Token :: Str ( "440" ) ] ) ;
318+ assert_ser_tokens ( & FilePermissions :: Custom ( 0o777 ) , & [ Token :: Str ( "777" ) ] ) ;
319+ }
320+
321+ #[ test]
322+ fn test_file_permissions_de ( ) {
323+ assert_de_tokens ( & FilePermissions :: Custom ( 0o550 ) , & [ Token :: Str ( "550" ) ] ) ;
324+ assert_de_tokens ( & FilePermissions :: Custom ( 0o440 ) , & [ Token :: Str ( "440" ) ] ) ;
325+ assert_de_tokens ( & FilePermissions :: Custom ( 0o777 ) , & [ Token :: Str ( "777" ) ] ) ;
326+ assert_de_tokens_error :: < FilePermissions > ( & [
327+ Token :: Str ( "1000" )
328+ ] , "invalid value: string \" 1000\" , expected a file permissions string in octal number format"
329+ ) ;
330+ }
331+ }
0 commit comments