11use std:: {
2- fs,
32 io:: stdout,
43 mem,
5- path:: { Path , PathBuf } ,
64 str:: FromStr ,
75 sync:: {
86 atomic:: { AtomicBool , Ordering } ,
@@ -27,7 +25,11 @@ use objdiff_core::{
2725 watcher:: { create_watcher, Watcher } ,
2826 BuildConfig ,
2927 } ,
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+ } ,
3133 diff,
3234 diff:: {
3335 ConfigEnum , ConfigPropertyId , ConfigPropertyKind , DiffObjConfig , MappingConfig , ObjDiff ,
@@ -40,6 +42,7 @@ use objdiff_core::{
4042 obj:: ObjInfo ,
4143} ;
4244use ratatui:: prelude:: * ;
45+ use typed_path:: { Utf8PlatformPath , Utf8PlatformPathBuf } ;
4346
4447use crate :: {
4548 util:: {
@@ -53,21 +56,21 @@ use crate::{
5356/// Diff two object files. (Interactive or one-shot mode)
5457#[ argp( subcommand, name = "diff" ) ]
5558pub struct Args {
56- #[ argp( option, short = '1' ) ]
59+ #[ argp( option, short = '1' , from_str_fn ( platform_path ) ) ]
5760 /// 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 ) ) ]
6063 /// 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 ) ) ]
6366 /// Project directory
64- project : Option < PathBuf > ,
67+ project : Option < Utf8PlatformPathBuf > ,
6568 #[ argp( option, short = 'u' ) ]
6669 /// Unit name within project
6770 unit : Option < String > ,
68- #[ argp( option, short = 'o' ) ]
71+ #[ argp( option, short = 'o' , from_str_fn ( platform_path ) ) ]
6972 /// Output file (one-shot mode) ("-" for stdout)
70- output : Option < PathBuf > ,
73+ output : Option < Utf8PlatformPathBuf > ,
7174 #[ argp( option) ]
7275 /// Output format (json, json-pretty, proto) (default: json)
7376 format : Option < String > ,
@@ -89,86 +92,61 @@ pub struct Args {
8992}
9093
9194pub 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" ) ?,
120105 )
106+ . context ( "Current directory is not valid UTF-8" ) ?,
121107 } ;
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 ) ) ?
156142 } else if let Some ( symbol_name) = & args. symbol {
157143 let mut idx = None ;
158144 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 ( ) {
168146 if obj
169147 . target_path
170148 . as_deref ( )
171- . map ( |o| obj:: read:: has_function ( o, symbol_name) )
149+ . map ( |o| obj:: read:: has_function ( o. as_ref ( ) , symbol_name) )
172150 . transpose ( ) ?
173151 . unwrap_or ( false )
174152 {
@@ -181,7 +159,7 @@ pub fn run(args: Args) -> Result<()> {
181159 }
182160 match ( count, idx) {
183161 ( 0 , None ) => bail ! ( "Symbol not found: {}" , symbol_name) ,
184- ( 1 , Some ( i) ) => & mut project_config . units_mut ( ) [ i] ,
162+ ( 1 , Some ( i) ) => & objects [ i] ,
185163 ( 2 .., Some ( _) ) => bail ! (
186164 "Multiple instances of {} were found, try specifying a unit" ,
187165 symbol_name
@@ -190,14 +168,13 @@ pub fn run(args: Args) -> Result<()> {
190168 }
191169 } else {
192170 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+ } ;
201178
202179 if let Some ( output) = & args. output {
203180 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)>
245222
246223fn run_oneshot (
247224 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 > ,
251228) -> Result < ( ) > {
252229 let output_format = OutputFormat :: from_option ( args. format . as_deref ( ) ) ?;
253230 let ( diff_config, mapping_config) = build_config_from_args ( args) ?;
254231 let target = target_path
255232 . 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) )
257234 } )
258235 . transpose ( ) ?;
259236 let base = base_path
260237 . 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) )
262239 } )
263240 . transpose ( ) ?;
264241 let result =
@@ -272,10 +249,10 @@ fn run_oneshot(
272249pub struct AppState {
273250 pub jobs : JobQueue ,
274251 pub waker : Arc < TermWaker > ,
275- pub project_dir : Option < PathBuf > ,
252+ pub project_dir : Option < Utf8PlatformPathBuf > ,
276253 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 > ,
279256 pub left_obj : Option < ( ObjInfo , ObjDiff ) > ,
280257 pub right_obj : Option < ( ObjInfo , ObjDiff ) > ,
281258 pub prev_obj : Option < ( ObjInfo , ObjDiff ) > ,
@@ -315,6 +292,53 @@ fn create_objdiff_config(state: &AppState) -> ObjDiffConfig {
315292 }
316293}
317294
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+
318342impl AppState {
319343 fn reload ( & mut self ) -> Result < ( ) > {
320344 let config = create_objdiff_config ( self ) ;
@@ -355,8 +379,8 @@ impl Wake for TermWaker {
355379
356380fn run_interactive (
357381 args : Args ,
358- target_path : Option < PathBuf > ,
359- base_path : Option < PathBuf > ,
382+ target_path : Option < Utf8PlatformPathBuf > ,
383+ base_path : Option < Utf8PlatformPathBuf > ,
360384 project_config : Option < ProjectConfig > ,
361385) -> Result < ( ) > {
362386 let Some ( symbol_name) = & args. symbol else { bail ! ( "Interactive mode requires a symbol name" ) } ;
@@ -384,7 +408,7 @@ fn run_interactive(
384408 let watch_patterns = project_config. build_watch_patterns ( ) ?;
385409 state. watcher = Some ( create_watcher (
386410 state. modified . clone ( ) ,
387- project_dir,
411+ project_dir. as_ref ( ) ,
388412 build_globset ( & watch_patterns) ?,
389413 Waker :: from ( state. waker . clone ( ) ) ,
390414 ) ?) ;
0 commit comments