Skip to content

Commit 70de978

Browse files
authored
Merge pull request #27 from SolverForge/codex/release-0.6.0-scaffold-salvage
feat(cli): salvage generic scaffold refresh for review
2 parents 731c877 + 1279239 commit 70de978

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2248
-189
lines changed

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ static BASIC_GENERIC_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates
1111
static EMPLOYEE_SCHEDULING_TEMPLATE: Dir =
1212
include_dir!("$CARGO_MANIFEST_DIR/templates/basic/employee-scheduling");
1313

14+
static LIST_GENERIC_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/list/generic");
15+
1416
static VEHICLE_ROUTING_TEMPLATE: Dir =
1517
include_dir!("$CARGO_MANIFEST_DIR/templates/list/vehicle-routing");
1618

@@ -20,6 +22,7 @@ const AVAILABLE_TEMPLATES: &str = "
2022
--basic=employee-scheduling — assign employees to shifts
2123
2224
List Variable (each entity owns an ordered sequence):
25+
--list — generic list-variable skeleton
2326
--list=vehicle-routing — capacitated vehicle routing (CVRP)";
2427

2528
pub fn run(
@@ -53,6 +56,15 @@ pub fn run(
5356
skip_readme,
5457
quiet,
5558
),
59+
Template::List => scaffold(
60+
name,
61+
&crate_name,
62+
&LIST_GENERIC_TEMPLATE,
63+
"list",
64+
skip_git,
65+
skip_readme,
66+
quiet,
67+
),
5668
Template::ListVehicleRouting => scaffold(
5769
name,
5870
&crate_name,
@@ -269,6 +281,15 @@ fn print_template_guidance(project_name: &str, label: &str) {
269281
println!(" solverforge generate constraint all_assigned --unary --hard");
270282
println!(" solverforge server");
271283
}
284+
"list" => {
285+
println!(" solverforge server");
286+
println!();
287+
println!(" This template includes:");
288+
println!(" - 2-phase solver (cheapest insertion + late acceptance)");
289+
println!(" - Balanced load constraint (soft)");
290+
println!(" - Sequence view at http://localhost:7860");
291+
println!(" - REST API with SSE live updates");
292+
}
272293
_ => {
273294
println!(" solverforge server");
274295
}
@@ -345,6 +366,7 @@ fn generate_readme(project_name: &str, _crate_name: &str, label: &str) -> String
345366
pub enum Template {
346367
Basic,
347368
BasicEmployeeScheduling,
369+
List,
348370
ListVehicleRouting,
349371
}
350372

@@ -353,10 +375,7 @@ impl Template {
353375
match (basic, list, specialization) {
354376
(true, false, None) => Ok(Template::Basic),
355377
(true, false, Some("employee-scheduling")) => Ok(Template::BasicEmployeeScheduling),
356-
(false, true, None) => Err(CliError::with_hint(
357-
"the --list template requires a specialization",
358-
"Use --list=vehicle-routing".to_string(),
359-
)),
378+
(false, true, None) => Ok(Template::List),
360379
(false, true, Some("vehicle-routing")) => Ok(Template::ListVehicleRouting),
361380
(false, false, None) => Err(CliError::with_hint(
362381
"specify a template flag",

crates/solverforge-cli/src/main.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use error::CliResult;
1313

1414
const EXAMPLES: &str = "\x1b[1mExamples:\x1b[0m
1515
solverforge new my-scheduler --basic=employee-scheduling
16+
solverforge new my-planner --basic
17+
solverforge new my-sorter --list
1618
solverforge new my-router --list=vehicle-routing
1719
solverforge generate entity shift --planning-variable employee_idx
1820
solverforge generate constraint no_overlap --pair --hard
@@ -56,14 +58,14 @@ enum Command {
5658
/// Variable class (required, mutually exclusive):
5759
///
5860
/// --basic Standard variable — each entity holds one assigned value
59-
/// --list=... List variable — each entity owns an ordered sequence
61+
/// --list List variable — each entity owns an ordered sequence
6062
///
6163
/// Specializations (append after the flag with =):
6264
///
6365
/// --basic=employee-scheduling
6466
/// --list=vehicle-routing
6567
#[command(
66-
after_help = "Examples:\n solverforge new my-scheduler --basic=employee-scheduling\n solverforge new my-router --list=vehicle-routing\n solverforge new my-planner --basic"
68+
after_help = "Examples:\n solverforge new my-scheduler --basic=employee-scheduling\n solverforge new my-planner --basic\n solverforge new my-sorter --list\n solverforge new my-router --list=vehicle-routing"
6769
)]
6870
New {
6971
/// Project name (directory that will be created)
@@ -73,7 +75,7 @@ enum Command {
7375
#[arg(long = "basic", value_name = "SPECIALIZATION", num_args = 0..=1, require_equals = true)]
7476
basic: Option<Option<String>>,
7577

76-
/// Scaffold a list-variable project specialization (currently: --list=vehicle-routing)
78+
/// Scaffold a list-variable project (optionally: --list=vehicle-routing)
7779
#[arg(
7880
long = "list",
7981
value_name = "SPECIALIZATION",

crates/solverforge-cli/templates/basic/generic/Cargo.toml.tmpl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ name = "{{crate_name}}"
99
path = "src/main.rs"
1010

1111
[dependencies]
12-
solverforge = { version = "{{solverforge_version}}", features = ["serde"] }
12+
solverforge = { version = "{{solverforge_version}}", features = ["serde", "console", "verbose-logging"] }
13+
solverforge-ui = "0.2.0"
1314
# Web server
1415
axum = "0.8"
1516
tokio = { version = "1", features = ["full"] }
17+
tokio-stream = { version = "0.1", features = ["sync"] }
1618
tower-http = { version = "0.6", features = ["fs", "cors"] }
1719
tower = "0.5"
1820

@@ -23,7 +25,3 @@ serde_json = "1"
2325
# Utilities
2426
uuid = { version = "1", features = ["v4", "serde"] }
2527
parking_lot = "0.12"
26-
tracing = "0.1"
27-
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
28-
owo-colors = "4"
29-
num-format = "0.4"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,14 @@
1+
[[phases]]
2+
type = "construction_heuristic"
3+
construction_heuristic_type = "allocate_to_value_from_queue"
4+
5+
[[phases]]
6+
type = "local_search"
7+
[phases.acceptor]
8+
type = "late_acceptance"
9+
late_acceptance_size = 400
10+
[phases.forager]
11+
accepted_count_limit = 4
12+
113
[termination]
214
seconds_spent_limit = 30

crates/solverforge-cli/templates/basic/generic/src/api/dto.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,29 @@ use solverforge::SolverStatus;
88
pub struct ResourceDto {
99
pub index: usize,
1010
pub name: String,
11+
pub capacity: i64,
12+
pub affinity_group: String,
1113
}
1214

1315
impl From<&Resource> for ResourceDto {
1416
fn from(r: &Resource) -> Self {
15-
Self { index: r.index, name: r.name.clone() }
17+
Self {
18+
index: r.index,
19+
name: r.name.clone(),
20+
capacity: r.capacity,
21+
affinity_group: r.affinity_group.clone(),
22+
}
1623
}
1724
}
1825

1926
impl ResourceDto {
2027
pub fn to_resource(&self) -> Resource {
21-
Resource::new(self.index, &self.name)
28+
Resource::new(
29+
self.index,
30+
&self.name,
31+
self.capacity,
32+
&self.affinity_group,
33+
)
2234
}
2335
}
2436

@@ -27,6 +39,8 @@ impl ResourceDto {
2739
pub struct TaskDto {
2840
pub id: String,
2941
pub name: String,
42+
pub demand: i64,
43+
pub preferred_group: String,
3044
pub resource: Option<ResourceDto>,
3145
}
3246

@@ -41,6 +55,32 @@ pub struct PlanDto {
4155
pub solver_status: Option<SolverStatus>,
4256
}
4357

58+
/// Constraint analysis result.
59+
#[derive(Debug, Serialize)]
60+
#[serde(rename_all = "camelCase")]
61+
pub struct ConstraintAnalysisDto {
62+
pub name: String,
63+
#[serde(rename = "type")]
64+
pub constraint_type: String,
65+
pub weight: String,
66+
pub score: String,
67+
pub matches: Vec<ConstraintMatchDto>,
68+
}
69+
70+
#[derive(Debug, Serialize)]
71+
#[serde(rename_all = "camelCase")]
72+
pub struct ConstraintMatchDto {
73+
pub score: String,
74+
pub justification: String,
75+
}
76+
77+
#[derive(Debug, Serialize)]
78+
#[serde(rename_all = "camelCase")]
79+
pub struct AnalyzeResponse {
80+
pub score: String,
81+
pub constraints: Vec<ConstraintAnalysisDto>,
82+
}
83+
4484
impl PlanDto {
4585
pub fn from_plan(plan: &Plan, status: Option<SolverStatus>) -> Self {
4686
let resources: Vec<ResourceDto> = plan.resources.iter().map(ResourceDto::from).collect();
@@ -50,6 +90,8 @@ impl PlanDto {
5090
.map(|t| TaskDto {
5191
id: t.id.clone(),
5292
name: t.name.clone(),
93+
demand: t.demand,
94+
preferred_group: t.preferred_group.clone(),
5395
resource: t
5496
.resource_idx
5597
.and_then(|idx| plan.resources.get(idx))
@@ -75,6 +117,8 @@ impl PlanDto {
75117
.map(|t| Task {
76118
id: t.id.clone(),
77119
name: t.name.clone(),
120+
demand: t.demand,
121+
preferred_group: t.preferred_group.clone(),
78122
resource_idx: t
79123
.resource
80124
.as_ref()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod dto;
22
mod routes;
3+
mod sse;
34

45
pub use routes::{router, AppState};

0 commit comments

Comments
 (0)