11use anyhow:: { anyhow, Context , Result } ;
22use async_std:: prelude:: FutureExt ;
3+ use either:: Either ;
34use futures:: stream:: { self , StreamExt } ;
45use log:: { info, warn} ;
56use prettytable:: { cell, format, row, Table } ;
7+ use std:: io:: { self , BufRead , Write } ;
68use std:: path:: PathBuf ;
79use structopt:: StructOpt ;
810
@@ -51,6 +53,74 @@ impl FeedCommand {
5153 Ok ( ( ) )
5254 }
5355
56+ async fn select_remotes ( state : & State , candidates : Vec < String > ) -> Result < RemoteFeed > {
57+ if candidates. is_empty ( ) {
58+ return Err ( anyhow ! (
59+ "Supplied URL is not a feed, and we can't find any potential candidate in the page."
60+ ) ) ;
61+ }
62+
63+ let length = candidates. len ( ) ;
64+ if length == 1 {
65+ let url = candidates. first ( ) . unwrap ( ) ;
66+ log:: info!(
67+ "Supplied URL is not a feed, but we found a potential candidate: {}" ,
68+ url
69+ ) ;
70+ return Ok ( RemoteFeed :: new ( url) . await ?) ;
71+ }
72+
73+ println ! (
74+ "Supplied URL is not a feed, but we found {} potential candidates. Please select one:" ,
75+ length
76+ ) ;
77+
78+ for ( idx, url) in candidates. iter ( ) . enumerate ( ) {
79+ println ! ( "{}) {}" , idx, url) ;
80+ }
81+
82+ let stdin = io:: stdin ( ) ;
83+ loop {
84+ print ! ( "select (0-{}, c to cancel): " , length - 1 ) ;
85+ io:: stdout ( ) . flush ( ) ?;
86+
87+ let mut selection = String :: new ( ) ;
88+ stdin. lock ( ) . read_line ( & mut selection) ?;
89+
90+ let selection = selection. trim ( ) ;
91+ if selection == "c" {
92+ break Err ( anyhow ! ( "No selection was made." ) ) ;
93+ }
94+
95+ match selection. parse :: < usize > ( ) {
96+ Ok ( select) if select < length => {
97+ let url = candidates. get ( select) . unwrap ( ) ;
98+
99+ let feed = {
100+ let conn = state. db . get ( ) ?;
101+ Feed :: get_by_url ( & conn, & url) ?
102+ } ;
103+
104+ if feed. is_some ( ) {
105+ println ! ( "Error: Invalid selection: selected feed already exists" ) ;
106+ continue ;
107+ }
108+
109+ match RemoteFeed :: new ( candidates. get ( select) . unwrap ( ) ) . await {
110+ Ok ( feed) => break Ok ( feed) ,
111+ Err ( e) => println ! ( "Error: Selection is not a feed: {}" , e) ,
112+ }
113+ }
114+ Ok ( _) => {
115+ println ! ( "Error: Invalid selection: out of range" ) ;
116+ }
117+ Err ( e) => {
118+ println ! ( "Error: Invalid selection: {}" , e) ;
119+ }
120+ }
121+ }
122+ }
123+
54124 async fn add ( state : State , url : String , group : Option < String > ) -> Result < ( ) > {
55125 let feed = {
56126 let conn = state. db . get ( ) ?;
@@ -61,7 +131,12 @@ impl FeedCommand {
61131 return Err ( anyhow ! ( "Feed `{}` already exists!" , url) ) ;
62132 }
63133
64- let remote = RemoteFeed :: new ( & url) . await ?;
134+ let remote = match RemoteFeed :: try_new ( & url) . await ? {
135+ Either :: Left ( remote) => remote,
136+ Either :: Right ( candidates) => Self :: select_remotes ( & state, candidates) . await ?,
137+ } ;
138+
139+ let url = remote. get_url ( ) . to_owned ( ) ;
65140
66141 let feed = Feed :: new (
67142 remote
0 commit comments