omni_dev/data/
context.rs

1//! Context data structures for enhanced commit message analysis
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::PathBuf;
7
8/// Complete context information for intelligent commit message improvement
9#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10pub struct CommitContext {
11    /// Project-level context and conventions
12    pub project: ProjectContext,
13    /// Branch analysis and work pattern detection
14    pub branch: BranchContext,
15    /// Multi-commit analysis and work patterns
16    pub range: CommitRangeContext,
17    /// File-specific context and architectural understanding
18    pub files: Vec<FileContext>,
19    /// User-provided context information
20    pub user_provided: Option<String>,
21}
22
23/// Project-level context discovered from configuration files
24#[derive(Debug, Clone, Serialize, Deserialize, Default)]
25pub struct ProjectContext {
26    /// Project-specific commit guidelines from .omni-dev/commit-guidelines.md
27    pub commit_guidelines: Option<String>,
28    /// Default commit message template from .gitmessage or .omni-dev/commit-template.txt
29    pub commit_template: Option<String>,
30    /// Valid scopes and their descriptions from .omni-dev/scopes.yaml
31    pub valid_scopes: Vec<ScopeDefinition>,
32    /// Feature-specific context from .omni-dev/context/
33    pub feature_contexts: HashMap<String, FeatureContext>,
34    /// Parsed conventions from CONTRIBUTING.md
35    pub project_conventions: ProjectConventions,
36    /// Detected ecosystem (rust, node, python, etc.)
37    pub ecosystem: Ecosystem,
38}
39
40/// Definition of a valid scope in the project
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ScopeDefinition {
43    /// Name of the scope
44    pub name: String,
45    /// Human-readable description of the scope
46    pub description: String,
47    /// Example commit messages using this scope
48    pub examples: Vec<String>,
49    /// File patterns that match this scope
50    pub file_patterns: Vec<String>,
51}
52
53/// Context for a specific feature or work area
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct FeatureContext {
56    /// Name of the feature context
57    pub name: String,
58    /// Description of the feature or work area
59    pub description: String,
60    /// Associated scope for this feature
61    pub scope: String,
62    /// Specific conventions for this feature
63    pub conventions: Vec<String>,
64}
65
66/// Project conventions parsed from documentation
67#[derive(Debug, Clone, Serialize, Deserialize, Default)]
68pub struct ProjectConventions {
69    /// Expected commit message format
70    pub commit_format: Option<String>,
71    /// Required trailers like Signed-off-by
72    pub required_trailers: Vec<String>,
73    /// Preferred commit types for this project
74    pub preferred_types: Vec<String>,
75    /// Scope usage requirements and definitions
76    pub scope_requirements: ScopeRequirements,
77}
78
79/// Requirements and validation rules for commit scopes
80#[derive(Debug, Clone, Serialize, Deserialize, Default)]
81pub struct ScopeRequirements {
82    /// Whether a scope is required in commit messages
83    pub required: bool,
84    /// List of valid scope names
85    pub valid_scopes: Vec<String>,
86    /// Mapping from file patterns to suggested scopes
87    pub scope_mapping: HashMap<String, Vec<String>>, // file patterns -> scope
88}
89
90/// Detected project ecosystem
91#[derive(Debug, Clone, Serialize, Deserialize, Default)]
92pub enum Ecosystem {
93    #[default]
94    /// Unknown or undetected ecosystem
95    Unknown,
96    /// Rust ecosystem with Cargo
97    Rust,
98    /// Node.js ecosystem with npm/yarn
99    Node,
100    /// Python ecosystem with pip/poetry
101    Python,
102    /// Go ecosystem with go modules
103    Go,
104    /// Java ecosystem with Maven/Gradle
105    Java,
106    /// Generic project without specific ecosystem
107    Generic,
108}
109
110/// Branch analysis and work pattern detection
111#[derive(Debug, Clone, Serialize, Deserialize, Default)]
112pub struct BranchContext {
113    /// Type of work being performed on this branch
114    pub work_type: WorkType,
115    /// Extracted scope from branch name
116    pub scope: Option<String>,
117    /// Associated ticket or issue ID
118    pub ticket_id: Option<String>,
119    /// Human-readable description of the branch purpose
120    pub description: String,
121    /// Whether this is a feature branch (vs main/master)
122    pub is_feature_branch: bool,
123    /// Base branch this was created from
124    pub base_branch: Option<String>,
125}
126
127/// Type of work being performed
128#[derive(Debug, Clone, Serialize, Deserialize, Default)]
129pub enum WorkType {
130    #[default]
131    /// Unknown or unspecified work type
132    Unknown,
133    /// New feature development
134    Feature,
135    /// Bug fix
136    Fix,
137    /// Documentation changes
138    Docs,
139    /// Code refactoring
140    Refactor,
141    /// Maintenance tasks
142    Chore,
143    /// Test-related changes
144    Test,
145    /// CI/CD pipeline changes
146    Ci,
147    /// Build system changes
148    Build,
149    /// Performance improvements
150    Perf,
151}
152
153impl std::str::FromStr for WorkType {
154    type Err = anyhow::Error;
155
156    fn from_str(s: &str) -> Result<Self> {
157        match s.to_lowercase().as_str() {
158            "feature" | "feat" => Ok(WorkType::Feature),
159            "fix" | "bugfix" => Ok(WorkType::Fix),
160            "docs" | "doc" => Ok(WorkType::Docs),
161            "refactor" | "refact" => Ok(WorkType::Refactor),
162            "chore" => Ok(WorkType::Chore),
163            "test" | "tests" => Ok(WorkType::Test),
164            "ci" => Ok(WorkType::Ci),
165            "build" => Ok(WorkType::Build),
166            "perf" | "performance" => Ok(WorkType::Perf),
167            _ => Ok(WorkType::Unknown),
168        }
169    }
170}
171
172impl std::fmt::Display for WorkType {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        match self {
175            WorkType::Unknown => write!(f, "unknown work"),
176            WorkType::Feature => write!(f, "feature development"),
177            WorkType::Fix => write!(f, "bug fix"),
178            WorkType::Docs => write!(f, "documentation update"),
179            WorkType::Refactor => write!(f, "refactoring"),
180            WorkType::Chore => write!(f, "maintenance"),
181            WorkType::Test => write!(f, "testing"),
182            WorkType::Ci => write!(f, "CI/CD"),
183            WorkType::Build => write!(f, "build system"),
184            WorkType::Perf => write!(f, "performance improvement"),
185        }
186    }
187}
188
189/// Multi-commit analysis and work patterns
190#[derive(Debug, Clone, Serialize, Deserialize, Default)]
191pub struct CommitRangeContext {
192    /// Related commit hashes in this analysis
193    pub related_commits: Vec<String>, // commit hashes
194    /// Files that appear in multiple commits
195    pub common_files: Vec<PathBuf>,
196    /// Detected work pattern across commits
197    pub work_pattern: WorkPattern,
198    /// Analysis of scope consistency
199    pub scope_consistency: ScopeAnalysis,
200    /// Overall architectural impact assessment
201    pub architectural_impact: ArchitecturalImpact,
202    /// Significance of changes for commit message detail
203    pub change_significance: ChangeSignificance,
204}
205
206/// Detected work pattern across commits
207#[derive(Debug, Clone, Serialize, Deserialize, Default)]
208pub enum WorkPattern {
209    #[default]
210    /// Unknown or undetected pattern
211    Unknown,
212    /// Building feature step by step
213    Sequential,
214    /// Multiple small cleanup commits
215    Refactoring,
216    /// Investigation and fixes
217    BugHunt,
218    /// Documentation updates
219    Documentation,
220    /// Config and setup changes
221    Configuration,
222}
223
224/// Analysis of scope consistency across commits
225#[derive(Debug, Clone, Serialize, Deserialize, Default)]
226pub struct ScopeAnalysis {
227    /// Most consistent scope across commits if any
228    pub consistent_scope: Option<String>,
229    /// All scope changes detected
230    pub scope_changes: Vec<String>,
231    /// Confidence level in scope consistency (0.0-1.0)
232    pub confidence: f32,
233}
234
235/// Impact on system architecture
236#[derive(Debug, Clone, Serialize, Deserialize, Default)]
237pub enum ArchitecturalImpact {
238    #[default]
239    /// Small changes, no architecture impact
240    Minimal,
241    /// Some architectural changes
242    Moderate,
243    /// Major architectural changes
244    Significant,
245    /// Breaking changes
246    Breaking,
247}
248
249/// Significance of changes for commit message detail level
250#[derive(Debug, Clone, Serialize, Deserialize, Default)]
251pub enum ChangeSignificance {
252    #[default]
253    /// Simple fix or small addition
254    Minor,
255    /// Notable feature or improvement
256    Moderate,
257    /// Significant enhancement or new capability
258    Major,
259    /// Major system changes or breaking changes
260    Critical,
261}
262
263/// File-based context and architectural understanding
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct FileContext {
266    /// Path to the file
267    pub path: PathBuf,
268    /// Purpose of this file in the project
269    pub file_purpose: FilePurpose,
270    /// Architectural layer this file belongs to
271    pub architectural_layer: ArchitecturalLayer,
272    /// Impact of changes to this file
273    pub change_impact: ChangeImpact,
274    /// Significance of this file in the project
275    pub project_significance: ProjectSignificance,
276}
277
278/// Purpose of the file in the project
279#[derive(Debug, Clone, Serialize, Deserialize)]
280pub enum FilePurpose {
281    /// Configuration files
282    Config,
283    /// Test files
284    Test,
285    /// Docs and README files
286    Documentation,
287    /// Main application logic
288    CoreLogic,
289    /// API definitions, public interfaces
290    Interface,
291    /// Build and deployment files
292    Build,
293    /// Development tools and scripts
294    Tooling,
295}
296
297/// Architectural layer of the file
298#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
299pub enum ArchitecturalLayer {
300    /// UI, CLI, web interfaces
301    Presentation,
302    /// Core business logic
303    Business,
304    /// Data access, models, storage
305    Data,
306    /// System, network, deployment
307    Infrastructure,
308    /// Cross-cutting concerns (logging, auth, etc.)
309    Cross,
310}
311
312/// Impact of changes to this file
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub enum ChangeImpact {
315    /// Formatting, comments, style changes
316    Style,
317    /// New functionality, non-breaking
318    Additive,
319    /// Changing existing functionality
320    Modification,
321    /// Breaking existing functionality
322    Breaking,
323    /// Security, safety, or critical fixes
324    Critical,
325}
326
327/// Significance of file in project
328#[derive(Debug, Clone, Serialize, Deserialize)]
329pub enum ProjectSignificance {
330    /// Common, everyday files
331    Routine,
332    /// Key files but not critical
333    Important,
334    /// Core files that define the project
335    Critical,
336}
337
338impl CommitContext {
339    /// Create a new empty context
340    pub fn new() -> Self {
341        Self::default()
342    }
343
344    /// Check if this context suggests a significant change needing detailed commit message
345    pub fn is_significant_change(&self) -> bool {
346        matches!(
347            self.range.change_significance,
348            ChangeSignificance::Major | ChangeSignificance::Critical
349        ) || matches!(
350            self.range.architectural_impact,
351            ArchitecturalImpact::Significant | ArchitecturalImpact::Breaking
352        ) || self.files.iter().any(|f| {
353            matches!(f.project_significance, ProjectSignificance::Critical)
354                || matches!(
355                    f.change_impact,
356                    ChangeImpact::Breaking | ChangeImpact::Critical
357                )
358        })
359    }
360
361    /// Get suggested commit message verbosity level
362    pub fn suggested_verbosity(&self) -> VerbosityLevel {
363        if self.is_significant_change() {
364            VerbosityLevel::Comprehensive
365        } else if matches!(self.range.change_significance, ChangeSignificance::Moderate)
366            || self.files.len() > 1
367            || self.files.iter().any(|f| {
368                matches!(
369                    f.architectural_layer,
370                    ArchitecturalLayer::Presentation | ArchitecturalLayer::Business
371                )
372            })
373        {
374            // Be more generous with Detailed level for twiddle system:
375            // - Moderate changes
376            // - Multiple files
377            // - UI/CLI or business logic changes
378            VerbosityLevel::Detailed
379        } else {
380            VerbosityLevel::Concise
381        }
382    }
383}
384
385/// Suggested level of detail for commit messages
386#[derive(Debug, Clone, Copy)]
387pub enum VerbosityLevel {
388    /// Single line, basic conventional commit
389    Concise,
390    /// Subject + brief body paragraph
391    Detailed,
392    /// Subject + detailed multi-paragraph body + lists
393    Comprehensive,
394}