ralph/queue/loader/
read.rs1use super::validation::validate_loaded_queues;
17use crate::config::Resolved;
18use crate::contracts::QueueFile;
19use crate::queue::json_repair::attempt_json_repair;
20use crate::queue::validation::{self, ValidationWarning};
21use anyhow::{Context, Result};
22use std::path::Path;
23
24pub fn load_queue_or_default(path: &Path) -> Result<QueueFile> {
26 if !path.exists() {
27 return Ok(QueueFile::default());
28 }
29 load_queue(path)
30}
31
32pub fn load_queue(path: &Path) -> Result<QueueFile> {
34 let raw = std::fs::read_to_string(path)
35 .with_context(|| format!("read queue file {}", path.display()))?;
36 let queue = crate::jsonc::parse_jsonc::<QueueFile>(&raw, &format!("queue {}", path.display()))?;
37 Ok(queue)
38}
39
40pub fn load_queue_with_repair(path: &Path) -> Result<QueueFile> {
43 let raw = std::fs::read_to_string(path)
44 .with_context(|| format!("read queue file {}", path.display()))?;
45
46 match crate::jsonc::parse_jsonc::<QueueFile>(&raw, &format!("queue {}", path.display())) {
47 Ok(queue) => Ok(queue),
48 Err(parse_err) => {
49 log::warn!("Queue JSON parse error, attempting repair: {}", parse_err);
50
51 if let Some(repaired) = attempt_json_repair(&raw) {
52 match crate::jsonc::parse_jsonc::<QueueFile>(
53 &repaired,
54 &format!("repaired queue {}", path.display()),
55 ) {
56 Ok(queue) => {
57 log::info!("Successfully repaired queue JSON");
58 Ok(queue)
59 }
60 Err(repair_err) => Err(parse_err).with_context(|| {
61 format!(
62 "parse queue {} as JSON/JSONC (repair also failed: {})",
63 path.display(),
64 repair_err
65 )
66 })?,
67 }
68 } else {
69 Err(parse_err)
70 }
71 }
72 }
73}
74
75pub fn load_queue_with_repair_and_validate(
82 path: &Path,
83 done: Option<&crate::contracts::QueueFile>,
84 id_prefix: &str,
85 id_width: usize,
86 max_dependency_depth: u8,
87) -> Result<(QueueFile, Vec<ValidationWarning>)> {
88 let queue = load_queue_with_repair(path)?;
89
90 let warnings = if let Some(d) = done {
91 validation::validate_queue_set(&queue, Some(d), id_prefix, id_width, max_dependency_depth)
92 .with_context(|| format!("validate repaired queue {}", path.display()))?
93 } else {
94 validation::validate_queue(&queue, id_prefix, id_width)
95 .with_context(|| format!("validate repaired queue {}", path.display()))?;
96 Vec::new()
97 };
98
99 Ok((queue, warnings))
100}
101
102fn load_queue_set_with_repair(
103 resolved: &Resolved,
104 include_done: bool,
105) -> Result<(QueueFile, QueueFile, bool)> {
106 let queue_file = load_queue_with_repair(&resolved.queue_path)?;
107 let done_path_exists = resolved.done_path.exists();
108 let done_file = if done_path_exists {
109 load_queue_with_repair(&resolved.done_path)?
110 } else {
111 QueueFile::default()
112 };
113
114 let done_file = if include_done || done_path_exists {
115 done_file
116 } else {
117 QueueFile::default()
118 };
119
120 Ok((queue_file, done_file, done_path_exists))
121}
122
123pub fn load_and_validate_queues(
128 resolved: &Resolved,
129 include_done: bool,
130) -> Result<(QueueFile, Option<QueueFile>)> {
131 let (queue_file, done_for_validation, _done_path_exists) =
132 load_queue_set_with_repair(resolved, include_done)?;
133 validate_loaded_queues(resolved, &queue_file, &done_for_validation)?;
134
135 let done_file = if include_done {
136 Some(done_for_validation)
137 } else {
138 None
139 };
140
141 Ok((queue_file, done_file))
142}