1use std::collections::{BTreeSet, HashMap};
5use std::io;
6use std::sync::Arc;
7
8use rig_core::completion::Message;
9use rig_core::conversation::{Aside, Author, Change, Conversation, Lineage};
10use serde::de::DeserializeOwned;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14use crate::blobs::{BLOBS, blob_names, named_blobs, put_blob, read_blob, store_change};
15use crate::format::{Body, Header, LogLines, LogRewrite, Meta, parse, write_change, write_meta};
16use crate::session::{Fold, Folded, fold_lines, read_header, read_logs};
17use crate::writer::{refused, set_aside_tail};
18use crate::{Files, MemoryStore, SessionError, Stamp, Store};
19use rig_core::now_ms;
20
21#[derive(Clone, Copy, Debug)]
23pub enum Place<'a> {
24 Record {
27 log: &'a str,
29 stamp: Stamp,
31 by: Option<&'a Author>,
33 },
34 File {
37 name: &'a str,
39 },
40}
41
42pub trait Rewrite: Send + Sync {
49 fn message(&self, at: &Place<'_>, message: &mut Message) -> bool {
53 let _ = (at, message);
54 false
55 }
56
57 fn aside(&self, at: &Place<'_>, aside: &mut Aside) -> bool {
59 let _ = (at, aside);
60 false
61 }
62
63 fn value(&self, at: &Place<'_>, key: &str, value: &mut Value) -> bool {
66 let _ = (at, key, value);
67 false
68 }
69
70 fn text(&self, at: &Place<'_>, text: &mut String) -> bool {
72 let _ = (at, text);
73 false
74 }
75
76 fn file(&self, name: &str, bytes: &mut Vec<u8>) -> bool {
78 map_file(self, name, bytes)
79 }
80}
81
82pub struct Strings<F: Fn(&mut String) -> bool + Send + Sync>(pub F);
111
112impl<F: Fn(&mut String) -> bool + Send + Sync> Rewrite for Strings<F> {
113 fn message(&self, _: &Place<'_>, message: &mut Message) -> bool {
114 map_serde(message, &self.0)
115 }
116
117 fn aside(&self, _: &Place<'_>, aside: &mut Aside) -> bool {
118 map_serde(aside, &self.0)
119 }
120
121 fn value(&self, _: &Place<'_>, _: &str, value: &mut Value) -> bool {
122 map_strings(value, &self.0)
123 }
124
125 fn text(&self, _: &Place<'_>, text: &mut String) -> bool {
126 (self.0)(text)
127 }
128}
129
130pub struct Messages<F: Fn(&mut Message) -> bool + Send + Sync>(pub F);
132
133impl<F: Fn(&mut Message) -> bool + Send + Sync> Rewrite for Messages<F> {
134 fn message(&self, _: &Place<'_>, message: &mut Message) -> bool {
135 (self.0)(message)
136 }
137
138 fn file(&self, _: &str, _: &mut Vec<u8>) -> bool {
139 false
140 }
141}
142
143pub fn map_strings(value: &mut Value, f: &dyn Fn(&mut String) -> bool) -> bool {
146 match value {
147 Value::String(text) => f(text),
148 Value::Array(items) => items
149 .iter_mut()
150 .fold(false, |changed, item| map_strings(item, f) | changed),
151 Value::Object(map) => {
152 let mut changed = map
153 .values_mut()
154 .fold(false, |changed, item| map_strings(item, f) | changed);
155 let renamed: Vec<(String, String)> = map
156 .keys()
157 .filter_map(|key| {
158 let mut new = key.clone();
159 f(&mut new).then(|| (key.clone(), new))
160 })
161 .collect();
162 if !renamed.is_empty() {
163 let renamed: HashMap<String, String> = renamed.into_iter().collect();
164 *map = std::mem::take(map)
165 .into_iter()
166 .map(|(key, item)| match renamed.get(&key) {
167 Some(new) => (new.clone(), item),
168 None => (key, item),
169 })
170 .collect();
171 changed = true;
172 }
173 changed
174 }
175 Value::Null | Value::Bool(_) | Value::Number(_) => false,
176 }
177}
178
179fn map_serde<T: Serialize + DeserializeOwned>(
184 item: &mut T,
185 f: &dyn Fn(&mut String) -> bool,
186) -> bool {
187 let Ok(mut value) = serde_json::to_value(&*item) else {
188 return false;
189 };
190 if !map_strings(&mut value, f) {
191 return false;
192 }
193 if let Ok(read) = T::deserialize(&value) {
194 *item = read;
195 return true;
196 }
197 let Ok(mut kept) = serde_json::to_value(&*item) else {
198 return false;
199 };
200 let mut leaves = Vec::new();
201 strings_of(&kept, &mut Vec::new(), f, &mut leaves);
202 let mut changed = false;
203 for leaf in leaves {
204 let mut trial = kept.clone();
205 if map_leaf(&mut trial, &leaf, f)
206 && let Ok(read) = T::deserialize(&trial)
207 {
208 *item = read;
209 kept = trial;
210 changed = true;
211 }
212 }
213 changed
214}
215
216#[derive(Clone)]
218enum Step {
219 Index(usize),
220 Key(String),
221}
222
223struct Leaf {
226 path: Vec<Step>,
227 key: bool,
228}
229
230fn strings_of(
233 value: &Value,
234 path: &mut Vec<Step>,
235 f: &dyn Fn(&mut String) -> bool,
236 leaves: &mut Vec<Leaf>,
237) {
238 let changes = |text: &str| f(&mut text.to_owned());
239 match value {
240 Value::String(text) if changes(text) => leaves.push(Leaf {
241 path: path.clone(),
242 key: false,
243 }),
244 Value::Array(items) => {
245 for (at, item) in items.iter().enumerate() {
246 path.push(Step::Index(at));
247 strings_of(item, path, f, leaves);
248 path.pop();
249 }
250 }
251 Value::Object(map) => {
252 for (key, item) in map {
253 path.push(Step::Key(key.clone()));
254 strings_of(item, path, f, leaves);
255 if changes(key) {
256 leaves.push(Leaf {
257 path: path.clone(),
258 key: true,
259 });
260 }
261 path.pop();
262 }
263 }
264 _ => {}
265 }
266}
267
268fn map_leaf(value: &mut Value, leaf: &Leaf, f: &dyn Fn(&mut String) -> bool) -> bool {
270 let (last, parent) = match leaf.path.split_last() {
271 Some((last, parent)) if leaf.key => (Some(last), parent),
272 _ => (None, leaf.path.as_slice()),
273 };
274 let mut at = value;
275 for step in parent {
276 let next = match step {
277 Step::Index(index) => at.get_mut(*index),
278 Step::Key(key) => at.get_mut(key.as_str()),
279 };
280 let Some(next) = next else {
281 return false;
282 };
283 at = next;
284 }
285 match (last, at) {
286 (None, Value::String(text)) => f(text),
287 (Some(Step::Key(key)), Value::Object(map)) => {
288 let mut new = key.clone();
289 if !f(&mut new) || map.contains_key(&new) || !map.contains_key(key) {
290 return false;
291 }
292 *map = std::mem::take(map)
293 .into_iter()
294 .map(|(old, item)| match old == *key {
295 true => (new.clone(), item),
296 false => (old, item),
297 })
298 .collect();
299 true
300 }
301 _ => false,
302 }
303}
304
305pub fn rewrite_value<T: Serialize + DeserializeOwned>(
315 with: &dyn Rewrite,
316 at: &Place<'_>,
317 key: &str,
318 item: &mut T,
319) -> Result<bool, serde_json::Error> {
320 let mut value = serde_json::to_value(&*item)?;
321 if !with.value(at, key, &mut value) {
322 return Ok(false);
323 }
324 *item = T::deserialize(value)?;
325 Ok(true)
326}
327
328pub fn rewrite_file(files: &dyn Files, name: &str, with: &dyn Rewrite) -> io::Result<bool> {
338 let mut bytes = match files.read_file(name) {
339 Ok(bytes) => bytes,
340 Err(failure) if failure.kind() == io::ErrorKind::NotFound => return Ok(false),
341 Err(failure) => return Err(failure),
342 };
343 if !with.file(name, &mut bytes) {
344 return Ok(false);
345 }
346 files.write_file(name, &bytes)?;
347 Ok(true)
348}
349
350fn map_file<R: Rewrite + ?Sized>(with: &R, name: &str, bytes: &mut Vec<u8>) -> bool {
352 let at = Place::File { name };
353 if let Ok(mut value) = serde_json::from_slice::<Value>(bytes) {
354 if !with.value(&at, name, &mut value) {
355 return false;
356 }
357 let pretty = bytes.trim_ascii_end().contains(&b'\n');
358 let written = match pretty {
359 true => serde_json::to_vec_pretty(&value),
360 false => serde_json::to_vec(&value),
361 };
362 let Ok(mut written) = written else {
363 return false;
364 };
365 if bytes.ends_with(b"\n") {
366 written.push(b'\n');
367 }
368 *bytes = written;
369 return true;
370 }
371 let Ok(text) = std::str::from_utf8(bytes) else {
372 return false;
373 };
374 let lines: Vec<(&str, &str, Option<Value>)> = text
375 .split_inclusive('\n')
376 .map(|line| {
377 let (body, end) = line
378 .strip_suffix('\n')
379 .map_or((line, ""), |body| (body, "\n"));
380 (body, end, serde_json::from_str(body).ok())
381 })
382 .collect();
383 let complete: Vec<_> = lines
384 .iter()
385 .filter(|(body, end, _)| !end.is_empty() && !body.trim().is_empty())
386 .collect();
387 let json_lines = !complete.is_empty() && complete.iter().all(|line| line.2.is_some());
388 if !json_lines {
389 let mut text = text.to_owned();
390 let changed = with.text(&at, &mut text);
391 if changed {
392 *bytes = text.into_bytes();
393 }
394 return changed;
395 }
396 let mut out = String::with_capacity(text.len());
397 let mut changed = false;
398 for (body, end, value) in lines {
399 match value {
400 Some(mut value) => match with.value(&at, name, &mut value) {
401 true => {
402 match serde_json::to_string(&value) {
403 Ok(line) => out.push_str(&line),
404 Err(_) => out.push_str(body),
405 }
406 changed = true;
407 }
408 false => out.push_str(body),
409 },
410 None => {
411 let mut line = body.to_owned();
412 changed |= with.text(&at, &mut line);
413 out.push_str(&line);
414 }
415 }
416 out.push_str(end);
417 }
418 if changed {
419 *bytes = out.into_bytes();
420 }
421 changed
422}
423
424#[derive(Clone, Debug, Default)]
426pub struct Rewritten {
427 pub logs: Vec<RewrittenLog>,
429 pub files: Vec<String>,
431 pub blobs_removed: Vec<String>,
433}
434
435#[derive(Clone, Debug)]
437pub struct RewrittenLog {
438 pub log: String,
440 pub records: usize,
442}
443
444pub(crate) struct Mapped {
446 pub header: (Stamp, Header),
448 pub body: Vec<u8>,
451 pub records: usize,
453}
454
455pub(crate) fn map_log(
459 name: &str,
460 lines: &LogLines,
461 with: &dyn Rewrite,
462 blobs: &dyn Store,
463) -> Result<Option<Mapped>, SessionError> {
464 let refuse = |line, why| SessionError::Record {
465 log: name.to_owned(),
466 line,
467 why,
468 };
469 let Some((_, _, first)) = lines.iter().next() else {
470 return Ok(None);
471 };
472 let (stamp, header) = read_header(name, first)?;
473 let mut mapped = Mapped {
474 header: (stamp, header),
475 body: Vec::new(),
476 records: 0,
477 };
478 for (line, _, bytes) in lines.iter().skip(1) {
479 let (stamp, body) = parse(bytes).map_err(|why| refuse(line, why))?;
480 match map_record(name, stamp, body, with, blobs).map_err(|why| refuse(line, why))? {
481 Some(record) => {
482 mapped.body.extend_from_slice(&record);
483 mapped.records += 1;
484 }
485 None => {
486 mapped.body.extend_from_slice(bytes);
487 mapped.body.push(b'\n');
488 }
489 }
490 }
491 Ok(Some(mapped))
492}
493
494pub(crate) fn map_record(
497 log: &str,
498 stamp: Stamp,
499 body: Body,
500 with: &dyn Rewrite,
501 blobs: &dyn Store,
502) -> Result<Option<Vec<u8>>, String> {
503 let mut line = Vec::new();
504 match body {
505 Body::Meta(Meta::State {
506 key,
507 value: Some(mut value),
508 }) if !value.is_null() => {
509 let at = Place::Record {
510 log,
511 stamp,
512 by: None,
513 };
514 if !with.value(&at, &key, &mut value) {
515 return Ok(None);
516 }
517 let state = Meta::State {
518 key,
519 value: Some(value),
520 };
521 write_meta(&mut line, stamp, &state).map_err(|failure| failure.to_string())?;
522 }
523 Body::Meta(_) => return Ok(None),
524 Body::Change(mut recorded) => {
525 let at = Place::Record {
526 log,
527 stamp,
528 by: recorded.by.as_ref(),
529 };
530 if !map_change(&mut recorded.change, &at, with)? {
531 return Ok(None);
532 }
533 let put = &mut |name: String, bytes: Vec<u8>| put_blob(blobs, &name, &bytes);
534 let stored = store_change(&recorded.change, put)
535 .map_err(|failure| format!("storing an image failed: {failure}"))?;
536 let change = stored.as_ref().unwrap_or(&recorded.change);
537 write_change(&mut line, stamp, recorded.by.as_ref(), change)
538 .map_err(|failure| failure.to_string())?;
539 }
540 }
541 Ok(Some(line))
542}
543
544pub(crate) fn map_change(
547 change: &mut Change,
548 at: &Place<'_>,
549 with: &dyn Rewrite,
550) -> Result<bool, String> {
551 Ok(match change {
552 Change::Push { entries } | Change::Splice { with: entries, .. } => {
553 entries.iter_mut().fold(false, |changed, entry| {
554 with.message(at, Arc::make_mut(&mut entry.message)) | changed
555 })
556 }
557 Change::Aside {
558 aside: Some(aside), ..
559 } => with.aside(at, aside),
560 Change::Reset { state } => map_state(state, at, with)?,
561 Change::Aside { aside: None, .. } | Change::Halt { .. } => false,
562 })
563}
564
565fn map_state(state: &mut Conversation, at: &Place<'_>, with: &dyn Rewrite) -> Result<bool, String> {
568 let fail = |failure: serde_json::Error| failure.to_string();
569 let mut value = serde_json::to_value(&*state).map_err(fail)?;
570 let mut changed = false;
571 if let Some(Value::Array(messages)) = value.get_mut("messages") {
572 for message in messages {
573 let mut typed = Message::deserialize(&*message).map_err(fail)?;
574 if with.message(at, &mut typed) {
575 *message = serde_json::to_value(&typed).map_err(fail)?;
576 changed = true;
577 }
578 }
579 }
580 if let Some(Value::Array(asides)) = value.get_mut("asides") {
581 for aside in asides.iter_mut().filter_map(|pair| pair.get_mut(1)) {
582 let mut typed = Aside::deserialize(&*aside).map_err(fail)?;
583 if with.aside(at, &mut typed) {
584 *aside = serde_json::to_value(&typed).map_err(fail)?;
585 changed = true;
586 }
587 }
588 }
589 if changed {
590 *state = Conversation::deserialize(&value).map_err(fail)?;
591 }
592 Ok(changed)
593}
594
595pub fn rewrite(
631 store: &dyn Store,
632 by: Author,
633 with: &dyn Rewrite,
634) -> Result<Rewritten, SessionError> {
635 let mut logs: Vec<(String, LogLines, Folded)> = Vec::new();
636 for (name, lines) in read_logs(store)? {
637 if let Some(folded) = fold_lines(&name, &lines, Fold::default(), &|_| {
638 Err(io::ErrorKind::NotFound.into())
639 })? {
640 logs.push((name, lines, folded));
641 }
642 }
643 logs.sort_by(|a, b| a.0.cmp(&b.0));
644 let t = now_ms();
645 let blobs = MemoryStore::default();
646 let blob = |name: &str| read_blob(&blobs, name).or_else(|_| read_blob(store, name));
647 let mut rewritten = Rewritten::default();
648 let mut replaced: Vec<(u64, Vec<u8>)> = Vec::new();
649 for (name, lines, _) in &logs {
650 let Some(mapped) = map_log(name, lines, with, &blobs)? else {
651 continue;
652 };
653 if mapped.records == 0 {
654 continue;
655 }
656 let (stamp, mut header) = mapped.header;
657 header.lineage = Lineage::fresh();
658 header.rewrites.push(LogRewrite { t, by: by.clone() });
659 let mut bytes = Vec::with_capacity(mapped.body.len() + 256);
660 write_meta(&mut bytes, stamp, &Meta::Header(header)).map_err(io::Error::from)?;
661 bytes.extend_from_slice(&mapped.body);
662 let new = LogLines::parse(bytes);
663 if fold_lines(name, &new, Fold::WHOLE, &blob)?.is_none() {
664 return Err(SessionError::Record {
665 log: name.clone(),
666 line: 1,
667 why: "the rewritten log is empty".to_owned(),
668 });
669 }
670 rewritten.logs.push(RewrittenLog {
671 log: name.clone(),
672 records: mapped.records,
673 });
674 replaced.push((lines.len(), new.prefix(u64::MAX).to_vec()));
675 }
676
677 for (name, _, old) in &logs {
679 if let Some((kept, cut)) = old.torn {
680 set_aside_tail(store, name, kept, cut)?;
681 }
682 }
683 for name in blob_names(&blobs)? {
684 put_blob(store, &name, &read_blob(&blobs, &name)?)?;
685 }
686 for (log, (len, bytes)) in rewritten.logs.iter().zip(&replaced) {
687 refused(&log.log, store.replace_log(&log.log, *len, bytes))?;
688 }
689 for name in store.files()? {
690 if !name.starts_with(BLOBS) && rewrite_file(store, &name, with)? {
691 rewritten.files.push(name);
692 }
693 }
694 rewritten.files.sort();
695 let mut named = BTreeSet::new();
696 for (name, lines, _) in &logs {
697 let log = rewritten.logs.iter().position(|log| &log.log == name);
698 let bytes = match log.and_then(|at| replaced.get(at)) {
699 Some((_, bytes)) => bytes.as_slice(),
700 None => lines.prefix(u64::MAX),
701 };
702 named_blobs(bytes, &mut named);
703 }
704 let mut stored = blob_names(store)?;
705 stored.sort();
706 for name in stored {
707 if !named.contains(&name) {
708 store.remove_file(&format!("{BLOBS}{name}"))?;
709 rewritten.blobs_removed.push(name);
710 }
711 }
712 Ok(rewritten)
713}
714
715#[cfg(test)]
716mod tests;