1use rusqlite::{params, Connection, OptionalExtension, Row};
2
3#[derive(Debug, Clone)]
9pub struct BashPatternWatchRow {
10 pub harness: String,
11 pub session_id: String,
12 pub task_id: String,
13 pub watch_id: String,
14 pub pattern_kind: String,
16 pub pattern: String,
17 pub once: bool,
18 pub created_at: i64,
19 pub stdout_offset: i64,
20 pub stderr_offset: i64,
21 pub pty_offset: i64,
22 pub scanning: bool,
24 pub pending_match: bool,
25 pub match_text: Option<String>,
26 pub match_offset: Option<i64>,
27 pub match_context: Option<String>,
28}
29
30pub fn upsert_bash_pattern_watch(
31 conn: &Connection,
32 row: &BashPatternWatchRow,
33) -> rusqlite::Result<()> {
34 conn.execute(
35 "INSERT INTO bash_pattern_watches (
36 harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
37 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
38 pending_match, match_text, match_offset, match_context
39 ) VALUES (
40 ?1, ?2, ?3, ?4, ?5, ?6, ?7,
41 ?8, ?9, ?10, ?11, ?12,
42 ?13, ?14, ?15, ?16
43 )
44 ON CONFLICT(harness, session_id, task_id, watch_id) DO UPDATE SET
45 pattern_kind = excluded.pattern_kind,
46 pattern = excluded.pattern,
47 once = excluded.once,
48 created_at = excluded.created_at,
49 stdout_offset = excluded.stdout_offset,
50 stderr_offset = excluded.stderr_offset,
51 pty_offset = excluded.pty_offset,
52 scanning = excluded.scanning,
53 pending_match = excluded.pending_match,
54 match_text = excluded.match_text,
55 match_offset = excluded.match_offset,
56 match_context = excluded.match_context",
57 params![
58 row.harness,
59 row.session_id,
60 row.task_id,
61 row.watch_id,
62 row.pattern_kind,
63 row.pattern,
64 row.once,
65 row.created_at,
66 row.stdout_offset,
67 row.stderr_offset,
68 row.pty_offset,
69 row.scanning,
70 row.pending_match,
71 row.match_text,
72 row.match_offset,
73 row.match_context,
74 ],
75 )?;
76 Ok(())
77}
78
79pub fn delete_bash_pattern_watch(
80 conn: &Connection,
81 harness: &str,
82 session_id: &str,
83 task_id: &str,
84 watch_id: &str,
85) -> rusqlite::Result<usize> {
86 conn.execute(
87 "DELETE FROM bash_pattern_watches
88 WHERE harness = ?1 AND session_id = ?2 AND task_id = ?3 AND watch_id = ?4",
89 params![harness, session_id, task_id, watch_id],
90 )
91}
92
93pub fn delete_bash_pattern_watches_for_task(
94 conn: &Connection,
95 harness: &str,
96 session_id: &str,
97 task_id: &str,
98) -> rusqlite::Result<usize> {
99 conn.execute(
100 "DELETE FROM bash_pattern_watches
101 WHERE harness = ?1 AND session_id = ?2 AND task_id = ?3",
102 params![harness, session_id, task_id],
103 )
104}
105
106pub fn list_bash_pattern_watches_for_task(
107 conn: &Connection,
108 harness: &str,
109 session_id: &str,
110 task_id: &str,
111) -> rusqlite::Result<Vec<BashPatternWatchRow>> {
112 let mut stmt = conn.prepare(
113 "SELECT harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
114 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
115 pending_match, match_text, match_offset, match_context
116 FROM bash_pattern_watches
117 WHERE harness = ?1 AND session_id = ?2 AND task_id = ?3
118 ORDER BY created_at ASC, watch_id ASC",
119 )?;
120 let rows = stmt
121 .query_map(params![harness, session_id, task_id], map_watch_row)?
122 .collect();
123 rows
124}
125
126pub fn list_bash_pattern_watches_for_session(
127 conn: &Connection,
128 harness: &str,
129 session_id: &str,
130) -> rusqlite::Result<Vec<BashPatternWatchRow>> {
131 let mut stmt = conn.prepare(
132 "SELECT harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
133 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
134 pending_match, match_text, match_offset, match_context
135 FROM bash_pattern_watches
136 WHERE harness = ?1 AND session_id = ?2
137 ORDER BY created_at ASC, task_id ASC, watch_id ASC",
138 )?;
139 let rows = stmt
140 .query_map(params![harness, session_id], map_watch_row)?
141 .collect();
142 rows
143}
144
145pub fn list_bash_pattern_watches(
146 conn: &Connection,
147 harness: &str,
148) -> rusqlite::Result<Vec<BashPatternWatchRow>> {
149 let mut stmt = conn.prepare(
150 "SELECT harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
151 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
152 pending_match, match_text, match_offset, match_context
153 FROM bash_pattern_watches
154 WHERE harness = ?1
155 ORDER BY created_at ASC, task_id ASC, watch_id ASC",
156 )?;
157 let rows = stmt.query_map(params![harness], map_watch_row)?.collect();
158 rows
159}
160
161pub fn list_bash_pattern_watches_by_task_id(
162 conn: &Connection,
163 harness: &str,
164 task_id: &str,
165) -> rusqlite::Result<Vec<BashPatternWatchRow>> {
166 let mut stmt = conn.prepare(
167 "SELECT harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
168 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
169 pending_match, match_text, match_offset, match_context
170 FROM bash_pattern_watches
171 WHERE harness = ?1 AND task_id = ?2
172 ORDER BY created_at ASC, session_id ASC, watch_id ASC",
173 )?;
174 let rows = stmt
175 .query_map(params![harness, task_id], map_watch_row)?
176 .collect();
177 rows
178}
179
180pub fn get_bash_pattern_watch(
181 conn: &Connection,
182 harness: &str,
183 session_id: &str,
184 task_id: &str,
185 watch_id: &str,
186) -> rusqlite::Result<Option<BashPatternWatchRow>> {
187 conn.query_row(
188 "SELECT harness, session_id, task_id, watch_id, pattern_kind, pattern, once,
189 created_at, stdout_offset, stderr_offset, pty_offset, scanning,
190 pending_match, match_text, match_offset, match_context
191 FROM bash_pattern_watches
192 WHERE harness = ?1 AND session_id = ?2 AND task_id = ?3 AND watch_id = ?4",
193 params![harness, session_id, task_id, watch_id],
194 map_watch_row,
195 )
196 .optional()
197}
198
199pub fn update_watch_offsets_for_task(
200 conn: &Connection,
201 harness: &str,
202 session_id: &str,
203 task_id: &str,
204 stdout_offset: i64,
205 stderr_offset: i64,
206 pty_offset: i64,
207) -> rusqlite::Result<usize> {
208 conn.execute(
209 "UPDATE bash_pattern_watches
210 SET stdout_offset = ?4, stderr_offset = ?5, pty_offset = ?6
211 WHERE harness = ?1 AND session_id = ?2 AND task_id = ?3",
212 params![
213 harness,
214 session_id,
215 task_id,
216 stdout_offset,
217 stderr_offset,
218 pty_offset
219 ],
220 )
221}
222
223fn map_watch_row(row: &Row<'_>) -> rusqlite::Result<BashPatternWatchRow> {
224 Ok(BashPatternWatchRow {
225 harness: row.get(0)?,
226 session_id: row.get(1)?,
227 task_id: row.get(2)?,
228 watch_id: row.get(3)?,
229 pattern_kind: row.get(4)?,
230 pattern: row.get(5)?,
231 once: row.get::<_, i64>(6)? != 0,
232 created_at: row.get(7)?,
233 stdout_offset: row.get(8)?,
234 stderr_offset: row.get(9)?,
235 pty_offset: row.get(10)?,
236 scanning: row.get::<_, i64>(11)? != 0,
237 pending_match: row.get::<_, i64>(12)? != 0,
238 match_text: row.get(13)?,
239 match_offset: row.get(14)?,
240 match_context: row.get(15)?,
241 })
242}