@@ -2,41 +2,148 @@ use std::sync::Arc;
22
33use axum:: {
44 extract:: { Path , Query } ,
5- response:: IntoResponse ,
6- Extension ,
5+ Extension , Json ,
76} ;
87
98use super :: prelude:: * ;
109
11- #[ derive( Debug , serde:: Deserialize ) ]
12- pub struct Params {
13- pub rev : Option < String > ,
10+ #[ derive( Debug , serde:: Deserialize , Default ) ]
11+ pub ( super ) struct Params {
12+ /// 1-indexed line number at which to start the snippet
13+ pub line_start : Option < isize > ,
14+
15+ /// 1-indexed line number at which to end the snippet
16+ pub line_end : Option < usize > ,
1417}
1518
1619#[ derive( serde:: Serialize ) ]
17- pub struct FileResponse {
20+ pub ( super ) struct FileResponse {
1821 contents : String ,
1922}
2023
2124impl super :: ApiResponse for FileResponse { }
2225
23- pub async fn handle (
26+ pub ( super ) async fn handle < ' a > (
2427 Path ( path) : Path < String > ,
2528 Query ( params) : Query < Params > ,
2629 Extension ( indexes) : Extension < Arc < Indexes > > ,
27- ) -> impl IntoResponse {
30+ ) -> Result < Json < super :: Response < ' a > > , Error > {
2831 // Strip leading slash, always present.
2932 let file_disk_path = & path[ 1 ..] ;
3033
31- if params. rev . is_some ( ) {
32- return Err ( Error :: internal ( "the `rev` parameter is not yet supported" ) ) ;
33- }
34-
35- let contents = indexes
34+ let doc = indexes
3635 . file
3736 . file_body ( file_disk_path)
3837 . await
3938 . map_err ( Error :: internal) ?;
4039
41- Ok ( json ( FileResponse { contents } ) )
40+ Ok ( json ( FileResponse {
41+ contents : split_by_lines ( & doc. content , & doc. line_end_indices , & params) ?. to_string ( ) ,
42+ } ) )
43+ }
44+
45+ fn split_by_lines < ' a > ( text : & ' a str , indices : & [ u32 ] , params : & Params ) -> Result < & ' a str , Error > {
46+ let char_start = match params. line_start {
47+ Some ( line_start) if line_start == 1 => 0 ,
48+ Some ( line_start) if line_start > 1 => {
49+ ( indices
50+ . get ( line_start as usize - 2 )
51+ . ok_or_else ( || Error :: user ( "invalid line number" ) ) ?
52+ + 1 ) as usize
53+ }
54+ Some ( _) => return Err ( Error :: user ( "line numbers are 1-indexed!" ) ) ,
55+ _ => 0 ,
56+ } ;
57+
58+ let line_end = params. line_end . unwrap_or ( indices. len ( ) ) - 1 ;
59+ let char_end = * indices
60+ . get ( line_end)
61+ . ok_or_else ( || Error :: user ( "invalid line number" ) ) ? as usize ;
62+
63+ Ok ( & text[ char_start..=char_end] )
64+ }
65+
66+ #[ cfg( test) ]
67+ mod tests {
68+ use super :: * ;
69+
70+ #[ test]
71+ fn no_params ( ) {
72+ let text = r#"aaaaaa
73+ bbbbbb
74+ cccccc
75+ "# ;
76+
77+ let indices = text
78+ . match_indices ( '\n' )
79+ . map ( |( i, _) | i as u32 )
80+ . collect :: < Vec < _ > > ( ) ;
81+
82+ println ! ( "{indices:?}" ) ;
83+
84+ assert_eq ! (
85+ split_by_lines(
86+ text,
87+ & indices,
88+ & Params {
89+ line_start: None ,
90+ line_end: None
91+ }
92+ )
93+ . unwrap_or_else( |_| panic!( "bad" ) ) ,
94+ text
95+ ) ;
96+
97+ assert_eq ! (
98+ split_by_lines(
99+ text,
100+ & indices,
101+ & Params {
102+ line_start: Some ( 1 ) ,
103+ line_end: None
104+ }
105+ )
106+ . unwrap_or_else( |_| panic!( "bad" ) ) ,
107+ text
108+ ) ;
109+
110+ assert_eq ! (
111+ split_by_lines(
112+ text,
113+ & indices,
114+ & Params {
115+ line_start: Some ( 2 ) ,
116+ line_end: None
117+ }
118+ )
119+ . unwrap_or_else( |_| panic!( "bad" ) ) ,
120+ & text[ 7 ..]
121+ ) ;
122+
123+ assert_eq ! (
124+ split_by_lines(
125+ text,
126+ & indices,
127+ & Params {
128+ line_start: Some ( 3 ) ,
129+ line_end: Some ( 3 ) ,
130+ }
131+ )
132+ . unwrap_or_else( |_| panic!( "bad" ) ) ,
133+ & text[ 14 ..]
134+ ) ;
135+
136+ assert_eq ! (
137+ split_by_lines(
138+ text,
139+ & indices,
140+ & Params {
141+ line_start: Some ( 2 ) ,
142+ line_end: Some ( 3 ) ,
143+ }
144+ )
145+ . unwrap_or_else( |_| panic!( "bad" ) ) ,
146+ & text[ 7 ..]
147+ ) ;
148+ }
42149}
0 commit comments