009_advanced_patterns/
009_advanced_patterns.rs

1//! # 009 - Advanced Patterns and Extensibility
2//!
3//! advanced usage patterns, extensibility, and integration with other rust ecosystem tools
4//! demonstrates `workspace_tools` as a foundation for more complex applications
5
6use workspace_tools::{ workspace, Workspace };
7use std::{ fs, collections::HashMap };
8
9fn main() -> Result< (), Box< dyn core::error::Error > >
10{
11  println!( "šŸš€ advanced workspace patterns and extensibility\n" );
12  
13  let manager = AdvancedWorkspaceManager::new()?;
14  manager.demonstrate_patterns()?;
15  manager.cleanup()?;
16  
17  println!( "\nšŸŽÆ this example demonstrates:" );
18  println!( "   • workspace plugin architecture" );
19  println!( "   • configuration overlays and environments" );
20  println!( "   • workspace templates and scaffolding" );
21  println!( "   • integration with other rust tools" );
22  println!( "   • advanced path resolution patterns" );
23  println!( "   • workspace composition and multi-workspace setups" );
24  
25  println!( "\nāœ… congratulations! you've completed all workspace_tools examples" );
26  println!( "   you now have a comprehensive understanding of workspace-relative development" );
27  println!( "   start using workspace_tools in your projects to eliminate path resolution pain!" );
28  
29  Ok( () )
30}
31
32struct AdvancedWorkspaceManager
33{
34  workspace : Workspace,
35  plugins : Vec< Box< dyn WorkspacePlugin > >,
36  environments : HashMap< String, EnvironmentConfig >,
37}
38
39trait WorkspacePlugin : Send + Sync
40{
41  fn name( &self ) -> &str;
42  fn initialize( &mut self, workspace : &Workspace ) -> Result< (), Box< dyn core::error::Error > >;
43  fn process( &self, workspace : &Workspace ) -> Result< PluginResult, Box< dyn core::error::Error > >;
44}
45
46struct PluginResult
47{
48  success : bool,
49  message : String,
50  data : HashMap< String, String >,
51}
52
53#[ derive( Clone ) ]
54struct EnvironmentConfig
55{
56  #[ allow( dead_code ) ]
57  name : String,
58  variables : HashMap< String, String >,
59  paths : HashMap< String, String >,
60  features : Vec< String >,
61}
62
63impl AdvancedWorkspaceManager
64{
65  fn new() -> Result< Self, Box< dyn core::error::Error > >
66  {
67    println!( "1ļøāƒ£  initializing advanced workspace manager..." );
68    
69    if std::env::var( "WORKSPACE_PATH" ).is_err()
70    {
71      std::env::set_var( "WORKSPACE_PATH", std::env::current_dir()? );
72    }
73    
74    let workspace = workspace()?;
75    
76    // initialize plugin system
77    let mut plugins = Self::create_plugins();
78    for plugin in &mut plugins
79    {
80      plugin.initialize( &workspace )?;
81      println!( "   initialized plugin: {}", plugin.name() );
82    }
83    
84    // setup environments
85    let environments = Self::create_environments();
86    
87    // create advanced directory structure
88    Self::setup_advanced_structure( &workspace )?;
89    
90    println!( "   āœ… advanced manager initialized with {} plugins", plugins.len() );
91    
92    Ok( Self { workspace, plugins, environments } )
93  }
94  
95  fn demonstrate_patterns( &self ) -> Result< (), Box< dyn core::error::Error > >
96  {
97    println!( "\n2ļøāƒ£  demonstrating advanced patterns:" );
98    
99    self.demonstrate_plugin_system();
100    self.demonstrate_environment_overlays()?;
101    self.demonstrate_workspace_templates()?;
102    self.demonstrate_tool_integration()?;
103    self.demonstrate_multi_workspace_composition()?;
104    
105    Ok( () )
106  }
107  
108  fn demonstrate_plugin_system( &self )
109  {
110    println!( "   šŸ”Œ plugin system demonstration:" );
111    
112    for plugin in &self.plugins
113    {
114      match plugin.process( &self.workspace )
115      {
116        Ok( result ) =>
117        {
118          println!( "     {} -> {} ({})", 
119            plugin.name(), 
120            if result.success { "āœ…" } else { "āŒ" },
121            result.message
122          );
123          
124          for ( key, value ) in result.data
125          {
126            println!( "       {key}: {value}" );
127          }
128        }
129        Err( e ) => println!( "     {} -> error: {}", plugin.name(), e ),
130      }
131    }
132  }
133  
134  fn demonstrate_environment_overlays( &self ) -> Result< (), Box< dyn core::error::Error > >
135  {
136    println!( "\n   šŸ—ļø  environment overlay system:" );
137    
138    for ( env_name, env_config ) in &self.environments
139    {
140      println!( "     environment: {env_name}" );
141      
142      // create environment-specific configuration
143      let env_dir = self.workspace.config_dir().join( "environments" ).join( env_name );
144      fs::create_dir_all( &env_dir )?;
145      
146      // base configuration
147      let base_config = format!( r#"# base configuration for {}
148debug = {}
149log_level = "{}"
150cache_enabled = {}
151"#, 
152        env_name,
153        env_name == "development",
154        env_config.variables.get( "LOG_LEVEL" ).unwrap_or( &"info".to_string() ),
155        env_name != "testing"
156      );
157      
158      fs::write( env_dir.join( "base.toml" ), base_config )?;
159      
160      // feature-specific overlays
161      for feature in &env_config.features
162      {
163        let feature_config = format!( r#"# {feature} feature configuration
164[{feature}]
165enabled = true
166config_file = "config/features/{feature}.toml"
167"# );
168        
169        fs::write( env_dir.join( format!( "{feature}.toml" ) ), feature_config )?;
170        println!( "       created overlay: {env_name}/{feature}.toml" );
171      }
172      
173      // apply environment variables
174      for ( key, value ) in &env_config.variables
175      {
176        println!( "       env {key}: {value}" );
177      }
178      
179      // resolve environment-specific paths
180      for ( path_name, path_value ) in &env_config.paths
181      {
182        let resolved_path = self.workspace.join( path_value );
183        println!( "       path {}: {}", path_name, resolved_path.display() );
184      }
185    }
186    
187    Ok( () )
188  }
189  
190  fn demonstrate_workspace_templates( &self ) -> Result< (), Box< dyn core::error::Error > >
191  {
192    println!( "\n   šŸ“‹ workspace template system:" );
193    
194    let templates = vec!
195    [
196      ( "rust-cli", Self::create_cli_template() ),
197      ( "web-service", Self::create_web_template() ),
198      ( "data-pipeline", Self::create_pipeline_template() ),
199      ( "desktop-app", Self::create_desktop_template() ),
200    ];
201    
202    let templates_dir = self.workspace.join( "templates" );
203    fs::create_dir_all( &templates_dir )?;
204    
205    for ( template_name, template_config ) in templates
206    {
207      let template_path = templates_dir.join( template_name );
208      fs::create_dir_all( &template_path )?;
209      
210      // create template metadata
211      let metadata = format!( r#"# workspace template: {}
212name = "{}"
213description = "{}"
214version = "1.0.0"
215author = "workspace_tools"
216
217[directories]
218{}
219
220[files]
221{}
222"#, 
223        template_name,
224        template_name,
225        template_config.description,
226        template_config.directories.join( "\n" ),
227        template_config.files.iter()
228          .map( | ( name, _ ) | format!( r#""{name}" = "template""# ) )
229          .collect::< Vec< _ > >()
230          .join( "\n" )
231      );
232      
233      fs::write( template_path.join( "template.toml" ), metadata )?;
234      
235      // create template files
236      let file_count = template_config.files.len();
237      for ( filename, content ) in &template_config.files
238      {
239        let file_path = template_path.join( filename );
240        if let Some( parent ) = file_path.parent()
241        {
242          fs::create_dir_all( parent )?;
243        }
244        fs::write( file_path, content )?;
245      }
246      
247      println!( "     created template: {template_name}" );
248      println!( "       directories: {}", template_config.directories.len() );
249      println!( "       files: {file_count}" );
250    }
251    
252    Ok( () )
253  }
254  
255  fn demonstrate_tool_integration( &self ) -> Result< (), Box< dyn core::error::Error > >
256  {
257    println!( "\n   šŸ”§ rust ecosystem tool integration:" );
258    
259    // cargo integration
260    let cargo_config = format!( r#"# cargo configuration with workspace_tools
261[env]
262WORKSPACE_PATH = {{ value = ".", relative = true }}
263
264[build]  
265target-dir = "{}/target"
266
267[install]
268root = "{}/bin"
269"#, 
270      self.workspace.data_dir().display(),
271      self.workspace.join( "tools" ).display()
272    );
273    
274    let cargo_dir = self.workspace.join( ".cargo" );
275    fs::create_dir_all( &cargo_dir )?;
276    fs::write( cargo_dir.join( "config.toml" ), cargo_config )?;
277    println!( "     āœ… cargo integration configured" );
278    
279    // justfile integration
280    let justfile = format!( r#"# justfile with workspace_tools integration
281# set workspace for all recipes
282export WORKSPACE_PATH := justfile_directory()
283
284# default recipe
285default:
286    @just --list
287
288# development tasks
289dev:
290    cargo run --example hello_workspace
291
292test:
293    cargo test --workspace
294
295# build tasks  
296build:
297    cargo build --release
298    
299# deployment tasks
300deploy env="staging":
301    echo "deploying to {{{{env}}}}"
302    echo "workspace: $WORKSPACE_PATH"
303    
304# cleanup tasks
305clean:
306    cargo clean
307    rm -rf {}/target
308    rm -rf {}/logs/*
309"#, 
310      self.workspace.data_dir().display(),
311      self.workspace.logs_dir().display()
312    );
313    
314    fs::write( self.workspace.join( "justfile" ), justfile )?;
315    println!( "     āœ… just integration configured" );
316    
317    // serde integration example
318    let serde_example = r#"// serde integration with workspace_tools
319use serde::{Deserialize, Serialize};
320use workspace_tools::workspace;
321
322#[derive(Serialize, Deserialize)]
323struct AppConfig {
324    name: String,
325    version: String,
326    database_url: String,
327}
328
329fn load_config() -> Result<AppConfig, Box<dyn std::error::Error>> {
330    let ws = workspace()?;
331    let config_path = ws.find_config("app")?;
332    let config_str = std::fs::read_to_string(config_path)?;
333    let config: AppConfig = toml::from_str(&config_str)?;
334    Ok(config)
335}
336"#;
337    
338    let examples_dir = self.workspace.join( "integration_examples" );
339    fs::create_dir_all( &examples_dir )?;
340    fs::write( examples_dir.join( "serde_integration.rs" ), serde_example )?;
341    println!( "     āœ… serde integration example created" );
342    
343    // tracing integration
344    let tracing_example = r#"// tracing integration with workspace_tools
345use tracing::{info, warn, error};
346use tracing_appender::rolling::{RollingFileAppender, Rotation};
347use workspace_tools::workspace;
348
349fn setup_logging() -> Result<(), Box<dyn std::error::Error>> {
350    let ws = workspace()?;
351    let log_dir = ws.logs_dir();
352    std::fs::create_dir_all(&log_dir)?;
353    
354    let file_appender = RollingFileAppender::new(
355        Rotation::DAILY,
356        log_dir,
357        "app.log"
358    );
359    
360    // configure tracing subscriber with workspace-aware file output
361    // tracing_subscriber setup would go here...
362    
363    info!("logging initialized with workspace: {}", ws.root().display());
364    Ok(())
365}
366"#;
367    
368    fs::write( examples_dir.join( "tracing_integration.rs" ), tracing_example )?;
369    println!( "     āœ… tracing integration example created" );
370    
371    Ok( () )
372  }
373  
374  fn demonstrate_multi_workspace_composition( &self ) -> Result< (), Box< dyn core::error::Error > >
375  {
376    println!( "\n   šŸ—ļø  multi-workspace composition:" );
377    
378    // create sub-workspaces for different components
379    let sub_workspaces = vec!
380    [
381      ( "frontend", "web frontend components" ),
382      ( "backend", "api and business logic" ),
383      ( "shared", "shared libraries and utilities" ),
384      ( "tools", "development and deployment tools" ),
385    ];
386    
387    for ( workspace_name, description ) in sub_workspaces
388    {
389      let sub_ws_dir = self.workspace.join( "workspaces" ).join( workspace_name );
390      fs::create_dir_all( &sub_ws_dir )?;
391      
392      // create sub-workspace cargo configuration
393      let sub_cargo_dir = sub_ws_dir.join( ".cargo" );
394      fs::create_dir_all( &sub_cargo_dir )?;
395      
396      let sub_cargo_config = r#"[env]
397WORKSPACE_PATH = { value = ".", relative = true }
398PARENT_WORKSPACE = { value = "../..", relative = true }
399
400[alias]
401parent-test = "test --manifest-path ../../Cargo.toml"
402"#.to_string();
403      
404      fs::write( sub_cargo_dir.join( "config.toml" ), sub_cargo_config )?;
405      
406      // create workspace composition manifest
407      let composition_manifest = format!( r#"# workspace composition manifest
408name = "{workspace_name}"
409description = "{description}"
410parent_workspace = "../.."
411
412[dependencies.internal]
413shared = {{ path = "../shared" }}
414
415[dependencies.external]
416# external dependencies specific to this workspace
417
418[directories]
419config = "config"
420data = "data" 
421logs = "logs"
422src = "src"
423
424[integration]
425parent_config = true
426parent_secrets = true
427isolated_data = true
428"# );
429      
430      fs::write( sub_ws_dir.join( "workspace.toml" ), composition_manifest )?;
431      
432      // create standard structure for sub-workspace
433      for dir in &[ "config", "data", "logs", "src" ]
434      {
435        fs::create_dir_all( sub_ws_dir.join( dir ) )?;
436      }
437      
438      println!( "     created sub-workspace: {workspace_name} ({description})" );
439    }
440    
441    // create workspace orchestration script
442    let orchestration_script = r#"#!/bin/bash
443# workspace orchestration script
444set -e
445
446PARENT_WS="$WORKSPACE_PATH"
447echo "orchestrating multi-workspace build..."
448echo "parent workspace: $PARENT_WS"
449
450# build shared components first
451echo "building shared workspace..."
452cd workspaces/shared
453export WORKSPACE_PATH="$(pwd)"
454cargo build
455
456# build backend
457echo "building backend workspace..."
458cd ../backend
459export WORKSPACE_PATH="$(pwd)"
460cargo build
461
462# build frontend  
463echo "building frontend workspace..."
464cd ../frontend
465export WORKSPACE_PATH="$(pwd)"
466cargo build
467
468# build tools
469echo "building tools workspace..."
470cd ../tools
471export WORKSPACE_PATH="$(pwd)"
472cargo build
473
474echo "multi-workspace build completed!"
475"#;
476    
477    let scripts_dir = self.workspace.join( "scripts" );
478    fs::create_dir_all( &scripts_dir )?;
479    fs::write( scripts_dir.join( "build-all.sh" ), orchestration_script )?;
480    println!( "     āœ… orchestration script created" );
481    
482    Ok( () )
483  }
484  
485  fn cleanup( &self ) -> Result< (), Box< dyn core::error::Error > >
486  {
487    println!( "\n3ļøāƒ£  cleaning up advanced demo..." );
488    
489    let cleanup_dirs = vec!
490    [
491      "templates", "workspaces", "scripts", "integration_examples", 
492      "tools", "bin", "target", ".cargo"
493    ];
494    
495    for dir_name in cleanup_dirs
496    {
497      let dir_path = self.workspace.join( dir_name );
498      if dir_path.exists()
499      {
500        fs::remove_dir_all( &dir_path )?;
501        println!( "   removed: {}", dir_path.display() );
502      }
503    }
504    
505    let cleanup_files = vec![ "justfile" ];
506    for file_name in cleanup_files
507    {
508      let file_path = self.workspace.join( file_name );
509      if file_path.exists()
510      {
511        fs::remove_file( &file_path )?;
512        println!( "   removed: {}", file_path.display() );
513      }
514    }
515    
516    // clean up config directories
517    let config_cleanup = vec![ "environments", "features" ];
518    for dir_name in config_cleanup
519    {
520      let dir_path = self.workspace.config_dir().join( dir_name );
521      if dir_path.exists()
522      {
523        fs::remove_dir_all( &dir_path )?;
524        println!( "   removed: {}", dir_path.display() );
525      }
526    }
527    
528    println!( "   āœ… cleanup completed" );
529    
530    Ok( () )
531  }
532  
533  // factory methods
534  
535  fn create_plugins() -> Vec< Box< dyn WorkspacePlugin > >
536  {
537    vec!
538    [
539      Box::new( ConfigValidatorPlugin::new() ),
540      Box::new( AssetOptimizerPlugin::new() ),
541      Box::new( SecurityScannerPlugin::new() ),
542      Box::new( DocumentationGeneratorPlugin::new() ),
543    ]
544  }
545  
546  fn create_environments() -> HashMap< String, EnvironmentConfig >
547  {
548    let mut environments = HashMap::new();
549    
550    // development environment
551    let mut dev_vars = HashMap::new();
552    dev_vars.insert( "LOG_LEVEL".to_string(), "debug".to_string() );
553    dev_vars.insert( "DEBUG".to_string(), "true".to_string() );
554    
555    let mut dev_paths = HashMap::new();
556    dev_paths.insert( "temp".to_string(), "data/dev_temp".to_string() );
557    dev_paths.insert( "cache".to_string(), "data/dev_cache".to_string() );
558    
559    environments.insert( "development".to_string(), EnvironmentConfig
560    {
561      name : "development".to_string(),
562      variables : dev_vars,
563      paths : dev_paths,
564      features : vec![ "hot_reload".to_string(), "debug_ui".to_string() ],
565    } );
566    
567    // production environment
568    let mut prod_vars = HashMap::new();
569    prod_vars.insert( "LOG_LEVEL".to_string(), "info".to_string() );
570    prod_vars.insert( "DEBUG".to_string(), "false".to_string() );
571    
572    let mut prod_paths = HashMap::new();
573    prod_paths.insert( "temp".to_string(), "data/temp".to_string() );
574    prod_paths.insert( "cache".to_string(), "data/cache".to_string() );
575    
576    environments.insert( "production".to_string(), EnvironmentConfig
577    {
578      name : "production".to_string(),
579      variables : prod_vars,
580      paths : prod_paths,  
581      features : vec![ "metrics".to_string(), "monitoring".to_string() ],
582    } );
583    
584    environments
585  }
586  
587  fn setup_advanced_structure( ws : &Workspace ) -> Result< (), Box< dyn core::error::Error > >
588  {
589    let advanced_dirs = vec!
590    [
591      "plugins", "templates", "environments", "scripts", "integration_examples",
592      "config/environments", "config/features", "config/plugins",
593      "data/plugins", "logs/plugins",
594    ];
595    
596    for dir in advanced_dirs
597    {
598      let dir_path = ws.join( dir );
599      fs::create_dir_all( dir_path )?;
600    }
601    
602    Ok( () )
603  }
604  
605  fn create_cli_template() -> WorkspaceTemplate
606  {
607    WorkspaceTemplate
608    {
609      description : "command-line interface application".to_string(),
610      directories : vec!
611      [
612        "src".to_string(), "tests".to_string(), "config".to_string(),
613        "data".to_string(), "logs".to_string(), "docs".to_string()
614      ],
615      files : vec!
616      [
617        ( "src/main.rs".to_string(), "// cli application main".to_string() ),
618        ( "src/cli.rs".to_string(), "// command line interface".to_string() ),
619        ( "config/app.toml".to_string(), "# cli configuration".to_string() ),
620        ( "Cargo.toml".to_string(), "# cargo manifest".to_string() ),
621      ],
622    }
623  }
624  
625  fn create_web_template() -> WorkspaceTemplate
626  {
627    WorkspaceTemplate
628    {
629      description : "web service application".to_string(),
630      directories : vec!
631      [
632        "src".to_string(), "templates".to_string(), "static".to_string(),
633        "uploads".to_string(), "config".to_string(), "data".to_string()
634      ],
635      files : vec!
636      [
637        ( "src/main.rs".to_string(), "// web service main".to_string() ),
638        ( "src/handlers.rs".to_string(), "// request handlers".to_string() ),
639        ( "templates/base.html".to_string(), "<!-- base template -->".to_string() ),
640        ( "static/css/main.css".to_string(), "/* main styles */".to_string() ),
641      ],
642    }
643  }
644  
645  fn create_pipeline_template() -> WorkspaceTemplate
646  {
647    WorkspaceTemplate
648    {
649      description : "data processing pipeline".to_string(),
650      directories : vec!
651      [
652        "src".to_string(), "pipelines".to_string(), "data/input".to_string(),
653        "data/output".to_string(), "data/temp".to_string(), "config".to_string()
654      ],
655      files : vec!
656      [
657        ( "src/main.rs".to_string(), "// pipeline runner".to_string() ),
658        ( "src/processors.rs".to_string(), "// data processors".to_string() ),
659        ( "pipelines/etl.toml".to_string(), "# etl pipeline config".to_string() ),
660      ],
661    }
662  }
663  
664  fn create_desktop_template() -> WorkspaceTemplate
665  {
666    WorkspaceTemplate
667    {
668      description : "desktop gui application".to_string(),
669      directories : vec!
670      [
671        "src".to_string(), "assets".to_string(), "resources".to_string(),
672        "config".to_string(), "data".to_string(), "plugins".to_string()
673      ],
674      files : vec!
675      [
676        ( "src/main.rs".to_string(), "// desktop app main".to_string() ),
677        ( "src/ui.rs".to_string(), "// user interface".to_string() ),
678        ( "assets/icon.png".to_string(), "// app icon data".to_string() ),
679      ],
680    }
681  }
682}
683
684struct WorkspaceTemplate
685{
686  description : String,
687  directories : Vec< String >,
688  files : Vec< ( String, String ) >,
689}
690
691// plugin implementations
692
693struct ConfigValidatorPlugin
694{
695  initialized : bool,
696}
697
698impl ConfigValidatorPlugin
699{
700  fn new() -> Self
701  {
702    Self { initialized : false }
703  }
704}
705
706impl WorkspacePlugin for ConfigValidatorPlugin
707{
708  fn name( &self ) -> &'static str { "config-validator" }
709  
710  fn initialize( &mut self, _workspace : &Workspace ) -> Result< (), Box< dyn core::error::Error > >
711  {
712    self.initialized = true;
713    Ok( () )
714  }
715  
716  fn process( &self, workspace : &Workspace ) -> Result< PluginResult, Box< dyn core::error::Error > >
717  {
718    let config_dir = workspace.config_dir();
719    let config_count = if config_dir.exists()
720    {
721      fs::read_dir( &config_dir )?.count()
722    }
723    else { 0 };
724    
725    let mut data = HashMap::new();
726    data.insert( "config_files".to_string(), config_count.to_string() );
727    data.insert( "config_dir".to_string(), config_dir.display().to_string() );
728    
729    Ok( PluginResult
730    {
731      success : config_count > 0,
732      message : format!( "found {config_count} config files" ),
733      data,
734    } )
735  }
736}
737
738struct AssetOptimizerPlugin;
739impl AssetOptimizerPlugin { fn new() -> Self { Self } }
740impl WorkspacePlugin for AssetOptimizerPlugin
741{
742  fn name( &self ) -> &'static str { "asset-optimizer" }
743  fn initialize( &mut self, _workspace : &Workspace ) -> Result< (), Box< dyn core::error::Error > > { Ok( () ) }
744  fn process( &self, workspace : &Workspace ) -> Result< PluginResult, Box< dyn core::error::Error > >
745  {
746    let static_dir = workspace.join( "static" );
747    let asset_count = if static_dir.exists() { fs::read_dir( static_dir )?.count() } else { 0 };
748    
749    let mut data = HashMap::new();
750    data.insert( "assets_found".to_string(), asset_count.to_string() );
751    
752    Ok( PluginResult
753    {
754      success : true,
755      message : format!( "optimized {asset_count} assets" ),
756      data,
757    } )
758  }
759}
760
761struct SecurityScannerPlugin;
762impl SecurityScannerPlugin { fn new() -> Self { Self } }
763impl WorkspacePlugin for SecurityScannerPlugin
764{
765  fn name( &self ) -> &'static str { "security-scanner" }  
766  fn initialize( &mut self, _workspace : &Workspace ) -> Result< (), Box< dyn core::error::Error > > { Ok( () ) }
767  fn process( &self, workspace : &Workspace ) -> Result< PluginResult, Box< dyn core::error::Error > >
768  {
769    let mut issues = 0;
770    let mut data = HashMap::new();
771    
772    // simulate security checks
773    #[ cfg( feature = "secrets" ) ]
774    {
775      let secret_dir = workspace.secret_dir();
776      if secret_dir.exists()
777      {
778        // check permissions, etc.
779        data.insert( "secret_dir_secure".to_string(), "true".to_string() );
780      }
781      else
782      {
783        issues += 1;
784        data.insert( "secret_dir_missing".to_string(), "true".to_string() );
785      }
786    }
787    
788    data.insert( "security_issues".to_string(), issues.to_string() );
789    
790    Ok( PluginResult
791    {
792      success : issues == 0,
793      message : format!( "security scan: {issues} issues found" ),
794      data,
795    } )
796  }
797}
798
799struct DocumentationGeneratorPlugin;
800impl DocumentationGeneratorPlugin { fn new() -> Self { Self } }
801impl WorkspacePlugin for DocumentationGeneratorPlugin
802{
803  fn name( &self ) -> &'static str { "doc-generator" }
804  fn initialize( &mut self, _workspace : &Workspace ) -> Result< (), Box< dyn core::error::Error > > { Ok( () ) }
805  fn process( &self, workspace : &Workspace ) -> Result< PluginResult, Box< dyn core::error::Error > >
806  {
807    let docs_dir = workspace.docs_dir();
808    fs::create_dir_all( &docs_dir )?;
809    
810    // generate workspace documentation
811    let workspace_doc = format!( r"# workspace documentation
812
813generated by workspace_tools documentation plugin
814
815## workspace information
816- root: {}
817- config: {}  
818- data: {}
819- logs: {}
820
821## structure
822this workspace follows the standard workspace_tools layout for consistent development.
823", 
824      workspace.root().display(),
825      workspace.config_dir().display(), 
826      workspace.data_dir().display(),
827      workspace.logs_dir().display()
828    );
829    
830    fs::write( docs_dir.join( "workspace.md" ), workspace_doc )?;
831    
832    let mut data = HashMap::new();
833    data.insert( "docs_generated".to_string(), "1".to_string() );
834    data.insert( "docs_path".to_string(), docs_dir.display().to_string() );
835    
836    Ok( PluginResult
837    {
838      success : true,
839      message : "generated workspace documentation".to_string(),
840      data,
841    } )
842  }
843}