1- use crate :: httpclient :: { Error as HttpError , HttpSnafu , UrlParseSnafu } ;
1+ use mime_guess :: from_path ;
22use reqwest;
33use serde:: de:: DeserializeOwned ;
44use snafu:: { ResultExt , Snafu } ;
5- use std:: path:: Path ;
5+ use std:: path:: { Path , PathBuf } ;
66use tokio:: fs:: File ;
77use tokio_util:: codec:: { BytesCodec , FramedRead } ;
8- use url:: Url ;
8+ use url:: { ParseError , Url } ;
99use walkdir:: WalkDir ;
1010
11+ use std:: io;
12+ use std:: sync:: LazyLock ;
1113pub struct ZenodoClient {
1214 http_client : reqwest:: Client ,
1315 base_url : Url ,
1416 token : String ,
1517}
1618
17- const BASE_URL : Url = Url :: parse ( "https://zenodo.org" ) . unwrap ( ) ;
19+ static BASE_URL : LazyLock < Url > =
20+ LazyLock :: new ( || Url :: parse ( "https://zenodo.org" ) . expect ( "Invalid Base URL config" ) ) ;
1821
19- #[ derive( Debug , Serialize , Deserialize ) ]
20- pub struct Deposition {
21- pub search : SearchServiceVersion ,
22- pub data : SimpleVersion ,
22+ #[ derive( Debug , Snafu ) ]
23+ pub enum Error {
24+ #[ snafu( display( "A zendoo client error occured: {}" , source) ) ]
25+ Reqwest { source : reqwest:: Error } ,
26+ #[ snafu( display( "A directory listing error occured: {}" , source) ) ]
27+ DirWalk { source : walkdir:: Error } ,
28+ #[ snafu( display( "An error occured reading the response: {}" , source) ) ]
29+ DeserializeResp { source : reqwest:: Error } ,
30+ #[ snafu( display( "An error occured reading the response: {}" , source) ) ]
31+ FileReading { source : io:: Error } ,
32+ #[ snafu( display( "An error occured parsing the file path: {}" , fp. to_string_lossy( ) ) ) ]
33+ FileParsing { fp : PathBuf } ,
34+ #[ snafu( display( "An error occured parsing the url {}" , source) ) ]
35+ UrlParse { source : ParseError } ,
2336}
2437
2538impl ZenodoClient {
2639 pub fn new ( token : String ) -> ZenodoClient {
2740 let http_client = reqwest:: Client :: new ( ) ;
2841 return ZenodoClient {
2942 http_client,
30- base_url : BASE_URL ,
43+ base_url : BASE_URL . clone ( ) ,
3144 token,
3245 } ;
3346 }
@@ -36,55 +49,76 @@ impl ZenodoClient {
3649 return self . base_url . join ( path) . context ( UrlParseSnafu ) ;
3750 }
3851
39- pub async fn get_deposition < R : DeserializeOwned > ( & self , id : & str ) -> Result < R , Error > {
40- let endpoint = self . make_url ( format ! ( "/api/deposit/depositions/{id}" ) ) ;
52+ pub async fn get_deposition < R : DeserializeOwned > (
53+ & self ,
54+ deposition_id : & str ,
55+ ) -> Result < R , Error > {
56+ let endpoint = self . make_url ( & format ! ( "/api/deposit/depositions/{deposition_id}" ) ) ?;
4157 let res = self
4258 . http_client
43- . get ( enpdoint )
59+ . get ( endpoint )
4460 . send ( )
4561 . await
46- . context ( HttpSnafu { url : endpoint } ) ?;
47- res. error_for_status ( ) ?;
48- return res. json ( ) ;
62+ . context ( ReqwestSnafu ) ?;
63+ // res.error_for_status().context(ReqwestSnafu )?;
64+ return res. json :: < R > ( ) . await . context ( DeserializeRespSnafu ) ;
4965 }
5066
5167 pub async fn upload_files ( & self , deposition_id : & str , source_path : & Path ) -> Result < ( ) , Error > {
5268 for f in WalkDir :: new ( source_path) {
53- self . upload_file ( deposition_id, f?. path ( ) ) . await ?;
69+ self . upload_file :: < ( ) > ( deposition_id, f. context ( DirWalkSnafu ) ?. path ( ) )
70+ . await ?;
5471 }
72+ Ok ( ( ) )
5573 }
5674
5775 pub async fn list_files < R : DeserializeOwned > ( & self , deposition_id : & str ) -> Result < R , Error > {
58- let endpoint = self . make_url ( format ! ( "/api/deposit/depositions/{id }/files" ) ) ;
76+ let endpoint = self . make_url ( & format ! ( "/api/deposit/depositions/{deposition_id }/files" ) ) ? ;
5977 let res = self
6078 . http_client
61- . get ( enpdoint )
79+ . get ( endpoint )
6280 . send ( )
6381 . await
64- . context ( HttpSnafu { url : endpoint } ) ?;
65- res. error_for_status ( ) ?;
66- return res. json ( ) ;
82+ . context ( ReqwestSnafu ) ?;
83+ // res.error_for_status().context(ReqwestSnafu )?;
84+ return res. json :: < R > ( ) . await . context ( DeserializeRespSnafu ) ;
6785 }
6886
6987 async fn upload_file < R : DeserializeOwned > (
7088 & self ,
7189 deposition_id : & str ,
7290 file_path : & Path ,
7391 ) -> Result < R , Error > {
74- let file = File :: open ( file_path) . await ?;
75- let endpoint = self . make_url ( format ! ( "/api/deposit/depositions/{id }/files" ) ) ;
92+ let file = File :: open ( file_path) . await . context ( FileReadingSnafu ) ?;
93+ let endpoint = self . make_url ( & format ! ( "/api/deposit/depositions/{deposition_id }/files" ) ) ? ;
7694 let stream = FramedRead :: new ( file, BytesCodec :: new ( ) ) ;
95+ let file_name = file_path
96+ . file_name ( )
97+ . ok_or ( Error :: FileParsing {
98+ fp : file_path. to_path_buf ( ) ,
99+ } ) ?
100+ . to_str ( )
101+ . ok_or ( Error :: FileParsing {
102+ fp : file_path. to_path_buf ( ) ,
103+ } ) ?;
104+ let mime_type = from_path ( file_path) . first_or_octet_stream ( ) ;
77105 let form = reqwest:: multipart:: Form :: new ( )
78- . text ( "name" , file_path. file_name ( ) ?. to_str ( ) ?)
79- . part ( "file" , stream) ;
106+ . text ( "name" , file_name. to_owned ( ) )
107+ . part (
108+ "file" ,
109+ reqwest:: multipart:: Part :: stream ( reqwest:: Body :: wrap_stream ( stream) )
110+ . file_name ( file_name. to_owned ( ) )
111+ . mime_str ( mime_type. as_ref ( ) )
112+ . context ( ReqwestSnafu ) ?,
113+ ) ;
80114 let res = self
81115 . http_client
82116 . post ( endpoint)
83117 . multipart ( form)
84118 . send ( )
85119 . await
86- . context ( HttpSnafu { url : endpoint } ) ?;
87- res. error_for_status ( ) ?;
88- return res. json ( ) ;
120+ . context ( ReqwestSnafu ) ?;
121+ // res.error_for_status().context(ReqwestSnafu )?;
122+ return res. json :: < R > ( ) . await . context ( DeserializeRespSnafu ) ;
89123 }
90124}
0 commit comments