1- use serde:: { Deserialize , Serialize } ;
2-
3- #[ derive( Serialize , Deserialize , Clone ) ]
4- pub struct FileNode {
5- pub name : String ,
6- pub path : String ,
7- pub is_directory : bool ,
8- pub children : Vec < FileNode > ,
9- }
10-
11- fn sort_nodes ( nodes : & mut Vec < FileNode > ) {
12- nodes. sort_by ( |a, b| {
13- if a. is_directory != b. is_directory {
14- return if a. is_directory {
15- std:: cmp:: Ordering :: Less
16- } else {
17- std:: cmp:: Ordering :: Greater
18- } ;
19- }
20- a. name . to_lowercase ( ) . cmp ( & b. name . to_lowercase ( ) )
21- } ) ;
22-
23- for node in nodes {
24- if !node. children . is_empty ( ) {
25- sort_nodes ( & mut node. children ) ;
26- }
27- }
28- }
29-
30- #[ cfg( not( target_os = "android" ) ) ]
31- fn build_tree_recursive_desktop ( path_str : & str ) -> std:: io:: Result < Vec < FileNode > > {
32- use std:: fs;
33- let mut nodes = Vec :: new ( ) ;
34- let entries = fs:: read_dir ( path_str) ?;
35-
36- for entry in entries {
37- if let Ok ( entry) = entry {
38- if let Ok ( metadata) = entry. metadata ( ) {
39- let file_name = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
40-
41- let is_directory = metadata. is_dir ( ) ;
42- let starts_with_dot = file_name. starts_with ( '.' ) ;
43- let ends_with_md = file_name. ends_with ( ".md" ) ;
44-
45- if ( is_directory && !starts_with_dot) || ends_with_md {
46- let path = entry. path ( ) . to_string_lossy ( ) . to_string ( ) ;
47- let mut children = Vec :: new ( ) ;
48-
49- if is_directory {
50- if let Ok ( sub_children) = build_tree_recursive_desktop ( & path) {
51- children = sub_children;
52- }
53- }
54-
55- nodes. push ( FileNode {
56- name : file_name. trim_end_matches ( ".md" ) . to_string ( ) ,
57- path,
58- is_directory,
59- children,
60- } ) ;
61- }
62- }
63- }
64- }
65- Ok ( nodes)
66- }
67-
68- #[ cfg( target_os = "android" ) ]
69- fn build_tree_recursive_android (
70- app : tauri:: AppHandle ,
71- path : String ,
72- document_top_tree_uri : Option < String > ,
73- ) -> std:: pin:: Pin < Box < dyn std:: future:: Future < Output = Result < Vec < FileNode > , String > > + Send > > {
74- Box :: pin ( async move {
75- use tauri_plugin_android_fs:: AndroidFsExt ;
76- use tauri_plugin_android_fs:: FileUri ;
77- use tauri_plugin_android_fs:: EntryOptions ;
78- use futures:: future:: join_all;
79-
80- let api = app. android_fs ( ) ;
81-
82- let json_obj = serde_json:: json!( {
83- "uri" : path,
84- "documentTopTreeUri" : document_top_tree_uri
85- } ) ;
86- let file_uri = FileUri :: from_json_str ( & json_obj. to_string ( ) )
87- . map_err ( |e| format ! ( "Failed to create FileUri: {}" , e) ) ?;
88-
89- let options = EntryOptions {
90- uri : false ,
91- name : true ,
92- last_modified : false ,
93- len : false ,
94- mime_type : false ,
95- } ;
96-
97- let entries = api. read_dir_with_options ( & file_uri, options)
98- . map_err ( |e| e. to_string ( ) ) ?;
99-
100- let mut futures = Vec :: new ( ) ;
101- let mut nodes = Vec :: new ( ) ;
102-
103- for entry in entries {
104- let is_directory = entry. is_dir ( ) ;
105- let name_opt = entry. name ( ) ;
106-
107- if let Some ( name_str) = name_opt {
108- let name = name_str. to_string ( ) ;
109- let starts_with_dot = name. starts_with ( '.' ) ;
110- let ends_with_md = name. ends_with ( ".md" ) ;
111-
112- if ( is_directory && !starts_with_dot) || ends_with_md {
113- let path_uri = format ! ( "{}%2F{}" , path, urlencoding:: encode( & name) ) ;
114-
115- if is_directory {
116- let app_clone = app. clone ( ) ;
117- let path_clone = path_uri. clone ( ) ;
118- let doc_uri_clone = document_top_tree_uri. clone ( ) ;
119- let name_clone = name. clone ( ) ;
120-
121- futures. push ( async move {
122- let children_res = build_tree_recursive_android (
123- app_clone,
124- path_clone. clone ( ) ,
125- doc_uri_clone
126- ) . await ;
127-
128- match children_res {
129- Ok ( children) => Some ( FileNode {
130- name : name_clone. trim_end_matches ( ".md" ) . to_string ( ) ,
131- path : path_clone,
132- is_directory : true ,
133- children,
134- } ) ,
135- Err ( _) => None
136- }
137- } ) ;
138- } else {
139- nodes. push ( FileNode {
140- name : name. trim_end_matches ( ".md" ) . to_string ( ) ,
141- path : path_uri,
142- is_directory : false ,
143- children : vec ! [ ] ,
144- } ) ;
145- }
146- }
147- }
148- }
149-
150- let results = join_all ( futures) . await ;
151- for res in results {
152- if let Some ( node) = res {
153- nodes. push ( node) ;
154- }
155- }
156-
157- Ok ( nodes)
158- } )
159- }
160-
161- #[ tauri:: command]
162- async fn build_file_tree (
163- app : tauri:: AppHandle ,
164- path : String ,
165- document_top_tree_uri : Option < String > ,
166- ) -> Result < Vec < FileNode > , String > {
167- let nodes;
168-
169- #[ cfg( target_os = "android" ) ]
170- {
171- let mut unsorted_nodes = build_tree_recursive_android ( app, path, document_top_tree_uri) . await ?;
172- sort_nodes ( & mut unsorted_nodes) ;
173- nodes = unsorted_nodes;
174- }
175-
176- #[ cfg( not( target_os = "android" ) ) ]
177- {
178- // Suppress unused variable warnings on desktop
179- let _ = app;
180- let _ = document_top_tree_uri;
181- nodes = tauri:: async_runtime:: spawn_blocking ( move || {
182- let mut n = build_tree_recursive_desktop ( & path) ?;
183- sort_nodes ( & mut n) ;
184- Ok ( n)
185- } ) . await . map_err ( |e| e. to_string ( ) ) ?
186- . map_err ( |e : std:: io:: Error | e. to_string ( ) ) ?;
187- }
188-
189- Ok ( nodes)
190- }
191-
1921#[ cfg_attr( mobile, tauri:: mobile_entry_point) ]
1932pub fn run ( ) {
1943 tauri:: Builder :: default ( )
@@ -197,7 +6,6 @@ pub fn run() {
1976 . plugin ( tauri_plugin_store:: Builder :: new ( ) . build ( ) )
1987 . plugin ( tauri_plugin_fs:: init ( ) )
1998 . plugin ( tauri_plugin_android_fs:: init ( ) )
200- . invoke_handler ( tauri:: generate_handler![ build_file_tree] )
2019 . setup ( |app| {
20210 if cfg ! ( debug_assertions) {
20311 app. handle ( ) . plugin (
0 commit comments