Skip to content

Commit 731c877

Browse files
authored
Merge pull request #26 from SolverForge/codex/release-0.6.0-integration
fix(release): port logging, score parsing, and sf-config wiring
2 parents d85b2df + c0f6571 commit 731c877

File tree

15 files changed

+328
-36
lines changed

15 files changed

+328
-36
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/solverforge-cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ clap_complete = { workspace = true }
1717
dialoguer = { workspace = true }
1818
include_dir = { workspace = true }
1919
owo-colors = { workspace = true }
20+
serde = { version = "1", features = ["derive"] }
21+
serde_json = "1"
2022
strsim = { workspace = true }
2123
toml = { workspace = true }
2224

crates/solverforge-cli/src/commands/destroy.rs

Lines changed: 76 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub fn run_entity(name: &str, skip_confirm: bool) -> CliResult {
100100

101101
remove_from_domain_mod(&file_name)?;
102102
unwire_collection_from_solution(&entity.field_name, &entity.item_type, &domain.solution_type)?;
103+
crate::commands::sf_config::remove_entity(&snake)?;
103104

104105
output::print_remove(&format!("src/domain/{}.rs", file_name));
105106
output::print_update("src/domain/mod.rs");
@@ -154,6 +155,7 @@ pub fn run_fact(name: &str, skip_confirm: bool) -> CliResult {
154155

155156
remove_from_domain_mod(&file_name)?;
156157
unwire_collection_from_solution(&fact.field_name, &fact.item_type, &domain.solution_type)?;
158+
crate::commands::sf_config::remove_fact(&snake)?;
157159

158160
output::print_remove(&format!("src/domain/{}.rs", file_name));
159161
output::print_update("src/domain/mod.rs");
@@ -182,6 +184,7 @@ pub fn run_constraint(name: &str, skip_confirm: bool) -> CliResult {
182184
})?;
183185

184186
remove_constraint_from_mod(&snake)?;
187+
crate::commands::sf_config::remove_constraint(&snake)?;
185188

186189
output::print_remove(&file_path);
187190
output::print_update("src/constraints/mod.rs");
@@ -200,15 +203,15 @@ fn remove_from_domain_mod(mod_name: &str) -> CliResult {
200203
})?;
201204

202205
let lines: Vec<&str> = content.lines().collect();
203-
let mut new_lines = Vec::new();
206+
let mut new_lines: Vec<String> = Vec::new();
204207

205208
for line in lines {
206209
if line.trim() == format!("mod {};", mod_name)
207210
|| line.trim().starts_with(&format!("pub use {}::", mod_name))
208211
{
209212
continue;
210213
}
211-
new_lines.push(line);
214+
new_lines.push(line.to_string());
212215
}
213216

214217
let new_content = new_lines.join("\n");
@@ -283,35 +286,21 @@ fn remove_constraint_from_mod(name: &str) -> CliResult {
283286
})?;
284287

285288
let lines: Vec<&str> = content.lines().collect();
286-
let mut new_lines = Vec::new();
287-
let mut in_tuple = false;
288-
let mut removed_item = false;
289+
let mut new_lines: Vec<String> = Vec::new();
289290

