@@ -12,7 +12,7 @@ use std::{
1212 time:: Duration ,
1313} ;
1414
15- use anyhow:: { bail, Context , Result } ;
15+ use anyhow:: { anyhow , bail, Context , Result } ;
1616use argp:: FromArgs ;
1717use crossterm:: {
1818 event,
@@ -27,9 +27,11 @@ use objdiff_core::{
2727 watcher:: { create_watcher, Watcher } ,
2828 BuildConfig ,
2929 } ,
30- config:: { build_globset, default_watch_patterns , ProjectConfig , ProjectObject } ,
30+ config:: { build_globset, ProjectConfig , ProjectObject } ,
3131 diff,
32- diff:: ObjDiff ,
32+ diff:: {
33+ ConfigEnum , ConfigPropertyId , ConfigPropertyKind , DiffObjConfig , MappingConfig , ObjDiff ,
34+ } ,
3335 jobs:: {
3436 objdiff:: { start_build, ObjDiffConfig } ,
3537 Job , JobQueue , JobResult ,
@@ -63,9 +65,6 @@ pub struct Args {
6365 #[ argp( option, short = 'u' ) ]
6466 /// Unit name within project
6567 unit : Option < String > ,
66- #[ argp( switch, short = 'x' ) ]
67- /// Relax relocation diffs
68- relax_reloc_diffs : bool ,
6968 #[ argp( option, short = 'o' ) ]
7069 /// Output file (one-shot mode) ("-" for stdout)
7170 output : Option < PathBuf > ,
@@ -75,6 +74,18 @@ pub struct Args {
7574 #[ argp( positional) ]
7675 /// Function symbol to diff
7776 symbol : Option < String > ,
77+ #[ argp( option, short = 'c' ) ]
78+ /// Configuration property (key=value)
79+ config : Vec < String > ,
80+ #[ argp( option, short = 'm' ) ]
81+ /// Symbol mapping (target=base)
82+ mapping : Vec < String > ,
83+ #[ argp( option) ]
84+ /// Left symbol name for selection
85+ selecting_left : Option < String > ,
86+ #[ argp( option) ]
87+ /// Right symbol name for selection
88+ selecting_right : Option < String > ,
7889}
7990
8091pub fn run ( args : Args ) -> Result < ( ) > {
@@ -84,7 +95,9 @@ pub fn run(args: Args) -> Result<()> {
8495 & args. project ,
8596 & args. unit ,
8697 ) {
87- ( Some ( t) , Some ( b) , None , None ) => ( Some ( t. clone ( ) ) , Some ( b. clone ( ) ) , None ) ,
98+ ( Some ( _) , Some ( _) , None , None )
99+ | ( Some ( _) , None , None , None )
100+ | ( None , Some ( _) , None , None ) => ( args. target . clone ( ) , args. base . clone ( ) , None ) ,
88101 ( None , None , p, u) => {
89102 let project = match p {
90103 Some ( project) => project. clone ( ) ,
@@ -193,24 +206,63 @@ pub fn run(args: Args) -> Result<()> {
193206 }
194207}
195208
209+ fn build_config_from_args ( args : & Args ) -> Result < ( DiffObjConfig , MappingConfig ) > {
210+ let mut diff_config = DiffObjConfig :: default ( ) ;
211+ for config in & args. config {
212+ let ( key, value) = config. split_once ( '=' ) . context ( "--config expects \" key=value\" " ) ?;
213+ let property_id = ConfigPropertyId :: from_str ( key)
214+ . map_err ( |( ) | anyhow ! ( "Invalid configuration property: {}" , key) ) ?;
215+ diff_config. set_property_value_str ( property_id, value) . map_err ( |( ) | {
216+ let mut options = String :: new ( ) ;
217+ match property_id. kind ( ) {
218+ ConfigPropertyKind :: Boolean => {
219+ options = "true, false" . to_string ( ) ;
220+ }
221+ ConfigPropertyKind :: Choice ( variants) => {
222+ for ( i, variant) in variants. iter ( ) . enumerate ( ) {
223+ if i > 0 {
224+ options. push_str ( ", " ) ;
225+ }
226+ options. push_str ( variant. value ) ;
227+ }
228+ }
229+ }
230+ anyhow ! ( "Invalid value for {}. Expected one of: {}" , property_id. name( ) , options)
231+ } ) ?;
232+ }
233+ let mut mapping_config = MappingConfig {
234+ mappings : Default :: default ( ) ,
235+ selecting_left : args. selecting_left . clone ( ) ,
236+ selecting_right : args. selecting_right . clone ( ) ,
237+ } ;
238+ for mapping in & args. mapping {
239+ let ( target, base) =
240+ mapping. split_once ( '=' ) . context ( "--mapping expects \" target=base\" " ) ?;
241+ mapping_config. mappings . insert ( target. to_string ( ) , base. to_string ( ) ) ;
242+ }
243+ Ok ( ( diff_config, mapping_config) )
244+ }
245+
196246fn run_oneshot (
197247 args : & Args ,
198248 output : & Path ,
199249 target_path : Option < & Path > ,
200250 base_path : Option < & Path > ,
201251) -> Result < ( ) > {
202252 let output_format = OutputFormat :: from_option ( args. format . as_deref ( ) ) ?;
203- let config = diff:: DiffObjConfig {
204- relax_reloc_diffs : args. relax_reloc_diffs ,
205- ..Default :: default ( ) // TODO
206- } ;
253+ let ( diff_config, mapping_config) = build_config_from_args ( args) ?;
207254 let target = target_path
208- . map ( |p| obj:: read:: read ( p, & config) . with_context ( || format ! ( "Loading {}" , p. display( ) ) ) )
255+ . map ( |p| {
256+ obj:: read:: read ( p, & diff_config) . with_context ( || format ! ( "Loading {}" , p. display( ) ) )
257+ } )
209258 . transpose ( ) ?;
210259 let base = base_path
211- . map ( |p| obj:: read:: read ( p, & config) . with_context ( || format ! ( "Loading {}" , p. display( ) ) ) )
260+ . map ( |p| {
261+ obj:: read:: read ( p, & diff_config) . with_context ( || format ! ( "Loading {}" , p. display( ) ) )
262+ } )
212263 . transpose ( ) ?;
213- let result = diff:: diff_objs ( & config, target. as_ref ( ) , base. as_ref ( ) , None ) ?;
264+ let result =
265+ diff:: diff_objs ( & diff_config, & mapping_config, target. as_ref ( ) , base. as_ref ( ) , None ) ?;
214266 let left = target. as_ref ( ) . and_then ( |o| result. left . as_ref ( ) . map ( |d| ( o, d) ) ) ;
215267 let right = base. as_ref ( ) . and_then ( |o| result. right . as_ref ( ) . map ( |d| ( o, d) ) ) ;
216268 write_output ( & DiffResult :: new ( left, right) , Some ( output) , output_format) ?;
@@ -229,9 +281,10 @@ pub struct AppState {
229281 pub prev_obj : Option < ( ObjInfo , ObjDiff ) > ,
230282 pub reload_time : Option < time:: OffsetDateTime > ,
231283 pub time_format : Vec < time:: format_description:: FormatItem < ' static > > ,
232- pub relax_reloc_diffs : bool ,
233284 pub watcher : Option < Watcher > ,
234285 pub modified : Arc < AtomicBool > ,
286+ pub diff_obj_config : DiffObjConfig ,
287+ pub mapping_config : MappingConfig ,
235288}
236289
237290fn create_objdiff_config ( state : & AppState ) -> ObjDiffConfig {
@@ -257,13 +310,8 @@ fn create_objdiff_config(state: &AppState) -> ObjDiffConfig {
257310 . is_some_and ( |p| p. build_target . unwrap_or ( false ) ) ,
258311 target_path : state. target_path . clone ( ) ,
259312 base_path : state. base_path . clone ( ) ,
260- diff_obj_config : diff:: DiffObjConfig {
261- relax_reloc_diffs : state. relax_reloc_diffs ,
262- ..Default :: default ( ) // TODO
263- } ,
264- symbol_mappings : Default :: default ( ) ,
265- selecting_left : None ,
266- selecting_right : None ,
313+ diff_obj_config : state. diff_obj_config . clone ( ) ,
314+ mapping_config : state. mapping_config . clone ( ) ,
267315 }
268316}
269317
@@ -314,6 +362,7 @@ fn run_interactive(
314362 let Some ( symbol_name) = & args. symbol else { bail ! ( "Interactive mode requires a symbol name" ) } ;
315363 let time_format = time:: format_description:: parse_borrowed :: < 2 > ( "[hour]:[minute]:[second]" )
316364 . context ( "Failed to parse time format" ) ?;
365+ let ( diff_obj_config, mapping_config) = build_config_from_args ( & args) ?;
317366 let mut state = AppState {
318367 jobs : Default :: default ( ) ,
319368 waker : Default :: default ( ) ,
@@ -326,17 +375,13 @@ fn run_interactive(
326375 prev_obj : None ,
327376 reload_time : None ,
328377 time_format,
329- relax_reloc_diffs : args. relax_reloc_diffs ,
330378 watcher : None ,
331379 modified : Default :: default ( ) ,
380+ diff_obj_config,
381+ mapping_config,
332382 } ;
333- if let Some ( project_dir) = & state. project_dir {
334- let watch_patterns = state
335- . project_config
336- . as_ref ( )
337- . and_then ( |c| c. watch_patterns . as_ref ( ) )
338- . cloned ( )
339- . unwrap_or_else ( default_watch_patterns) ;
383+ if let ( Some ( project_dir) , Some ( project_config) ) = ( & state. project_dir , & state. project_config ) {
384+ let watch_patterns = project_config. build_watch_patterns ( ) ?;
340385 state. watcher = Some ( create_watcher (
341386 state. modified . clone ( ) ,
342387 project_dir,
0 commit comments