11//! Source code browser
22
3- use super :: file:: File as DbFile ;
4- use super :: page:: Page ;
5- use super :: MetaData ;
6- use crate :: db:: Pool ;
7- use crate :: Config ;
8- use iron:: prelude:: * ;
3+ use crate :: {
4+ db:: Pool ,
5+ impl_webpage,
6+ web:: { error:: Nope , file:: File as DbFile , page:: WebPage , MetaData } ,
7+ Config ,
8+ } ;
9+ use iron:: { status:: Status , IronError , IronResult , Request , Response } ;
910use postgres:: Connection ;
1011use router:: Router ;
11- use serde:: ser :: { Serialize , SerializeStruct , Serializer } ;
12+ use serde:: Serialize ;
1213use serde_json:: Value ;
1314use std:: cmp:: Ordering ;
14- use std:: collections:: HashMap ;
1515
16- /// A source file's type
17- #[ derive( PartialEq , PartialOrd ) ]
18- enum FileType {
19- Dir ,
20- Text ,
21- Binary ,
22- RustSource ,
23- }
24-
25- /// A source file
26- #[ derive( PartialEq , PartialOrd ) ]
16+ /// A source file's name and mime type
17+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Serialize ) ]
2718struct File {
19+ /// The name of the file
2820 name : String ,
29- file_type : FileType ,
21+ /// The mime type of the file
22+ mime : String ,
3023}
3124
3225/// A list of source files
26+ #[ derive( Debug , Clone , PartialEq , Serialize ) ]
3327struct FileList {
3428 metadata : MetaData ,
3529 files : Vec < File > ,
3630}
3731
38- impl Serialize for FileList {
39- fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
40- where
41- S : Serializer ,
42- {
43- let mut state = serializer. serialize_struct ( "FileList" , 2 ) ?;
44- state. serialize_field ( "metadata" , & self . metadata ) ?;
45-
46- let mut files = Vec :: with_capacity ( self . files . len ( ) ) ;
47- for file in & self . files {
48- let mut map = HashMap :: with_capacity ( 2 ) ;
49- map. insert ( "name" , Value :: String ( file. name . to_owned ( ) ) ) ;
50-
51- let file_type = match file. file_type {
52- FileType :: Dir => "file_type_dir" ,
53- FileType :: Text => "file_type_text" ,
54- FileType :: Binary => "file_type_binary" ,
55- FileType :: RustSource => "file_type_rust_source" ,
56- } ;
57- map. insert ( file_type, Value :: Bool ( true ) ) ;
58-
59- files. push ( map) ;
60- }
61- state. serialize_field ( "files" , & files) ?;
62-
63- state. end ( )
64- }
65- }
66-
6732impl FileList {
6833 /// Gets FileList from a request path
6934 ///
@@ -126,19 +91,15 @@ impl FileList {
12691 let path_splited: Vec < & str > = path. split ( '/' ) . collect ( ) ;
12792
12893 // if path have '/' it is a directory
129- let ftype = if path_splited. len ( ) > 1 {
130- FileType :: Dir
131- } else if mime. starts_with ( "text" ) && path_splited[ 0 ] . ends_with ( ".rs" ) {
132- FileType :: RustSource
133- } else if mime. starts_with ( "text" ) {
134- FileType :: Text
94+ let mime = if path_splited. len ( ) > 1 {
95+ "dir" . to_owned ( )
13596 } else {
136- FileType :: Binary
97+ mime . to_owned ( )
13798 } ;
13899
139100 let file = File {
140101 name : path_splited[ 0 ] . to_owned ( ) ,
141- file_type : ftype ,
102+ mime ,
142103 } ;
143104
144105 // avoid adding duplicates, a directory may occur more than once
@@ -155,9 +116,9 @@ impl FileList {
155116
156117 file_list. sort_by ( |a, b| {
157118 // directories must be listed first
158- if a. file_type == FileType :: Dir && b. file_type != FileType :: Dir {
119+ if a. mime == "dir" && b. mime != "dir" {
159120 Ordering :: Less
160- } else if a. file_type != FileType :: Dir && b. file_type == FileType :: Dir {
121+ } else if a. mime != "dir" && b. mime == "dir" {
161122 Ordering :: Greater
162123 } else {
163124 a. name . to_lowercase ( ) . cmp ( & b. name . to_lowercase ( ) )
@@ -181,6 +142,18 @@ impl FileList {
181142 }
182143}
183144
145+ #[ derive( Debug , Clone , PartialEq , Serialize ) ]
146+ struct SourcePage {
147+ file_list : FileList ,
148+ show_parent_link : bool ,
149+ file_content : Option < String > ,
150+ is_rust_source : bool ,
151+ }
152+
153+ impl_webpage ! {
154+ SourcePage = "crate/source.html" ,
155+ }
156+
184157pub fn source_browser_handler ( req : & mut Request ) -> IronResult < Response > {
185158 let router = extension ! ( req, Router ) ;
186159 let name = cexpect ! ( req, router. find( "name" ) ) ;
@@ -222,7 +195,7 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult<Response> {
222195 None
223196 } ;
224197
225- let ( content , is_rust_source) = if let Some ( file) = file {
198+ let ( file_content , is_rust_source) = if let Some ( file) = file {
226199 // serve the file with DatabaseFileHandler if file isn't text and not empty
227200 if !file. 0 . mime . starts_with ( "text" ) && !file. is_empty ( ) {
228201 return Ok ( file. serve ( ) ) ;
@@ -238,77 +211,21 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult<Response> {
238211 ( None , false )
239212 } ;
240213
241- let list = FileList :: from_path ( & conn, & name, & version, & req_path) ;
242- if list. is_none ( ) {
243- use super :: error:: Nope ;
244- use iron:: status;
245- return Err ( IronError :: new ( Nope :: NoResults , status:: NotFound ) ) ;
246- }
247-
248- let page = Page :: new ( list)
249- . set_bool ( "show_parent_link" , !req_path. is_empty ( ) )
250- . set_true ( "javascript_highlightjs" )
251- . set_true ( "show_package_navigation" )
252- . set_true ( "package_source_tab" ) ;
214+ let file_list = FileList :: from_path ( & conn, & name, & version, & req_path)
215+ . ok_or_else ( || IronError :: new ( Nope :: NoResults , Status :: NotFound ) ) ?;
253216
254- if let Some ( content) = content {
255- page. set ( "file_content" , & content)
256- . set_bool ( "file_content_rust_source" , is_rust_source)
257- . to_resp ( "source" )
258- } else {
259- page. to_resp ( "source" )
217+ SourcePage {
218+ file_list,
219+ show_parent_link : !req_path. is_empty ( ) ,
220+ file_content,
221+ is_rust_source,
260222 }
223+ . into_response ( req)
261224}
262225
263226#[ cfg( test) ]
264227mod tests {
265- use super :: * ;
266228 use crate :: test:: { assert_success, wrapper} ;
267- use serde_json:: json;
268-
269- #[ test]
270- fn serialize_file_list ( ) {
271- let file_list = FileList {
272- metadata : MetaData {
273- name : "rcc" . to_string ( ) ,
274- version : "0.0.0" . to_string ( ) ,
275- description : Some ( "it compiles an unholy language" . to_string ( ) ) ,
276- target_name : None ,
277- rustdoc_status : true ,
278- default_target : "x86_64-unknown-linux-gnu" . to_string ( ) ,
279- } ,
280- files : vec ! [
281- File {
282- name: "main.rs" . to_string( ) ,
283- file_type: FileType :: RustSource ,
284- } ,
285- File {
286- name: "lib.rs" . to_string( ) ,
287- file_type: FileType :: RustSource ,
288- } ,
289- ] ,
290- } ;
291-
292- let correct_json = json ! ( {
293- "metadata" : {
294- "name" : "rcc" ,
295- "version" : "0.0.0" ,
296- "description" : "it compiles an unholy language" ,
297- "target_name" : null,
298- "rustdoc_status" : true ,
299- "default_target" : "x86_64-unknown-linux-gnu"
300- } ,
301- "files" : [ {
302- "name" : "main.rs" ,
303- "file_type_rust_source" : true
304- } , {
305- "name" : "lib.rs" ,
306- "file_type_rust_source" : true
307- } ] ,
308- } ) ;
309-
310- assert_eq ! ( correct_json, serde_json:: to_value( & file_list) . unwrap( ) , ) ;
311- }
312229
313230 #[ test]
314231 fn cargo_ok_not_skipped ( ) {
0 commit comments