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