1
+ use gitbutler_command_context:: CommandContext ;
1
2
use serde:: { Deserialize , Serialize } ;
2
3
3
4
pub mod db;
@@ -31,7 +32,7 @@ pub enum Trigger {
31
32
/// A filter is a condition that determines what files or changes the rule applies to.
32
33
/// Within a filter, multiple conditions are combined with AND logic (i.e. to match all conditions must be met)
33
34
#[ derive( Serialize , Deserialize , Debug , Clone ) ]
34
- #[ serde( rename_all = "camelCase" ) ]
35
+ #[ serde( rename_all = "camelCase" , tag = "type" , content = "subject" ) ]
35
36
pub enum Filter {
36
37
/// Matches the file path (relative to the repository root) against all provided regex patterns.
37
38
#[ serde( with = "serde_regex" ) ]
@@ -63,7 +64,7 @@ pub enum TreeStatus {
63
64
/// Represents a semantic type of change that was inferred for the change.
64
65
/// Typically this means a heuristic or an LLM determinded that a change represents a refactor, a new feature, a bug fix, or documentation update.
65
66
#[ derive( Serialize , Deserialize , Debug , Clone ) ]
66
- #[ serde( rename_all = "camelCase" ) ]
67
+ #[ serde( rename_all = "camelCase" , tag = "type" , content = "subject" ) ]
67
68
pub enum SemanticType {
68
69
/// A change that is a refactor, meaning it does not change the external behavior of the code but improves its structure.
69
70
Refactor ,
@@ -81,7 +82,7 @@ pub enum SemanticType {
81
82
/// An action can be either explicit, meaning the user defined something like "Assign in Lane A" or "Ammend into Commit X"
82
83
/// or it is implicit, meaning the action was determined by heuristics or AI, such as "Assign to appropriate branch" or "Absorb in dependent commit".
83
84
#[ derive( Serialize , Deserialize , Debug , Clone ) ]
84
- #[ serde( rename_all = "camelCase" ) ]
85
+ #[ serde( rename_all = "camelCase" , tag = "type" , content = "subject" ) ]
85
86
pub enum Action {
86
87
/// An action that has an explicit operation defined by the user.
87
88
Explicit ( Operation ) ,
@@ -91,7 +92,7 @@ pub enum Action {
91
92
92
93
/// Represents the operation that a user can configure to be performed in an explicit action.
93
94
#[ derive( Serialize , Deserialize , Debug , Clone ) ]
94
- #[ serde( rename_all = "camelCase" ) ]
95
+ #[ serde( rename_all = "camelCase" , tag = "type" , content = "subject" ) ]
95
96
pub enum Operation {
96
97
/// Assign the matched changes to a specific stack ID.
97
98
Assign { stack_id : String } ,
@@ -103,7 +104,7 @@ pub enum Operation {
103
104
104
105
/// Represents the implicit operation that is determined by heuristics or AI.
105
106
#[ derive( Serialize , Deserialize , Debug , Clone ) ]
106
- #[ serde( rename_all = "camelCase" ) ]
107
+ #[ serde( rename_all = "camelCase" , tag = "type" , content = "subject" ) ]
107
108
pub enum ImplicitOperation {
108
109
/// Assign the matched changes to the appropriate branch based on offline heuristics.
109
110
AssignToAppropriateBranch ,
@@ -112,3 +113,108 @@ pub enum ImplicitOperation {
112
113
/// Perform an operation based on LLM-driven analysis and tool calling.
113
114
LLMPrompt ( String ) ,
114
115
}
116
+
117
+ /// A request to create a new workspace rule.
118
+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
119
+ #[ serde( rename_all = "camelCase" ) ]
120
+ pub struct CreateRuleRequest {
121
+ /// The trigger that causes the rule to be evaluated.
122
+ pub trigger : Trigger ,
123
+ /// The filters that determine what files or changes the rule applies to. Can not be empty.
124
+ pub filters : Vec < Filter > ,
125
+ /// The action that determines what happens to the files or changes that matched the filters.
126
+ pub action : Action ,
127
+ }
128
+
129
+ /// Creates a new workspace rule
130
+ pub fn create_rule (
131
+ ctx : & mut CommandContext ,
132
+ req : CreateRuleRequest ,
133
+ ) -> anyhow:: Result < WorkspaceRule > {
134
+ if req. filters . is_empty ( ) {
135
+ return Err ( anyhow:: anyhow!( "At least one filter is required" ) ) ;
136
+ }
137
+ let rule = WorkspaceRule {
138
+ id : uuid:: Uuid :: new_v4 ( ) . to_string ( ) ,
139
+ created_at : chrono:: Local :: now ( ) . naive_local ( ) ,
140
+ enabled : true ,
141
+ trigger : req. trigger ,
142
+ filters : req. filters ,
143
+ action : req. action ,
144
+ } ;
145
+
146
+ ctx. db ( ) ?
147
+ . workspace_rules ( )
148
+ . insert ( rule. clone ( ) . try_into ( ) ?)
149
+ . map_err ( |e| anyhow:: anyhow!( "Failed to insert workspace rule: {}" , e) ) ?;
150
+ Ok ( rule)
151
+ }
152
+
153
+ /// Deletes an existing workspace rule by its ID.
154
+ pub fn delete_rule ( ctx : & mut CommandContext , id : & str ) -> anyhow:: Result < ( ) > {
155
+ ctx. db ( ) ?
156
+ . workspace_rules ( )
157
+ . delete ( id)
158
+ . map_err ( |e| anyhow:: anyhow!( "Failed to delete workspace rule: {}" , e) ) ?;
159
+ Ok ( ( ) )
160
+ }
161
+
162
+ /// A request to update an existing workspace rule.
163
+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
164
+ #[ serde( rename_all = "camelCase" ) ]
165
+ pub struct UpdateRuleRequest {
166
+ /// The ID of the rule to update.
167
+ id : String ,
168
+ /// The new enabled state of the rule. If not provided, the existing state is retained.
169
+ enabled : Option < bool > ,
170
+ /// The new trigger for the rule. If not provided, the existing trigger is retained.
171
+ trigger : Option < Trigger > ,
172
+ /// The new filters for the rule. If not provided, the existing filters are retained.
173
+ filters : Option < Vec < Filter > > ,
174
+ /// The new action for the rule. If not provided, the existing action is retained.
175
+ action : Option < Action > ,
176
+ }
177
+
178
+ /// Updates an existing workspace rule with the provided request data.
179
+ pub fn update_rule (
180
+ ctx : & mut CommandContext ,
181
+ req : UpdateRuleRequest ,
182
+ ) -> anyhow:: Result < WorkspaceRule > {
183
+ let mut rule: WorkspaceRule = ctx
184
+ . db ( ) ?
185
+ . workspace_rules ( )
186
+ . get ( & req. id ) ?
187
+ . ok_or_else ( || anyhow:: anyhow!( "Rule with ID {} not found" , req. id) ) ?
188
+ . try_into ( ) ?;
189
+
190
+ if let Some ( enabled) = req. enabled {
191
+ rule. enabled = enabled;
192
+ }
193
+ if let Some ( trigger) = req. trigger {
194
+ rule. trigger = trigger;
195
+ }
196
+ if let Some ( filters) = req. filters {
197
+ rule. filters = filters;
198
+ }
199
+ if let Some ( action) = req. action {
200
+ rule. action = action;
201
+ }
202
+
203
+ ctx. db ( ) ?
204
+ . workspace_rules ( )
205
+ . update ( & req. id , rule. clone ( ) . try_into ( ) ?)
206
+ . map_err ( |e| anyhow:: anyhow!( "Failed to update workspace rule: {}" , e) ) ?;
207
+ Ok ( rule)
208
+ }
209
+
210
+ /// Lists all workspace rules in the database.
211
+ pub fn list_rules ( ctx : & mut CommandContext ) -> anyhow:: Result < Vec < WorkspaceRule > > {
212
+ let rules = ctx
213
+ . db ( ) ?
214
+ . workspace_rules ( )
215
+ . list ( ) ?
216
+ . into_iter ( )
217
+ . map ( |r| r. try_into ( ) )
218
+ . collect :: < Result < Vec < WorkspaceRule > , _ > > ( ) ?;
219
+ Ok ( rules)
220
+ }
0 commit comments