Skip to content

Commit 1b013bc

Browse files
committed
refactor: Implement pack and materialize commands
- Add complete pack command for portable archive creation - Add complete materialize command for template rendering - Add comprehensive test coverage for both commands - Document architectural debt in state management - Remove obsolete file_old.rs handler - Add validation_error and state_error helpers - Update spec with implementation notes on state workaround
1 parent 2cab89a commit 1b013bc

File tree

13 files changed

+1302
-20
lines changed

13 files changed

+1302
-20
lines changed

module/core/genfile/readme.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# genfile
2+
3+
[![Crates.io](https://img.shields.io/crates/v/genfile.svg)](https://crates.io/crates/genfile)
4+
[![docs.rs](https://docs.rs/genfile/badge.svg)](https://docs.rs/genfile)
5+
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6+
7+
CLI for template archive management - create, manage, and materialize code generation templates.
8+
9+
## Quick Start
10+
11+
Install via cargo:
12+
13+
```bash
14+
cargo install genfile
15+
```
16+
17+
Create a portable template archive from a directory:
18+
19+
```bash
20+
# Pack directory into archive
21+
genfile .pack input::"./my-template" output::"template.json"
22+
23+
# Load and materialize
24+
genfile .archive.load path::"template.json"
25+
genfile .value.set name::"project_name" value::"my-project"
26+
genfile .materialize destination::"./output"
27+
```
28+
29+
## Features
30+
31+
- **Archive Management** - Create, load, save template archives
32+
- **File Operations** - Add, remove, list template files
33+
- **Parameter System** - Define and manage template parameters
34+
- **Value Management** - Set parameter values for materialization
35+
- **Content Control** - Inline or reference-based file storage
36+
- **REPL Mode** - Interactive command-line interface
37+
- **Dual Mode** - Works as single command or interactive REPL
38+
39+
## Interactive REPL
40+
41+
Run without arguments for interactive mode:
42+
43+
```bash
44+
genfile
45+
```
46+
47+
```
48+
genfile REPL v0.1.0
49+
Type '.help' for help, 'exit' to quit
50+
51+
genfile[0]> .archive.new name::"api-scaffold"
52+
Created archive: api-scaffold
53+
54+
genfile[1]> .file.add path::"main.rs" content::"fn main() {}"
55+
Added file: main.rs
56+
57+
genfile[2]> .archive.save path::"api.json"
58+
Saved archive to: api.json
59+
```
60+
61+
## Documentation
62+
63+
- [API Documentation](https://docs.rs/genfile) - Complete API reference
64+
- [Specification](spec.md) - Detailed architecture and design
65+
- [Examples](https://github.com/Wandalen/wTools/tree/master/module/core/genfile/examples) - Usage examples
66+
67+
## Architecture
68+
69+
genfile is built on:
70+
- **genfile_core** - Core template archive library
71+
- **unilang** - Universal CLI framework with REPL support
72+
- **error_tools** - Structured error handling
73+
74+
## Commands Overview
75+
76+
| Category | Commands |
77+
|----------|----------|
78+
| Archive | `.archive.new`, `.archive.load`, `.archive.save`, `.archive.from_directory` |
79+
| Files | `.file.add`, `.file.remove`, `.file.list`, `.file.show` |
80+
| Parameters | `.parameter.add`, `.parameter.list`, `.parameter.remove` |
81+
| Values | `.value.set`, `.value.list`, `.value.clear` |
82+
| Content | `.content.internalize`, `.content.externalize`, `.content.list` |
83+
84+
## Development Status
85+
86+
Current version: **0.2.0**
87+
88+
- ✅ Archive lifecycle management
89+
- ✅ File operations
90+
- ✅ Parameter and value management
91+
- ✅ Content transformation
92+
- ✅ REPL mode with state persistence
93+
- 🚧 Materialization (planned)
94+
- 🚧 Analysis and introspection (planned)
95+
- 🚧 Help system (planned)
96+
97+
## Contributing
98+
99+
See [wTools](https://github.com/Wandalen/wTools) repository for contribution guidelines.
100+
101+
## License
102+
103+
MIT - see [LICENSE](LICENSE) for details.

module/core/genfile/spec.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,19 @@ struct ArchiveState {
425425
4. State cleared on exit
426426
```
427427

428+
> **⚠️ Implementation Note (v0.2.0):**
429+
>
430+
> The design above represents the intended architecture. Current implementation uses
431+
> thread-local storage (`handlers::shared_state::CURRENT_ARCHIVE`) as a workaround
432+
> because `unilang::ExecutionContext` does not yet support custom state passing.
433+
>
434+
> - `state::ArchiveState` exists but is unused (created in main.rs:32, ignored in handlers)
435+
> - Handlers use `get_current_archive()`/`set_current_archive()` from `shared_state.rs`
436+
> - See `state.rs` module docs for migration path and technical debt explanation
437+
>
438+
> This deviation violates "No global mutable state" principle and will be corrected
439+
> when ExecutionContext API evolves.
440+
428441
### Error Handling Strategy
429442

430443
**Error Flow:**
Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,102 @@
1-
//! Materialize commands
1+
//! Materialize command - Render templates to destination directory
2+
//!
3+
//! Implements FR6: Template Materialization
4+
//! Transforms template archives into actual files with parameter substitution.
25
36
use unilang::registry::CommandRegistry;
7+
use unilang::data::{ CommandDefinition, ArgumentDefinition, Kind, ArgumentAttributes };
48

59
/// Register materialize commands
6-
pub fn register( _registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
10+
#[ allow( deprecated ) ]
11+
pub fn register( registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
712
{
8-
// TODO: Implement materialize command registration
13+
register_materialize( registry )?;
14+
Ok( () )
15+
}
16+
17+
/// Register .materialize command
18+
#[ allow( deprecated ) ]
19+
fn register_materialize( registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
20+
{
21+
let cmd = CommandDefinition
22+
{
23+
name : ".materialize".to_string(),
24+
namespace : String::new(),
25+
description : "Render template archive to destination directory with parameter substitution".to_string(),
26+
hint : "Materialize templates to files".to_string(),
27+
status : "stable".to_string(),
28+
version : "0.1.0".to_string(),
29+
aliases : vec![],
30+
tags : vec![ "materialize".to_string(), "render".to_string(), "template".to_string() ],
31+
permissions : vec![],
32+
idempotent : false,
33+
deprecation_message : String::new(),
34+
http_method_hint : String::new(),
35+
examples : vec![
36+
".materialize destination::\"./output\"".to_string(),
37+
".materialize destination::\"./my-project\" verbosity::2".to_string(),
38+
".materialize destination::\"./preview\" dry::1".to_string(),
39+
],
40+
routine_link : None,
41+
auto_help_enabled : true,
42+
arguments : vec![
43+
ArgumentDefinition
44+
{
45+
name : "destination".to_string(),
46+
description : "Output directory for materialized files".to_string(),
47+
kind : Kind::Path,
48+
hint : "Destination directory path".to_string(),
49+
attributes : ArgumentAttributes
50+
{
51+
optional : false,
52+
default : None,
53+
sensitive : false,
54+
interactive : false,
55+
multiple : false,
56+
},
57+
validation_rules : vec![],
58+
aliases : vec![],
59+
tags : vec![],
60+
},
61+
ArgumentDefinition
62+
{
63+
name : "verbosity".to_string(),
64+
description : "Output verbosity level (0-5)".to_string(),
65+
kind : Kind::Integer,
66+
hint : "Verbosity level".to_string(),
67+
attributes : ArgumentAttributes
68+
{
69+
optional : true,
70+
default : Some( "1".to_string() ),
71+
sensitive : false,
72+
interactive : false,
73+
multiple : false,
74+
},
75+
validation_rules : vec![],
76+
aliases : vec![],
77+
tags : vec![],
78+
},
79+
ArgumentDefinition
80+
{
81+
name : "dry".to_string(),
82+
description : "Dry run mode (0 or 1)".to_string(),
83+
kind : Kind::Boolean,
84+
hint : "Dry run flag".to_string(),
85+
attributes : ArgumentAttributes
86+
{
87+
optional : true,
88+
default : Some( "0".to_string() ),
89+
sensitive : false,
90+
interactive : false,
91+
multiple : false,
92+
},
93+
validation_rules : vec![],
94+
aliases : vec![],
95+
tags : vec![],
96+
},
97+
],
98+
};
99+
100+
registry.command_add_runtime( &cmd, Box::new( crate::handlers::materialize::materialize_handler ) )?;
9101
Ok( () )
10102
}
Lines changed: 113 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,120 @@
1-
//! Pack commands
1+
//! Pack command - Create portable archives with inlined content
2+
//!
3+
//! Implements FR7: Archive Serialization
4+
//! Creates self-contained portable archives by internalizing all file content.
25
36
use unilang::registry::CommandRegistry;
7+
use unilang::data::{ CommandDefinition, ArgumentDefinition, Kind, ArgumentAttributes };
48

59
/// Register pack commands
6-
pub fn register( _registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
10+
#[ allow( deprecated ) ]
11+
pub fn register( registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
712
{
8-
// TODO: Implement pack command registration
13+
register_pack( registry )?;
14+
Ok( () )
15+
}
16+
17+
/// Register .pack command
18+
#[ allow( deprecated ) ]
19+
fn register_pack( registry : &mut CommandRegistry ) -> Result< (), Box< dyn core::error::Error > >
20+
{
21+
let cmd = CommandDefinition
22+
{
23+
name : ".pack".to_string(),
24+
namespace : String::new(),
25+
description : "Create portable archive from directory with inline content".to_string(),
26+
hint : "Pack directory to portable archive".to_string(),
27+
status : "stable".to_string(),
28+
version : "0.1.0".to_string(),
29+
aliases : vec![],
30+
tags : vec![ "pack".to_string(), "serialize".to_string(), "portable".to_string() ],
31+
permissions : vec![],
32+
idempotent : false,
33+
deprecation_message : String::new(),
34+
http_method_hint : String::new(),
35+
examples : vec![
36+
".pack input::\"./my-template\" output::\"template.json\"".to_string(),
37+
".pack input::\"./src\" output::\"backup.yaml\" verbosity::2".to_string(),
38+
".pack input::\"./templates\" output::\"archive.json\" dry::1".to_string(),
39+
],
40+
routine_link : None,
41+
auto_help_enabled : true,
42+
arguments : vec![
43+
ArgumentDefinition
44+
{
45+
name : "input".to_string(),
46+
description : "Source directory to pack".to_string(),
47+
kind : Kind::Directory,
48+
hint : "Input directory path".to_string(),
49+
attributes : ArgumentAttributes
50+
{
51+
optional : false,
52+
default : None,
53+
sensitive : false,
54+
interactive : false,
55+
multiple : false,
56+
},
57+
validation_rules : vec![],
58+
aliases : vec![],
59+
tags : vec![],
60+
},
61+
ArgumentDefinition
62+
{
63+
name : "output".to_string(),
64+
description : "Output file path (JSON or YAML)".to_string(),
65+
kind : Kind::Path,
66+
hint : "Output file path".to_string(),
67+
attributes : ArgumentAttributes
68+
{
69+
optional : false,
70+
default : None,
71+
sensitive : false,
72+
interactive : false,
73+
multiple : false,
74+
},
75+
validation_rules : vec![],
76+
aliases : vec![],
77+
tags : vec![],
78+
},
79+
ArgumentDefinition
80+
{
81+
name : "verbosity".to_string(),
82+
description : "Output verbosity level (0-5)".to_string(),
83+
kind : Kind::Integer,
84+
hint : "Verbosity level".to_string(),
85+
attributes : ArgumentAttributes
86+
{
87+
optional : true,
88+
default : Some( "1".to_string() ),
89+
sensitive : false,
90+
interactive : false,
91+
multiple : false,
92+
},
93+
validation_rules : vec![],
94+
aliases : vec![],
95+
tags : vec![],
96+
},
97+
ArgumentDefinition
98+
{
99+
name : "dry".to_string(),
100+
description : "Dry run mode (0 or 1)".to_string(),
101+
kind : Kind::Boolean,
102+
hint : "Dry run flag".to_string(),
103+
attributes : ArgumentAttributes
104+
{
105+
optional : true,
106+
default : Some( "0".to_string() ),
107+
sensitive : false,
108+
interactive : false,
109+
multiple : false,
110+
},
111+
validation_rules : vec![],
112+
aliases : vec![],
113+
tags : vec![],
114+
},
115+
],
116+
};
117+
118+
registry.command_add_runtime( &cmd, Box::new( crate::handlers::pack::pack_handler ) )?;
9119
Ok( () )
10120
}

module/core/genfile/src/error.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn file_error( message : impl Into< String > ) -> ErrorData
8383
}
8484

8585
/// Format validation error
86-
pub fn _validation_error( message : impl Into< String > ) -> ErrorData
86+
pub fn validation_error( message : impl Into< String > ) -> ErrorData
8787
{
8888
ErrorData
8989
{
@@ -92,3 +92,14 @@ pub fn _validation_error( message : impl Into< String > ) -> ErrorData
9292
source : None,
9393
}
9494
}
95+
96+
/// Format state-related error
97+
pub fn state_error( message : impl Into< String > ) -> ErrorData
98+
{
99+
ErrorData
100+
{
101+
code : ErrorCode::InternalError,
102+
message : format!( "[ERROR] [STATE]: {}", message.into() ),
103+
source : None,
104+
}
105+
}

module/core/genfile/src/handlers/file_old.rs

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)