@@ -22527,6 +22527,10 @@ pub extern "C-unwind" fn knot_api_register(
2252722527
2252822528static DOC_REGISTRY: Mutex<Vec<(String, String)>> = Mutex::new(Vec::new());
2252922529
22530+/// The `with { ... }` block's source text, embedded by the compiler so the
22531+/// `dump` subcommand prints the exact declarations.
22532+static SCHEMA_SOURCE: Mutex<Option<String>> = Mutex::new(None);
22533+
2253022534#[unsafe(no_mangle)]
2253122535pub extern "C-unwind" fn knot_doc_add(
2253222536 name_ptr: *const u8,
@@ -22542,6 +22546,13 @@ pub extern "C-unwind" fn knot_doc_add(
2254222546 .push((name, md));
2254322547}
2254422548
22549+/// Embed the `with { ... }` block's source text (called by the generated main).
22550+#[unsafe(no_mangle)]
22551+pub extern "C-unwind" fn knot_schema_source_set(src_ptr: *const u8, src_len: usize) {
22552+ let src = unsafe { str_from_raw(src_ptr, src_len) }.to_string();
22553+ *SCHEMA_SOURCE.lock().unwrap_or_else(|e| e.into_inner()) = Some(src);
22554+}
22555+
2254522556/// Render one line of markdown with light terminal syntax highlighting.
2254622557/// Headings bold, code spans/fences cyan, bold/emphasis preserved via ANSI.
2254722558/// Plain lines pass through unchanged. Only used on a TTY.
@@ -22814,29 +22825,36 @@ fn dump_database(db_path: String) -> Result<String, String> {
2281422825 .collect::<Result<Vec<_>, _>>()
2281522826 .map_err(|e| format!("failed to read schema rows: {}", e))?;
2281622827
22817− let mut decls = String::new();
2281822828 let mut writes = String::new();
2281922829
22820− for (name, schema) in &sources {
22821− // Reconstruct the declaration. The descriptor is the knot-type schema:
22822− // a record (`{name Text age Int}`) or an ADT (`(Circle{radius Int} | ...)`).
22823− let type_name = capitalize(name);
22824− let trimmed = schema.trim();
22825− if trimmed.starts_with('(') {
22826− // An ADT: (Ctor{f T} | Ctor2 {}). Convert the descriptor's
22827− // `Ctor{f T}` to knot's `Ctor {f (T 1)}`.
22828− let ctors = schema_descriptor_to_adt_ctors(trimmed);
22829− decls.push_str(&format!("{} {}\n", type_name, ctors.join(" ")));
22830− } else {
22831− // A record.
22832− let fields = schema_descriptor_to_fields(trimmed);
22833− decls.push_str(&format!("{} {{{}}}\n", type_name, fields.join(" ")));
22830+ // The type name for each source: from the embedded source's `Rel Type
22831+ // *name` declaration (the exact name), else the capitalized source name.
22832+ let schema_source = SCHEMA_SOURCE
22833+ .lock()
22834+ .unwrap_or_else(|e| e.into_inner())
22835+ .clone();
22836+ let type_name_for = |name: &str| -> String {
22837+ if let Some(src) = &schema_source {
22838+ // Find the `Rel Type *name` declaration.
22839+ for line in src.lines() {
22840+ let line = line.trim();
22841+ if line.starts_with("Rel ") && line.ends_with(&format!("*{}", name)) {
22842+ let parts: Vec<&str> = line.split_whitespace().collect();
22843+ if parts.len() >= 3 {
22844+ return parts[1].to_string();
22845+ }
22846+ }
22847+ }
2283422848 }
22835− decls.push_str(&format!("Rel {} *{}\n", type_name, name));
22849+ capitalize(name)
22850+ };
2283622851
22852+ for (name, schema) in &sources {
2283722853 // Read the values from the table. For an ADT source, qualify the
2283822854 // constructor tags with the type name (the dump's `Circle` → the
22839− // knot-parseable `Shapes.Circle`).
22855+ // knot-parseable `Shape.Circle`).
22856+ let type_name = type_name_for(name);
22857+ let trimmed = schema.trim();
2284022858 let rows = read_source_rows(&conn, name, schema, None).0;
2284122859 let row_strs: Vec<String> = rows
2284222860 .iter()
@@ -22853,11 +22871,32 @@ fn dump_database(db_path: String) -> Result<String, String> {
2285322871 writes.push_str(&format!(" *{} = [{}]\n", name, row_strs.join(" ")));
2285422872 }
2285522873
22874+ // The declarations: the embedded `with { ... }` source (the exact text the
22875+ // program was compiled with) if available, else reconstruct from the
22876+ // descriptors.
22877+ let decls = schema_source.unwrap_or_else(|| {
22878+ // Fallback: reconstruct from the descriptors.
22879+ let mut d = String::new();
22880+ for (name, schema) in &sources {
22881+ let type_name = capitalize(name);
22882+ let trimmed = schema.trim();
22883+ if trimmed.starts_with('(') {
22884+ let ctors = schema_descriptor_to_adt_ctors(trimmed);
22885+ d.push_str(&format!("{} {}\n", type_name, ctors.join(" ")));
22886+ } else {
22887+ let fields = schema_descriptor_to_fields(trimmed);
22888+ d.push_str(&format!("{} {{{}}}\n", type_name, fields.join(" ")));
22889+ }
22890+ d.push_str(&format!("Rel {} *{}\n", type_name, name));
22891+ }
22892+ format!("{{\n{}}}", d)
22893+ });
22894+
2285622895 // The program: the declarations + the writes.
2285722896 let mut program = String::new();
22858− program.push_str("with {\n");
22897+ program.push_str("with ");
2285922898 program.push_str(&decls);
22860− program.push_str("}\n");
22899+ program.push('\n');
2286122900 program.push_str("(|\n");
2286222901 program.push_str(&writes);
2286322902 program.push_str(" yield {})\n");