@@ -1258,8 +1258,12 @@ impl GlobalContext {
12581258 ) -> CargoResult < ( ) > {
12591259 let includes = self . include_paths ( cv, false ) ?;
12601260 for include in includes {
1261+ let Some ( abs_path) = include. resolve_path ( self ) else {
1262+ continue ;
1263+ } ;
1264+
12611265 let mut cv = self
1262- . _load_file ( & include . abs_path ( self ) , seen, false , WhyLoad :: FileDiscovery )
1266+ . _load_file ( & abs_path, seen, false , WhyLoad :: FileDiscovery )
12631267 . with_context ( || {
12641268 format ! (
12651269 "failed to load config include `{}` from `{}`" ,
@@ -1369,7 +1373,11 @@ impl GlobalContext {
13691373 // Accumulate all values here.
13701374 let mut root = CV :: Table ( HashMap :: new ( ) , value. definition ( ) . clone ( ) ) ;
13711375 for include in includes {
1372- self . _load_file ( & include. abs_path ( self ) , seen, true , why_load)
1376+ let Some ( abs_path) = include. resolve_path ( self ) else {
1377+ continue ;
1378+ } ;
1379+
1380+ self . _load_file ( & abs_path, seen, true , why_load)
13731381 . and_then ( |include| root. merge ( include, true ) )
13741382 . with_context ( || {
13751383 format ! (
@@ -1410,7 +1418,20 @@ impl GlobalContext {
14101418 ) ,
14111419 None => bail ! ( "missing field `path` at `include[{idx}]` in `{def}`" ) ,
14121420 } ;
1413- Ok ( ConfigInclude :: new ( s, def) )
1421+
1422+ // Extract optional `include.optional` field
1423+ let optional = match table. remove ( "optional" ) {
1424+ Some ( CV :: Boolean ( b, _) ) => b,
1425+ Some ( other) => bail ! (
1426+ "expected a boolean, but found {} at `include[{idx}].optional` in `{def}`" ,
1427+ other. desc( )
1428+ ) ,
1429+ None => false ,
1430+ } ;
1431+
1432+ let mut include = ConfigInclude :: new ( s, def) ;
1433+ include. optional = optional;
1434+ Ok ( include)
14141435 }
14151436 other => bail ! (
14161437 "expected a string or table, but found {} at `include[{idx}]` in {}" ,
@@ -2495,17 +2516,20 @@ struct ConfigInclude {
24952516 /// Could be either relative or absolute.
24962517 path : PathBuf ,
24972518 def : Definition ,
2519+ /// Whether this include is optional (missing files are silently ignored)
2520+ optional : bool ,
24982521}
24992522
25002523impl ConfigInclude {
25012524 fn new ( p : impl Into < PathBuf > , def : Definition ) -> Self {
25022525 Self {
25032526 path : p. into ( ) ,
25042527 def,
2528+ optional : false ,
25052529 }
25062530 }
25072531
2508- /// Gets the absolute path of the config- include config file .
2532+ /// Resolves the absolute path for this include.
25092533 ///
25102534 /// For file based include,
25112535 /// it is relative to parent directory of the config file includes it.
@@ -2514,12 +2538,27 @@ impl ConfigInclude {
25142538 ///
25152539 /// For CLI based include (e.g., `--config 'include = "foo.toml"'`),
25162540 /// it is relative to the current working directory.
2517- fn abs_path ( & self , gctx : & GlobalContext ) -> PathBuf {
2518- match & self . def {
2541+ ///
2542+ /// Returns `None` if this is an optional include and the file doesn't exist.
2543+ /// Otherwise returns `Some(PathBuf)` with the absolute path.
2544+ fn resolve_path ( & self , gctx : & GlobalContext ) -> Option < PathBuf > {
2545+ let abs_path = match & self . def {
25192546 Definition :: Path ( p) | Definition :: Cli ( Some ( p) ) => p. parent ( ) . unwrap ( ) ,
25202547 Definition :: Environment ( _) | Definition :: Cli ( None ) | Definition :: BuiltIn => gctx. cwd ( ) ,
25212548 }
2522- . join ( & self . path )
2549+ . join ( & self . path ) ;
2550+
2551+ if self . optional && !abs_path. exists ( ) {
2552+ tracing:: info!(
2553+ "skipping optional include `{}` in `{}`: file not found at `{}`" ,
2554+ self . path. display( ) ,
2555+ self . def,
2556+ abs_path. display( ) ,
2557+ ) ;
2558+ None
2559+ } else {
2560+ Some ( abs_path)
2561+ }
25232562 }
25242563}
25252564
0 commit comments