{
    "app": {
        "name": "design-patterns",
        "description": "Rust design patterns for RTK. Newtype, Builder, RAII, Trait Objects, State Machine. Applied to CLI filter modules. Use when designing new modules or refactoring existing ones.",
        "mode": "advanced-chat",
        "model_config": {
            "provider": "deepseek",
            "model": "deepseek-chat",
            "parameters": {
                "temperature": 0.7,
                "max_tokens": 4096
            }
        }
    },
    "instructions": "name design-patterns description Rust design patterns for RTK. Newtype, Builder, RAII, Trait Objects, State Machine. Applied to CLI filter modules. Use when designing new modules or refactoring existing ones. triggers [\"design pattern\",\"how to structure\",\"best pattern for\",\"refactor to pattern\"] allowed-tools [\"Read\",\"Grep\",\"Glob\"] effort medium tags [\"rust\",\"design-patterns\",\"architecture\",\"newtype\",\"builder\",\"rtk\"] RTK Rust Design Patterns Patterns that apply to RTK's filter module architecture. Focused on CLI tool patterns, not web/service patterns. Pattern 1: Newtype (Type Safety) Use when: wrapping primitive types to prevent misuse (command names, paths, token counts). // Without Newtype — easy to mix up fn track (input_tokens: usize , output_tokens: usize ) { ... } track (output_tokens, input_tokens); // Silent bug! // With Newtype — compile error on swap pub struct InputTokens ( pub usize ); pub struct OutputTokens ( pub usize ); fn track (input: InputTokens, output: OutputTokens) { ... } track ( OutputTokens ( 100 ), InputTokens ( 400 )); // Compile error ✅ // Practical RTK example: command name validation pub struct CommandName ( String ); impl CommandName { pub fn new (s: & str ) -> Result < Self > { if s. contains ( ';' ) || s. contains ( '|' ) || s. contains ( '`' ) { anyhow::bail!( \"Invalid command name: shell metacharacters\" ); } Ok ( Self (s. to_string ())) } pub fn as_str (& self ) -> & str { & self . 0 } } Pattern 2: Builder (Complex Configuration) Use when: a struct has 4+ optional fields, many with defaults. #[derive(Default)] pub struct FilterConfig { max_lines: Option < usize >, strip_ansi: bool , show_warnings: bool , truncate_at: Option < usize >, } impl FilterConfig { pub fn new () -> Self { Self :: default () } pub fn max_lines ( mut self , n: usize ) -> Self { self .max_lines = Some (n); self } pub fn strip_ansi ( mut self , v: bool ) -> Self { self .strip_ansi = v; self } pub fn show_warnings ( mut self , v: bool ) -> Self { self .show_warnings = v; self } } // Usage — readable, no positional arg confusion let config = FilterConfig:: new () . max_lines ( 50 ) . strip_ansi ( true ) . show_warnings ( false ); When NOT to use Builder: if the struct has 1-3 fields with obvious meaning. Over-engineering for simple cases. Pattern 3: State Machine (Parser/Filter Flows) Use when: parsing multi-section output (test results, build output) where context changes behavior. // RTK example: pytest output parsing #[derive(Debug, PartialEq)] enum ParseState { LookingForTests, InTestOutput, InFailureSummary, Done, } fn parse_pytest (input: & str ) -> String { let mut state = ParseState::LookingForTests; let mut failures = Vec :: new (); for line in input. lines () { match state { ParseState::LookingForTests => { if line. contains ( \"FAILED\" ) || line. contains ( \"ERROR\" ) { state = ParseState::InFailureSummary; failures. push (line); } } ParseState::InFailureSummary => { if line. starts_with ( \"=====\" ) { state = ParseState::Done; } else { failures. push (line); } } ParseState::Done => break , _ => {} } } failures. join ( \"\\n\" ) } Pattern 4: Trait Object (Command Dispatch) Use when: different command families need the same interface. Avoids massive match arms. // Define a common interface for filters pub trait OutputFilter { fn filter (& self , input: & str ) -> Result < String >; fn command_name (& self ) -> & str ; } pub struct GitFilter ; pub struct CargoFilter ; impl OutputFilter for GitFilter { fn filter (& self , input: & str ) -> Result < String > { filter_git (input) } fn command_name (& self ) -> & str { \"git\" } } // RTK currently uses match-based dispatch in main.rs (simpler, no dynamic dispatch overhead) // Trait objects are useful if filter registry becomes dynamic (e.g., TOML-loaded plugins) Note: RTK's current match dispatch in main.rs is intentional — static dispatch, zero overhead. Only move to trait objects if the match arm count exceeds ~20 commands. Pattern 5: RAII (Resource Management) Use when: managing resources that need cleanup (temp files, SQLite connections). // RTK tee.rs — RAII for temp output files pub struct TeeFile { path: PathBuf, } impl TeeFile { pub fn create (content: & str ) -> Result < Self > { let path = tee_path ()?; fs:: write (&path, content) . with_context (|| format! ( \"Failed to write tee file: {}\" , path. display ()))?; Ok ( Self { path }) } pub fn path (& self ) -> &Path { & self .path } } // No explicit cleanup needed — file persists intentionally (rotation handled separately) // If cleanup were needed: impl Drop { fn drop(&mut self) { let _ = fs::remove_file(&self.path); } } Pattern 6: Strategy (Swappable Filter Logic) Use when: a command has multiple filtering modes (e.g., compact vs. verbose). pub enum FilterMode { Compact, // Show only failures/errors Summary, // Show counts + top errors Full, // Pass through unchanged } pub fn apply_filter (input: & str , mode: FilterMode) -> String { match mode { FilterMode::Compact => filter_compact (input), FilterMode::Summary => filter_summary (input), FilterMode::Full => input. to_string (), } } Pattern 7: Extension Trait (Add Methods to External Types) Use when: you need to add methods to types you don't own (like &str for RTK-specific parsing). pub trait RtkStrExt { fn is_error_line (& self ) -> bool ; fn is_warning_line (& self ) -> bool ; fn token_count (& self ) -> usize ; } impl RtkStrExt for str { fn is_error_line (& self ) -> bool { self . starts_with ( \"error\" ) || self . contains ( \"[E\" ) } fn is_warning_line (& self ) -> bool { self . starts_with ( \"warning\" ) } fn token_count (& self ) -> usize { self . split_whitespace (). count () } } // Usage if line. is_error_line () { ... } let tokens = output. token_count (); RTK Pattern Selection Guide Situation Pattern Avoid New *_cmd.rs filter module Standard module pattern (see CLAUDE.md) Over-abstracting 4+ optional config fields Builder Struct literal Multi-phase output parsing State Machine Nested if/else Type-safe wrapper around string Newtype Raw String Adding methods to &str Extension Trait Free functions Resource with cleanup RAII / Drop Manual cleanup Dynamic filter registry Trait Object Match sprawl Anti-Patterns in RTK Context // ❌ Generic over-engineering for one command pub trait Filterable <T: CommandArgs + Send + Sync + 'static > { ... } // ✅ Just write the function pub fn filter_git_log (input: & str ) -> Result < String > { ... } // ❌ Singleton registry with global state static FILTER_REGISTRY: Mutex<HashMap< String , Box < dyn Filter>>> = ...; // ✅ Match in main.rs — simple, zero overhead, easy to trace // ❌ Async traits for \"future-proofing\" #[async_trait] pub trait Filter { async fn apply (& self , input: & str ) -> Result < String >; } // ✅ Synchronous — RTK is single-threaded by design pub trait Filter { fn apply (& self , input: & str ) -> Result < String >; }",
    "variables": [],
    "opening_statement": "你好，我是 design-patterns，Rust design patterns for RTK. Newtype, Builder, RA...",
    "suggested_questions": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=rtk-ai-rtk-claude-skills-design-patterns-skill-md"
}