1
+ use std:: {
2
+ fs:: File ,
3
+ path:: { Path , PathBuf } ,
4
+ } ;
5
+
6
+ use arrow:: json:: writer:: JsonArray ;
1
7
use rkshare:: {
2
8
eastmoney:: cli:: Eastmoney ,
3
9
sse:: cli:: Sse ,
4
10
utils:: {
5
- data:: { Data , Fetch } ,
11
+ data:: { Data , Fetch , HasTypeHint , TypeHint } ,
6
12
pretty:: pretty_print,
7
13
} ,
8
14
xueqiu:: cli:: Xueqiu ,
9
15
} ;
10
16
11
- /// 请求数据
17
+ /// 从各平台接口获取数据
18
+ #[ derive( clap:: Args , Debug ) ]
19
+ pub struct Get {
20
+ // TODO: allow specify format
21
+ /// 输出结果到文件
22
+ #[ arg( long, short, global = true ) ]
23
+ output : Option < PathBuf > ,
24
+
25
+ #[ command( subcommand) ]
26
+ command : Command ,
27
+ }
28
+
12
29
#[ derive( clap:: Subcommand , Debug ) ]
13
- pub enum Get {
30
+ enum Command {
14
31
#[ command( subcommand) ]
15
32
Sse ( Sse ) ,
16
33
#[ command( subcommand) ]
@@ -19,20 +36,72 @@ pub enum Get {
19
36
Xueqiu ( Xueqiu ) ,
20
37
}
21
38
39
+ impl HasTypeHint for Get {
40
+ fn type_hint ( & self ) -> Option < TypeHint > {
41
+ match & self . command {
42
+ Command :: Sse ( sse) => sse. type_hint ( ) ,
43
+ Command :: Eastmoney ( eastmoney) => eastmoney. type_hint ( ) ,
44
+ Command :: Xueqiu ( xueqiu) => xueqiu. type_hint ( ) ,
45
+ }
46
+ }
47
+ }
48
+
22
49
impl Get {
23
50
pub fn action ( self ) -> anyhow:: Result < ( ) > {
24
- let data = rt ( ) ?. block_on ( self . fetch ( ) ) ?;
25
- pretty_print ( data) ?;
51
+ let type_hint = self . type_hint ( ) ;
52
+ let Self { output, command } = self ;
53
+ let format = match & output {
54
+ Some ( path) => Some (
55
+ OutputFormat :: infer_from_path ( path) . ok_or ( anyhow:: anyhow!( "无法从路径推断格式" ) ) ?,
56
+ ) ,
57
+ None => None ,
58
+ } ;
59
+ // TODO: early validate
60
+
61
+ let data = rt ( ) ?. block_on ( command. fetch ( ) ) ?;
62
+
63
+ match ( format, type_hint) {
64
+ ( None , None ) => {
65
+ pretty_print ( data) ?;
66
+ }
67
+ // TODO: should split pretty print
68
+ ( None , Some ( _) ) => {
69
+ pretty_print ( data) ?;
70
+ }
71
+ ( Some ( format) , None ) => {
72
+ match format {
73
+ // TODO: 输出 JSON
74
+ OutputFormat :: Json => {
75
+ let file = File :: create ( output. unwrap ( ) ) . unwrap ( ) ;
76
+ let mut writer =
77
+ arrow:: json:: WriterBuilder :: new ( ) . build :: < _ , JsonArray > ( file) ;
78
+ writer. write ( data. as_arrow ( ) . unwrap ( ) ) . unwrap ( ) ;
79
+ }
80
+ // TODO: 输出 CSV
81
+ OutputFormat :: Csv => {
82
+ let file = File :: create ( output. unwrap ( ) ) . unwrap ( ) ;
83
+ let mut writer = arrow:: csv:: WriterBuilder :: new ( )
84
+ . with_header ( true )
85
+ . build ( file) ;
86
+ writer. write ( data. as_arrow ( ) . unwrap ( ) ) . unwrap ( ) ;
87
+ }
88
+ }
89
+ }
90
+ ( Some ( _output_format) , Some ( _data_format) ) => {
91
+ // TODO: 验证类型匹配
92
+ std:: fs:: write ( output. unwrap ( ) , & data. as_raw ( ) . unwrap ( ) ) ?;
93
+ }
94
+ }
26
95
Ok ( ( ) )
27
96
}
28
97
}
29
98
30
- impl Fetch for Get {
99
+ impl Fetch for Command {
31
100
async fn fetch ( self ) -> anyhow:: Result < Data > {
32
101
match self {
33
- Self :: Sse ( sse) => sse. fetch ( ) . await ,
34
- Self :: Eastmoney ( eastmoney) => eastmoney. fetch ( ) . await ,
35
- Self :: Xueqiu ( xueqiu) => xueqiu. fetch ( ) . await ,
102
+ Command :: Sse ( sse) => sse. fetch ( ) . await ,
103
+ Command :: Eastmoney ( eastmoney) => eastmoney. fetch ( ) . await ,
104
+ Command :: Xueqiu ( xueqiu) => xueqiu. fetch ( ) . await ,
36
105
}
37
106
}
38
107
}
@@ -42,3 +111,23 @@ fn rt() -> tokio::io::Result<tokio::runtime::Runtime> {
42
111
. enable_all ( )
43
112
. build ( )
44
113
}
114
+
115
+ // TODO: support options like CSV->TSV, json indent and formatting
116
+ /// 输出数据格式。
117
+ pub enum OutputFormat {
118
+ Json ,
119
+ Csv ,
120
+ // TODO: excel
121
+ // TODO: parquet
122
+ }
123
+
124
+ impl OutputFormat {
125
+ /// 从文件路径推断数据格式。
126
+ pub fn infer_from_path ( path : impl AsRef < Path > ) -> Option < Self > {
127
+ match path. as_ref ( ) . extension ( ) ?. to_str ( ) ? {
128
+ "json" => Some ( Self :: Json ) ,
129
+ "csv" => Some ( Self :: Csv ) ,
130
+ _ => None ,
131
+ }
132
+ }
133
+ }
0 commit comments