1
1
use std:: {
2
- fs,
3
2
io:: stdout,
4
3
mem,
5
- path:: { Path , PathBuf } ,
6
4
str:: FromStr ,
7
5
sync:: {
8
6
atomic:: { AtomicBool , Ordering } ,
@@ -27,7 +25,11 @@ use objdiff_core::{
27
25
watcher:: { create_watcher, Watcher } ,
28
26
BuildConfig ,
29
27
} ,
30
- config:: { build_globset, ProjectConfig , ProjectObject } ,
28
+ config:: {
29
+ build_globset,
30
+ path:: { check_path_buf, platform_path, platform_path_serde_option} ,
31
+ ProjectConfig , ProjectObject , ProjectObjectMetadata ,
32
+ } ,
31
33
diff,
32
34
diff:: {
33
35
ConfigEnum , ConfigPropertyId , ConfigPropertyKind , DiffObjConfig , MappingConfig , ObjDiff ,
@@ -40,6 +42,7 @@ use objdiff_core::{
40
42
obj:: ObjInfo ,
41
43
} ;
42
44
use ratatui:: prelude:: * ;
45
+ use typed_path:: { Utf8PlatformPath , Utf8PlatformPathBuf } ;
43
46
44
47
use crate :: {
45
48
util:: {
@@ -53,21 +56,21 @@ use crate::{
53
56
/// Diff two object files. (Interactive or one-shot mode)
54
57
#[ argp( subcommand, name = "diff" ) ]
55
58
pub struct Args {
56
- #[ argp( option, short = '1' ) ]
59
+ #[ argp( option, short = '1' , from_str_fn ( platform_path ) ) ]
57
60
/// Target object file
58
- target : Option < PathBuf > ,
59
- #[ argp( option, short = '2' ) ]
61
+ target : Option < Utf8PlatformPathBuf > ,
62
+ #[ argp( option, short = '2' , from_str_fn ( platform_path ) ) ]
60
63
/// Base object file
61
- base : Option < PathBuf > ,
62
- #[ argp( option, short = 'p' ) ]
64
+ base : Option < Utf8PlatformPathBuf > ,
65
+ #[ argp( option, short = 'p' , from_str_fn ( platform_path ) ) ]
63
66
/// Project directory
64
- project : Option < PathBuf > ,
67
+ project : Option < Utf8PlatformPathBuf > ,
65
68
#[ argp( option, short = 'u' ) ]
66
69
/// Unit name within project
67
70
unit : Option < String > ,
68
- #[ argp( option, short = 'o' ) ]
71
+ #[ argp( option, short = 'o' , from_str_fn ( platform_path ) ) ]
69
72
/// Output file (one-shot mode) ("-" for stdout)
70
- output : Option < PathBuf > ,
73
+ output : Option < Utf8PlatformPathBuf > ,
71
74
#[ argp( option) ]
72
75
/// Output format (json, json-pretty, proto) (default: json)
73
76
format : Option < String > ,
@@ -89,86 +92,61 @@ pub struct Args {
89
92
}
90
93
91
94
pub fn run ( args : Args ) -> Result < ( ) > {
92
- let ( target_path, base_path, project_config) = match (
93
- & args. target ,
94
- & args. base ,
95
- & args. project ,
96
- & args. unit ,
97
- ) {
98
- ( Some ( _) , Some ( _) , None , None )
99
- | ( Some ( _) , None , None , None )
100
- | ( None , Some ( _) , None , None ) => ( args. target . clone ( ) , args. base . clone ( ) , None ) ,
101
- ( None , None , p, u) => {
102
- let project = match p {
103
- Some ( project) => project. clone ( ) ,
104
- _ => std:: env:: current_dir ( ) . context ( "Failed to get the current directory" ) ?,
105
- } ;
106
- let Some ( ( project_config, project_config_info) ) =
107
- objdiff_core:: config:: try_project_config ( & project)
108
- else {
109
- bail ! ( "Project config not found in {}" , & project. display( ) )
110
- } ;
111
- let mut project_config = project_config. with_context ( || {
112
- format ! ( "Reading project config {}" , project_config_info. path. display( ) )
113
- } ) ?;
114
- let object = {
115
- let resolve_paths = |o : & mut ProjectObject | {
116
- o. resolve_paths (
117
- & project,
118
- project_config. target_dir . as_deref ( ) ,
119
- project_config. base_dir . as_deref ( ) ,
95
+ let ( target_path, base_path, project_config) =
96
+ match ( & args. target , & args. base , & args. project , & args. unit ) {
97
+ ( Some ( _) , Some ( _) , None , None )
98
+ | ( Some ( _) , None , None , None )
99
+ | ( None , Some ( _) , None , None ) => ( args. target . clone ( ) , args. base . clone ( ) , None ) ,
100
+ ( None , None , p, u) => {
101
+ let project = match p {
102
+ Some ( project) => project. clone ( ) ,
103
+ _ => check_path_buf (
104
+ std:: env:: current_dir ( ) . context ( "Failed to get the current directory" ) ?,
120
105
)
106
+ . context ( "Current directory is not valid UTF-8" ) ?,
121
107
} ;
122
- if let Some ( u ) = u {
123
- let unit_path =
124
- PathBuf :: from_str ( u ) . ok ( ) . and_then ( |p| fs :: canonicalize ( p ) . ok ( ) ) ;
125
-
126
- let Some ( object ) = project_config
127
- . units
128
- . as_deref_mut ( )
129
- . unwrap_or_default ( )
130
- . iter_mut ( )
131
- . find_map ( |obj| {
132
- if obj . name . as_deref ( ) == Some ( u ) {
133
- resolve_paths ( obj ) ;
134
- return Some ( obj ) ;
135
- }
136
-
137
- let up = unit_path . as_deref ( ) ? ;
138
-
139
- resolve_paths ( obj ) ;
140
-
141
- if [ & obj . base_path , & obj . target_path ]
142
- . into_iter ( )
143
- . filter_map ( |p| p . as_ref ( ) . and_then ( |p| p . canonicalize ( ) . ok ( ) ) )
144
- . any ( |p| p == up )
145
- {
146
- return Some ( obj ) ;
147
- }
148
-
149
- None
150
- } )
151
- else {
152
- bail ! ( "Unit not found: {}" , u )
153
- } ;
154
-
155
- object
108
+ let Some ( ( project_config , project_config_info ) ) =
109
+ objdiff_core :: config :: try_project_config ( project . as_ref ( ) )
110
+ else {
111
+ bail ! ( "Project config not found in {}" , & project )
112
+ } ;
113
+ let project_config = project_config . with_context ( || {
114
+ format ! ( "Reading project config {}" , project_config_info . path . display ( ) )
115
+ } ) ? ;
116
+ let target_obj_dir = project_config
117
+ . target_dir
118
+ . as_ref ( )
119
+ . map ( |p| project . join ( p . with_platform_encoding ( ) ) ) ;
120
+ let base_obj_dir = project_config
121
+ . base_dir
122
+ . as_ref ( )
123
+ . map ( |p| project . join ( p . with_platform_encoding ( ) ) ) ;
124
+ let objects = project_config
125
+ . units
126
+ . iter ( )
127
+ . flatten ( )
128
+ . map ( |o| {
129
+ ObjectConfig :: new (
130
+ o ,
131
+ & project ,
132
+ target_obj_dir . as_deref ( ) ,
133
+ base_obj_dir . as_deref ( ) ,
134
+ )
135
+ } )
136
+ . collect :: < Vec < _ > > ( ) ;
137
+ let object = if let Some ( u ) = u {
138
+ objects
139
+ . iter ( )
140
+ . find ( |obj| obj . name == * u )
141
+ . ok_or_else ( || anyhow ! ( "Unit not found: {}" , u ) ) ?
156
142
} else if let Some ( symbol_name) = & args. symbol {
157
143
let mut idx = None ;
158
144
let mut count = 0usize ;
159
- for ( i, obj) in project_config
160
- . units
161
- . as_deref_mut ( )
162
- . unwrap_or_default ( )
163
- . iter_mut ( )
164
- . enumerate ( )
165
- {
166
- resolve_paths ( obj) ;
167
-
145
+ for ( i, obj) in objects. iter ( ) . enumerate ( ) {
168
146
if obj
169
147
. target_path
170
148
. as_deref ( )
171
- . map ( |o| obj:: read:: has_function ( o, symbol_name) )
149
+ . map ( |o| obj:: read:: has_function ( o. as_ref ( ) , symbol_name) )
172
150
. transpose ( ) ?
173
151
. unwrap_or ( false )
174
152
{
@@ -181,7 +159,7 @@ pub fn run(args: Args) -> Result<()> {
181
159
}
182
160
match ( count, idx) {
183
161
( 0 , None ) => bail ! ( "Symbol not found: {}" , symbol_name) ,
184
- ( 1 , Some ( i) ) => & mut project_config . units_mut ( ) [ i] ,
162
+ ( 1 , Some ( i) ) => & objects [ i] ,
185
163
( 2 .., Some ( _) ) => bail ! (
186
164
"Multiple instances of {} were found, try specifying a unit" ,
187
165
symbol_name
@@ -190,14 +168,13 @@ pub fn run(args: Args) -> Result<()> {
190
168
}
191
169
} else {
192
170
bail ! ( "Must specify one of: symbol, project and unit, target and base objects" )
193
- }
194
- } ;
195
- let target_path = object. target_path . clone ( ) ;
196
- let base_path = object. base_path . clone ( ) ;
197
- ( target_path, base_path, Some ( project_config) )
198
- }
199
- _ => bail ! ( "Either target and base or project and unit must be specified" ) ,
200
- } ;
171
+ } ;
172
+ let target_path = object. target_path . clone ( ) ;
173
+ let base_path = object. base_path . clone ( ) ;
174
+ ( target_path, base_path, Some ( project_config) )
175
+ }
176
+ _ => bail ! ( "Either target and base or project and unit must be specified" ) ,
177
+ } ;
201
178
202
179
if let Some ( output) = & args. output {
203
180
run_oneshot ( & args, output, target_path. as_deref ( ) , base_path. as_deref ( ) )
@@ -245,20 +222,20 @@ fn build_config_from_args(args: &Args) -> Result<(DiffObjConfig, MappingConfig)>
245
222
246
223
fn run_oneshot (
247
224
args : & Args ,
248
- output : & Path ,
249
- target_path : Option < & Path > ,
250
- base_path : Option < & Path > ,
225
+ output : & Utf8PlatformPath ,
226
+ target_path : Option < & Utf8PlatformPath > ,
227
+ base_path : Option < & Utf8PlatformPath > ,
251
228
) -> Result < ( ) > {
252
229
let output_format = OutputFormat :: from_option ( args. format . as_deref ( ) ) ?;
253
230
let ( diff_config, mapping_config) = build_config_from_args ( args) ?;
254
231
let target = target_path
255
232
. map ( |p| {
256
- obj:: read:: read ( p, & diff_config) . with_context ( || format ! ( "Loading {}" , p. display ( ) ) )
233
+ obj:: read:: read ( p. as_ref ( ) , & diff_config) . with_context ( || format ! ( "Loading {}" , p) )
257
234
} )
258
235
. transpose ( ) ?;
259
236
let base = base_path
260
237
. map ( |p| {
261
- obj:: read:: read ( p, & diff_config) . with_context ( || format ! ( "Loading {}" , p. display ( ) ) )
238
+ obj:: read:: read ( p. as_ref ( ) , & diff_config) . with_context ( || format ! ( "Loading {}" , p) )
262
239
} )
263
240
. transpose ( ) ?;
264
241
let result =
@@ -272,10 +249,10 @@ fn run_oneshot(
272
249
pub struct AppState {
273
250
pub jobs : JobQueue ,
274
251
pub waker : Arc < TermWaker > ,
275
- pub project_dir : Option < PathBuf > ,
252
+ pub project_dir : Option < Utf8PlatformPathBuf > ,
276
253
pub project_config : Option < ProjectConfig > ,
277
- pub target_path : Option < PathBuf > ,
278
- pub base_path : Option < PathBuf > ,
254
+ pub target_path : Option < Utf8PlatformPathBuf > ,
255
+ pub base_path : Option < Utf8PlatformPathBuf > ,
279
256
pub left_obj : Option < ( ObjInfo , ObjDiff ) > ,
280
257
pub right_obj : Option < ( ObjInfo , ObjDiff ) > ,
281
258
pub prev_obj : Option < ( ObjInfo , ObjDiff ) > ,
@@ -315,6 +292,53 @@ fn create_objdiff_config(state: &AppState) -> ObjDiffConfig {
315
292
}
316
293
}
317
294
295
+ /// The configuration for a single object file.
296
+ #[ derive( Default , Clone , serde:: Deserialize , serde:: Serialize ) ]
297
+ pub struct ObjectConfig {
298
+ pub name : String ,
299
+ #[ serde( default , with = "platform_path_serde_option" ) ]
300
+ pub target_path : Option < Utf8PlatformPathBuf > ,
301
+ #[ serde( default , with = "platform_path_serde_option" ) ]
302
+ pub base_path : Option < Utf8PlatformPathBuf > ,
303
+ pub metadata : ProjectObjectMetadata ,
304
+ pub complete : Option < bool > ,
305
+ }
306
+
307
+ impl ObjectConfig {
308
+ pub fn new (
309
+ object : & ProjectObject ,
310
+ project_dir : & Utf8PlatformPath ,
311
+ target_obj_dir : Option < & Utf8PlatformPath > ,
312
+ base_obj_dir : Option < & Utf8PlatformPath > ,
313
+ ) -> Self {
314
+ let target_path = if let ( Some ( target_obj_dir) , Some ( path) , None ) =
315
+ ( target_obj_dir, & object. path , & object. target_path )
316
+ {
317
+ Some ( target_obj_dir. join ( path. with_platform_encoding ( ) ) )
318
+ } else if let Some ( path) = & object. target_path {
319
+ Some ( project_dir. join ( path. with_platform_encoding ( ) ) )
320
+ } else {
321
+ None
322
+ } ;
323
+ let base_path = if let ( Some ( base_obj_dir) , Some ( path) , None ) =
324
+ ( base_obj_dir, & object. path , & object. base_path )
325
+ {
326
+ Some ( base_obj_dir. join ( path. with_platform_encoding ( ) ) )
327
+ } else if let Some ( path) = & object. base_path {
328
+ Some ( project_dir. join ( path. with_platform_encoding ( ) ) )
329
+ } else {
330
+ None
331
+ } ;
332
+ Self {
333
+ name : object. name ( ) . to_string ( ) ,
334
+ target_path,
335
+ base_path,
336
+ metadata : object. metadata . clone ( ) . unwrap_or_default ( ) ,
337
+ complete : object. complete ( ) ,
338
+ }
339
+ }
340
+ }
341
+
318
342
impl AppState {
319
343
fn reload ( & mut self ) -> Result < ( ) > {
320
344
let config = create_objdiff_config ( self ) ;
@@ -355,8 +379,8 @@ impl Wake for TermWaker {
355
379
356
380
fn run_interactive (
357
381
args : Args ,
358
- target_path : Option < PathBuf > ,
359
- base_path : Option < PathBuf > ,
382
+ target_path : Option < Utf8PlatformPathBuf > ,
383
+ base_path : Option < Utf8PlatformPathBuf > ,
360
384
project_config : Option < ProjectConfig > ,
361
385
) -> Result < ( ) > {
362
386
let Some ( symbol_name) = & args. symbol else { bail ! ( "Interactive mode requires a symbol name" ) } ;
@@ -384,7 +408,7 @@ fn run_interactive(
384
408
let watch_patterns = project_config. build_watch_patterns ( ) ?;
385
409
state. watcher = Some ( create_watcher (
386
410
state. modified . clone ( ) ,
387
- project_dir,
411
+ project_dir. as_ref ( ) ,
388
412
build_globset ( & watch_patterns) ?,
389
413
Waker :: from ( state. waker . clone ( ) ) ,
390
414
) ?) ;
0 commit comments