290291
for line in lines {
291292
if line.trim() == format!("mod {};", name) {
292293
continue;
293294
}
294295

295-
if line.contains("pub fn all() ->") || line.contains("impl Constraint") {
296-
in_tuple = true;
297-
new_lines.push(line);
298-
} else if in_tuple && line.contains(')') {
299-
if removed_item && !new_lines.is_empty() {
300-
let last_idx = new_lines.len() - 1;
301-
if let Some(last) = new_lines.get_mut(last_idx) {
302-
if last.trim().ends_with(',') {
303-
*last = last.trim().trim_end_matches(',');
304-
}
305-
}
296+
if let Some(updated_line) = remove_constraint_call_from_line(line, name) {
297+
if updated_line.trim().is_empty() {
298+
continue;
306299
}
307-
in_tuple = false;
308-
new_lines.push(line);
309-
} else if in_tuple && line.contains(&format!("{}::", name)) {
310-
removed_item = true;
300+
new_lines.push(updated_line);
311301
continue;
312-
} else {
313-
new_lines.push(line);
314302
}
303+
new_lines.push(line.to_string());
315304
}
316305

317306
let result = new_lines.join("\n");
@@ -323,3 +312,68 @@ fn remove_constraint_from_mod(name: &str) -> CliResult {
323312

324313
Ok(())
325314
}
315+
316+
fn remove_constraint_call_from_line(line: &str, name: &str) -> Option<String> {
317+
let needle = format!("{name}::constraint()");
318+
if !line.contains(&needle) {
319+
return None;
320+
}
321+
322+
let indent: String = line.chars().take_while(|c| c.is_whitespace()).collect();
323+
let trimmed = line.trim();
324+
let had_trailing_comma = trimmed.ends_with(',');
325+
let without_trailing_comma = trimmed.trim_end_matches(',');
326+
let has_tuple_wrapper =
327+
without_trailing_comma.starts_with('(') && without_trailing_comma.ends_with(')');
328+
let inner = if has_tuple_wrapper {
329+
&without_trailing_comma[1..without_trailing_comma.len() - 1]
330+
} else {
331+
without_trailing_comma
332+
};
333+
334+
let kept_parts: Vec<&str> = inner
335+
.split(',')
336+
.map(str::trim)
337+
.filter(|part| !part.is_empty() && *part != needle)
338+
.collect();
339+
340+
if kept_parts.is_empty() {
341+
return Some(String::new());
342+
}
343+
344+
let mut rebuilt = if has_tuple_wrapper {
345+
format!("({})", kept_parts.join(", "))
346+
} else {
347+
kept_parts.join(", ")
348+
};
349+
350+
if had_trailing_comma {
351+
rebuilt.push(',');
352+
}
353+
354+
Some(format!("{indent}{rebuilt}"))
355+
}
356+
357+
#[cfg(test)]
358+
mod tests {
359+
use super::remove_constraint_call_from_line;
360+
361+
#[test]
362+
fn removes_constraint_from_multiline_tuple_entry() {
363+
let line = " all_assigned::constraint(),";
364+
let updated = remove_constraint_call_from_line(line, "all_assigned")
365+
.expect("line should be rewritten");
366+
assert!(updated.is_empty());
367+
}
368+
369+
#[test]
370+
fn removes_constraint_from_flat_tuple_line() {
371+
let line = " (capacity::constraint(), extra::constraint(), distance::constraint())";
372+
let updated =
373+
remove_constraint_call_from_line(line, "extra").expect("line should be rewritten");
374+
assert_eq!(
375+
updated,
376+
" (capacity::constraint(), distance::constraint())"
377+
);
378+
}
379+
}

crates/solverforge-cli/src/commands/generate_constraint/run.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ pub fn run(
108108
source: e,
109109
})?;
110110

111+
crate::commands::sf_config::add_constraint(name)?;
112+
111113
output::print_create(&format!("src/constraints/{}.rs", name));
112114
print_diff_verbose("", &skeleton);
113115
output::print_update("src/constraints/mod.rs");

crates/solverforge-cli/src/commands/generate_domain/run.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ pub fn run_entity(
5454

5555
update_domain_mod(name, &pascal)?;
5656
wire_collection_into_solution(name, &pascal, "planning_entity_collection")?;
57+
let plural = super::utils::pluralize(name);
58+
crate::commands::sf_config::add_entity(name, &pascal, &plural)?;
5759

5860
output::print_create(file_path.to_str().unwrap());
5961
print_diff_verbose("", &src);
@@ -94,6 +96,8 @@ pub fn run_fact(name: &str, fields: &[String], force: bool, pretend: bool) -> Cl
9496

9597
update_domain_mod(name, &pascal)?;
9698
wire_collection_into_solution(name, &pascal, "problem_fact_collection")?;
99+
let plural = super::utils::pluralize(name);
100+
crate::commands::sf_config::add_fact(name, &pascal, &plural)?;
97101

98102
output::print_create(file_path.to_str().unwrap());
99103
print_diff_verbose("", &src);

crates/solverforge-cli/src/commands/generate_domain/tests.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::{
44
utils::{pluralize, snake_to_pascal, validate_score_type},
55
wiring::{add_import, replace_score_type},
66
};
7-
use std::sync::{Mutex, OnceLock};
7+
use crate::test_support;
88

99
#[test]
1010
fn test_snake_to_pascal() {
@@ -151,12 +151,7 @@ fn test_generate_data_loader_stub_is_compile_safe() {
151151

152152
#[test]
153153
fn test_remove_default_scaffold_rewrites_data_module_without_todo() {
154-
static CWD_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
155-
156-
let _guard = CWD_LOCK
157-
.get_or_init(|| Mutex::new(()))
158-
.lock()
159-
.expect("cwd lock poisoned");
154+
let guard = test_support::lock_cwd();
160155

161156
let tmp = tempfile::tempdir().expect("failed to create temp dir");
162157
let original_dir = std::env::current_dir().expect("failed to get current dir");
@@ -194,6 +189,7 @@ fn test_remove_default_scaffold_rewrites_data_module_without_todo() {
194189
std::env::set_current_dir(tmp.path()).expect("failed to enter temp dir");
195190
let result = remove_default_scaffold();
196191
std::env::set_current_dir(original_dir).expect("failed to restore current dir");
192+
drop(guard);
197193

198194
result.expect("remove_default_scaffold should succeed");
199195

crates/solverforge-cli/src/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ pub mod info;
99
pub mod new;
1010
pub mod routes;
1111
pub mod server;
12+
pub mod sf_config;
1213
pub mod test;

0 commit comments

Comments
 (0)