Project Structure
A guided tour of the Two-Face codebase.
Directory Layout
two-face/
├── Cargo.toml # Project manifest
├── Cargo.lock # Dependency lock file
├── CLAUDE.md # Development notes
├── README.md # Project readme
│
├── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Library root (if applicable)
│ ├── config.rs # Configuration loading
│ ├── parser.rs # XML protocol parser
│ ├── network.rs # Network connections
│ │
│ ├── core/ # Core business logic
│ │ ├── mod.rs
│ │ ├── app_core/ # Application state
│ │ │ ├── mod.rs
│ │ │ ├── state.rs
│ │ │ └── layout.rs
│ │ └── menu_actions.rs
│ │
│ ├── data/ # Data models
│ │ ├── mod.rs
│ │ └── widget.rs # Widget data structures
│ │
│ └── frontend/ # User interface
│ └── tui/ # Terminal UI
│ ├── mod.rs
│ ├── input.rs
│ ├── input_handlers.rs
│ ├── command_input.rs
│ ├── text_window.rs
│ ├── tabbed_text_window.rs
│ ├── search.rs
│ ├── sync.rs
│ ├── menu_actions.rs
│ └── window_editor.rs
│
├── book/ # mdbook documentation
│ ├── book.toml
│ └── src/
│ └── *.md
│
├── tests/ # Integration tests
│ └── *.rs
│
└── docs/ # Additional documentation
Module Organization
Three-Layer Architecture
┌─────────────────────────────────────────────────────────────┐
│ src/frontend/tui/ │
│ │
│ Terminal rendering, input handling, visual themes │
│ Knows about: core, data │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ src/core/ │
│ │
│ Application state, business logic, event processing │
│ Knows about: data │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ src/data/ │
│ │
│ Data models, parsing, serialization │
│ Knows about: nothing (foundation layer) │
└─────────────────────────────────────────────────────────────┘
Import rule: Upper layers can import lower layers, not vice versa.
Key Files
Entry Point: src/main.rs
// Typical structure
fn main() {
// Parse command line arguments
let args = Args::parse();
// Load configuration
let config = Config::load(&args)?;
// Initialize application
let app = App::new(config)?;
// Run main loop
app.run()?;
}
Responsibilities:
- CLI argument parsing
- Configuration loading
- Application initialization
- Main event loop
Configuration: src/config.rs
#![allow(unused)]
fn main() {
// Configuration structures
pub struct Config {
pub connection: ConnectionConfig,
pub layout: LayoutConfig,
pub colors: ColorConfig,
pub keybinds: KeybindConfig,
// ...
}
impl Config {
pub fn load(path: &Path) -> Result<Self> { ... }
pub fn save(&self, path: &Path) -> Result<()> { ... }
}
}
Responsibilities:
- TOML parsing
- Default values
- Validation
- Serialization
Parser: src/parser.rs
#![allow(unused)]
fn main() {
// XML protocol parsing
pub enum ParsedElement {
Text(String),
RoomName(String),
RoomDesc(String),
Prompt(PromptData),
Vitals(VitalsData),
// Many more variants...
}
pub struct Parser {
// Parser state
}
impl Parser {
pub fn parse(&mut self, input: &str) -> Vec<ParsedElement> { ... }
}
}
Responsibilities:
- XML tag recognition
- State machine parsing
- Element extraction
- Stream identification
Network: src/network.rs
#![allow(unused)]
fn main() {
// Connection handling
pub enum ConnectionMode {
Lich { host: String, port: u16 },
Direct { account: String, ... },
}
pub struct Connection {
mode: ConnectionMode,
stream: TcpStream,
}
impl Connection {
pub fn connect(mode: ConnectionMode) -> Result<Self> { ... }
pub fn send(&mut self, data: &str) -> Result<()> { ... }
pub fn receive(&mut self) -> Result<String> { ... }
}
}
Responsibilities:
- Connection establishment
- TLS handling (direct mode)
- Data transmission
- Reconnection logic
Widget Data: src/data/widget.rs
#![allow(unused)]
fn main() {
// Widget type definitions
pub enum WidgetType {
Text,
TabbedText,
Progress,
Compass,
// ...
}
pub struct WidgetConfig {
pub widget_type: WidgetType,
pub name: String,
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
// Type-specific config...
}
}
Responsibilities:
- Widget type enumeration
- Configuration structures
- Layout data
- Serialization traits
Application State: src/core/app_core/state.rs
#![allow(unused)]
fn main() {
// Application state management
pub struct AppState {
pub vitals: Vitals,
pub room: RoomInfo,
pub inventory: Inventory,
pub indicators: Indicators,
// ...
generation: u64, // Change tracking
}
impl AppState {
pub fn update(&mut self, element: ParsedElement) {
// Update state based on parsed element
self.generation += 1;
}
}
}
Responsibilities:
- Game state tracking
- Update processing
- Change notification
- Generation counting
Layout: src/core/app_core/layout.rs
#![allow(unused)]
fn main() {
// Layout management
pub struct Layout {
widgets: Vec<Widget>,
focus_index: usize,
}
impl Layout {
pub fn load(config: &LayoutConfig) -> Result<Self> { ... }
pub fn render(&self, frame: &mut Frame) { ... }
pub fn handle_input(&mut self, key: KeyEvent) { ... }
}
}
Responsibilities:
- Widget arrangement
- Focus management
- Layout loading
- Coordinate calculation
TUI Module: src/frontend/tui/mod.rs
#![allow(unused)]
fn main() {
// Terminal UI main module
pub struct Tui {
terminal: Terminal<Backend>,
layout: Layout,
state: AppState,
}
impl Tui {
pub fn new() -> Result<Self> { ... }
pub fn run(&mut self) -> Result<()> { ... }
pub fn render(&mut self) -> Result<()> { ... }
pub fn handle_event(&mut self, event: Event) -> Result<()> { ... }
}
}
Responsibilities:
- Terminal setup/teardown
- Event loop
- Rendering coordination
- Input routing
Data Flow
Incoming Data
Network → Parser → State Update → Widget Sync → Render
│ │ │ │ │
│ │ │ │ └─ TUI draws frame
│ │ │ └─ Widgets check generation
│ │ └─ AppState updates, bumps generation
│ └─ Raw XML → ParsedElements
└─ TCP/TLS receives bytes
User Input
Terminal Event → TUI → Handler → Action → Effect
│ │ │ │ │
│ │ │ │ └─ State change or command send
│ │ │ └─ Keybind lookup / macro expansion
│ │ └─ Input type routing (key/mouse/resize)
│ └─ Event loop captures
└─ User presses key
Testing Structure
tests/
├── integration_test.rs # Full application tests
├── parser_tests.rs # Parser-specific tests
└── widget_tests.rs # Widget rendering tests
src/
├── parser.rs
│ └── #[cfg(test)] mod tests { ... } # Unit tests
└── config.rs
└── #[cfg(test)] mod tests { ... } # Unit tests
Configuration Files
User configuration (runtime):
~/.two-face/
├── config.toml
├── layout.toml
├── colors.toml
├── highlights.toml
├── keybinds.toml
├── triggers.toml
└── simu.pem
Documentation
book/ # mdbook documentation (this!)
├── book.toml
└── src/
├── SUMMARY.md
└── *.md
docs/ # Additional docs
└── *.md
Build Artifacts
target/
├── debug/ # Debug build
│ └── two-face
├── release/ # Release build
│ └── two-face
└── doc/ # Generated documentation
└── two_face/
See Also
- Architecture - System design
- Adding Widgets - Extend widget system
- Parser Extensions - Extend parser