@@ -3,18 +3,31 @@ use std::fs;
33use std:: path:: Path ;
44use tokio:: time;
55
6- fn extract_links_from_file < P : AsRef < Path > > ( path : P ) -> Vec < String > {
7- let content = fs:: read_to_string ( & path) . unwrap ( ) ;
8- let url_regex = Regex :: new ( r"https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)" ) . unwrap ( ) ;
6+ fn process_path ( path : & Path , url_regex : & Regex ) -> Vec < String > {
7+ let mut links = Vec :: new ( ) ;
8+
9+ if path. is_file ( ) {
10+ if let Ok ( content) = fs:: read_to_string ( path) {
11+ links. extend ( url_regex. find_iter ( & content) . map ( |mat| {
12+ mat. as_str ( )
13+ . trim_end_matches ( & [ ')' , '>' , '.' , ',' , ';' ] [ ..] )
14+ . to_string ( )
15+ } ) ) ;
16+ }
17+ } else if path. is_dir ( ) {
18+ if let Ok ( entries) = fs:: read_dir ( path) {
19+ for entry in entries. flatten ( ) {
20+ links. extend ( process_path ( & entry. path ( ) , url_regex) ) ;
21+ }
22+ }
23+ }
924
10- url_regex
11- . find_iter ( & content)
12- . map ( |mat| {
13- let url = mat. as_str ( ) ;
14- url. trim_end_matches ( & [ ')' , '>' , '.' , ',' , ';' ] [ ..] )
15- . to_string ( )
16- } )
17- . collect ( )
25+ links
26+ }
27+
28+ fn extract_links_from_path < P : AsRef < Path > > ( path : P ) -> Vec < String > {
29+ let url_regex = Regex :: new ( r"https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)" ) . unwrap ( ) ;
30+ process_path ( path. as_ref ( ) , & url_regex)
1831}
1932
2033#[ derive( Debug , Eq , PartialEq ) ]
@@ -55,7 +68,7 @@ async fn check_link(url: &str) -> LinkCheckResult {
5568#[ tokio:: main]
5669async fn main ( ) {
5770 let file_path = "example.md" ;
58- let links = extract_links_from_file ( file_path) ;
71+ let links = extract_links_from_path ( file_path) ;
5972
6073 let mut handles = Vec :: new ( ) ;
6174 for link in links {
@@ -78,11 +91,11 @@ async fn main() {
7891#[ cfg( test) ]
7992mod tests {
8093 use super :: * ;
81- use std:: fs:: File ;
94+ use std:: fs:: { self , File } ;
8295 use std:: io:: Write ;
8396
8497 #[ test]
85- fn extracts_links_from_file ( ) -> Result < ( ) , std:: io:: Error > {
98+ fn test_extracts_links_from_file ( ) -> Result < ( ) , std:: io:: Error > {
8699 // 테스트용 파일 생성
87100 let test_file_path = "test_file.txt" ;
88101 let mut file = File :: create ( test_file_path) ?;
@@ -93,7 +106,7 @@ mod tests {
93106 ) ?;
94107
95108 // 함수 호출 및 결과 확인
96- let links = extract_links_from_file ( test_file_path) ;
109+ let links = extract_links_from_path ( test_file_path) ;
97110 assert_eq ! (
98111 links,
99112 vec![
@@ -117,4 +130,36 @@ mod tests {
117130 let link = "https://lazypazy.tistory.com" ;
118131 assert_eq ! ( check_link( link) . await , LinkCheckResult :: Valid ) ;
119132 }
133+
134+ #[ test]
135+ fn test_process_path ( ) -> Result < ( ) , std:: io:: Error > {
136+ let test_dir = "test_dir" ;
137+ if Path :: new ( test_dir) . exists ( ) {
138+ fs:: remove_dir_all ( test_dir) ?;
139+ }
140+
141+ fs:: create_dir_all ( test_dir) ?;
142+
143+ let subdir = format ! ( "{}/subdir" , test_dir) ;
144+ fs:: create_dir ( & subdir) ?;
145+
146+ let mut file1 = File :: create ( format ! ( "{}/file1.txt" , test_dir) ) ?;
147+ writeln ! ( file1, "Visit https://example1.com for more info." ) ?;
148+
149+ let mut file2 = File :: create ( format ! ( "{}/file2.txt" , subdir) ) ?;
150+ writeln ! ( file2, "Check https://example2.com and https://example3.com" ) ?;
151+
152+ let url_regex = Regex :: new ( r"https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)" ) . unwrap ( ) ;
153+
154+ let links = process_path ( Path :: new ( test_dir) , & url_regex) ;
155+
156+ fs:: remove_dir_all ( test_dir) ?;
157+
158+ assert_eq ! ( links. len( ) , 3 ) ;
159+ assert ! ( links. contains( & "https://example1.com" . to_string( ) ) ) ;
160+ assert ! ( links. contains( & "https://example2.com" . to_string( ) ) ) ;
161+ assert ! ( links. contains( & "https://example3.com" . to_string( ) ) ) ;
162+
163+ Ok ( ( ) )
164+ }
120165}
0 commit comments