diff --git a/contrib/tools/config-docs-generator/generate-config-docs.sh b/contrib/tools/config-docs-generator/generate-config-docs.sh index 05fe08f087e..dea9459ac1c 100755 --- a/contrib/tools/config-docs-generator/generate-config-docs.sh +++ b/contrib/tools/config-docs-generator/generate-config-docs.sh @@ -57,12 +57,6 @@ main() { cd "$PROJECT_ROOT" - # Workaround for new nightly lint that breaks stacks-common build. - # Allow callers to override or extend, but default to allowing the lint so documentation generation - # stays green until codebase is updated. - # TODO: Remove this once codebase will be updated to use the new lifetime syntax. - export RUSTFLAGS="${RUSTFLAGS:-} -A mismatched-lifetime-syntaxes" - # Step 1: Build the documentation generation tools if [[ "$SKIP_BUILD" != "true" ]]; then log_info "Building documentation generation tools..." diff --git a/contrib/tools/config-docs-generator/src/extract_docs.rs b/contrib/tools/config-docs-generator/src/extract_docs.rs index 689b7a7facb..ec6e9a6344a 100644 --- a/contrib/tools/config-docs-generator/src/extract_docs.rs +++ b/contrib/tools/config-docs-generator/src/extract_docs.rs @@ -134,7 +134,7 @@ fn main() -> Result<()> { // Write the extracted docs to file fs::write(output_file, serde_json::to_string_pretty(&config_docs)?)?; - println!("Successfully extracted documentation to {}", output_file); + println!("Successfully extracted documentation to {output_file}"); println!( "Found {} structs with documentation", config_docs.structs.len() @@ -183,10 +183,7 @@ fn generate_rustdoc_json(package: &str) -> Result { // Generate rustdoc for additional crates that might contain referenced constants for additional_crate in &additional_crates { - let error_msg = format!( - "Failed to run cargo rustdoc command for {}", - additional_crate - ); + let error_msg = format!("Failed to run cargo rustdoc command for {additional_crate}"); let output = StdCommand::new("cargo") .args([ "+nightly", @@ -208,10 +205,7 @@ fn generate_rustdoc_json(package: &str) -> Result { if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - eprintln!( - "Warning: Failed to generate rustdoc for {}: {}", - additional_crate, stderr - ); + eprintln!("Warning: Failed to generate rustdoc for {additional_crate}: {stderr}"); } } @@ -225,7 +219,7 @@ fn generate_rustdoc_json(package: &str) -> Result { }; // Read the generated JSON file - rustdoc generates it based on library name - let json_file_path = format!("{}/doc/{}.json", rustdoc_target_dir, lib_name); + let json_file_path = format!("{rustdoc_target_dir}/doc/{lib_name}.json"); let json_content = std::fs::read_to_string(json_file_path) .context("Failed to read generated rustdoc JSON file")?; @@ -249,10 +243,10 @@ fn extract_config_docs_from_rustdoc( // Check if this item is a struct by looking for the "struct" field if get_json_object(item, &["inner", "struct"]).is_some() { // Check if this struct is in our target list (if specified) - if let Some(targets) = target_structs { - if !targets.contains(&name.to_string()) { - continue; - } + if let Some(targets) = target_structs + && !targets.contains(&name.to_string()) + { + continue; } let (struct_doc_opt, referenced_constants) = @@ -464,8 +458,7 @@ fn parse_field_documentation( "" => false, // Empty string defaults to false text => text.parse::().unwrap_or_else(|_| { eprintln!( - "Warning: Invalid @required value '{}' for field '{}', defaulting to false", - text, field_name + "Warning: Invalid @required value '{text}' for field '{field_name}', defaulting to false" ); false }), @@ -632,14 +625,14 @@ fn parse_folded_block_scalar(lines: &[&str], _base_indent: usize) -> String { // Remove trailing empty lines but preserve a single trailing newline if content exists let trimmed = result.trim_end_matches('\n'); if !trimmed.is_empty() && result.ends_with('\n') { - format!("{}\n", trimmed) + format!("{trimmed}\n") } else { trimmed.to_string() } } fn extract_annotation(metadata_section: &str, annotation_name: &str) -> Option { - let annotation_pattern = format!("@{}:", annotation_name); + let annotation_pattern = format!("@{annotation_name}:"); if let Some(_start_pos) = metadata_section.find(&annotation_pattern) { // Split the metadata section into lines for processing @@ -820,15 +813,13 @@ fn resolve_constant_reference( let additional_crate_libs = ["stacks_common"]; // Library names for additional crates for lib_name in &additional_crate_libs { - let json_file_path = format!("target/rustdoc-json/doc/{}.json", lib_name); - if let Ok(json_content) = std::fs::read_to_string(&json_file_path) { - if let Ok(rustdoc_json) = serde_json::from_str::(&json_content) { - if let Some(index) = get_json_object(&rustdoc_json, &["index"]) { - if let Some(value) = resolve_constant_in_index(name, index) { - return Some(value); - } - } - } + let json_file_path = format!("target/rustdoc-json/doc/{lib_name}.json"); + if let Ok(json_content) = std::fs::read_to_string(&json_file_path) + && let Ok(rustdoc_json) = serde_json::from_str::(&json_content) + && let Some(index) = get_json_object(&rustdoc_json, &["index"]) + && let Some(value) = resolve_constant_in_index(name, index) + { + return Some(value); } } @@ -842,61 +833,57 @@ fn resolve_constant_in_index( // Look for a constant with the given name in the rustdoc index for (_item_id, item) in rustdoc_index { // Check if this item's name matches the constant we're looking for - if let Some(item_name) = get_json_string(item, &["name"]) { - if item_name == name { - // Check if this item is a constant by looking for the "constant" field - if let Some(constant_data) = get_json_object(item, &["inner", "constant"]) { - // Try newer rustdoc JSON structure first (with nested 'const' field) - let constant_data_value = serde_json::Value::Object(constant_data.clone()); - if get_json_object(&constant_data_value, &["const"]).is_some() { - // For literal constants, prefer expr which doesn't have type suffix - if get_json_path(&constant_data_value, &["const", "is_literal"]) - .and_then(|v| v.as_bool()) - == Some(true) - { - // Access the expression field for literal constant values - if let Some(expr) = - get_json_string(&constant_data_value, &["const", "expr"]) - { - if expr != "_" { - return Some(expr.to_string()); - } - } - } - - // For computed constants or when expr is "_", use value but strip type suffix - if let Some(value) = - get_json_string(&constant_data_value, &["const", "value"]) - { - return Some(strip_type_suffix(value)); - } - - // Fallback to expr if value is not available + if let Some(item_name) = get_json_string(item, &["name"]) + && item_name == name + { + // Check if this item is a constant by looking for the "constant" field + if let Some(constant_data) = get_json_object(item, &["inner", "constant"]) { + // Try newer rustdoc JSON structure first (with nested 'const' field) + let constant_data_value = serde_json::Value::Object(constant_data.clone()); + if get_json_object(&constant_data_value, &["const"]).is_some() { + // For literal constants, prefer expr which doesn't have type suffix + if get_json_path(&constant_data_value, &["const", "is_literal"]) + .and_then(|v| v.as_bool()) + == Some(true) + { + // Access the expression field for literal constant values if let Some(expr) = get_json_string(&constant_data_value, &["const", "expr"]) + && expr != "_" { - if expr != "_" { - return Some(expr.to_string()); - } + return Some(expr.to_string()); } } - // Fall back to older rustdoc JSON structure for compatibility - if let Some(value) = get_json_string(&constant_data_value, &["value"]) { + // For computed constants or when expr is "_", use value but strip type suffix + if let Some(value) = get_json_string(&constant_data_value, &["const", "value"]) + { return Some(strip_type_suffix(value)); } - if let Some(expr) = get_json_string(&constant_data_value, &["expr"]) { - if expr != "_" { - return Some(expr.to_string()); - } - } - // For some constants, the value might be in the type field if it's a simple literal - if let Some(type_str) = get_json_string(&constant_data_value, &["type"]) { - // Handle simple numeric or string literals embedded in type - return Some(type_str.to_string()); + // Fallback to expr if value is not available + if let Some(expr) = get_json_string(&constant_data_value, &["const", "expr"]) + && expr != "_" + { + return Some(expr.to_string()); } } + + // Fall back to older rustdoc JSON structure for compatibility + if let Some(value) = get_json_string(&constant_data_value, &["value"]) { + return Some(strip_type_suffix(value)); + } + if let Some(expr) = get_json_string(&constant_data_value, &["expr"]) + && expr != "_" + { + return Some(expr.to_string()); + } + + // For some constants, the value might be in the type field if it's a simple literal + if let Some(type_str) = get_json_string(&constant_data_value, &["type"]) { + // Handle simple numeric or string literals embedded in type + return Some(type_str.to_string()); + } } } } diff --git a/contrib/tools/config-docs-generator/src/generate_markdown.rs b/contrib/tools/config-docs-generator/src/generate_markdown.rs index c0a06bc0fd3..435065da2df 100644 --- a/contrib/tools/config-docs-generator/src/generate_markdown.rs +++ b/contrib/tools/config-docs-generator/src/generate_markdown.rs @@ -103,7 +103,7 @@ fn main() -> Result<()> { let mappings_path = matches.get_one::("mappings").unwrap(); let input_content = fs::read_to_string(input_path) - .with_context(|| format!("Failed to read input JSON file: {}", input_path))?; + .with_context(|| format!("Failed to read input JSON file: {input_path}"))?; let config_docs: ConfigDocs = serde_json::from_str(&input_content).with_context(|| "Failed to parse input JSON")?; @@ -113,43 +113,32 @@ fn main() -> Result<()> { let markdown = generate_markdown(&config_docs, template_path, &custom_mappings)?; fs::write(output_path, markdown) - .with_context(|| format!("Failed to write output file: {}", output_path))?; + .with_context(|| format!("Failed to write output file: {output_path}"))?; - println!( - "Successfully generated Markdown documentation at {}", - output_path - ); + println!("Successfully generated Markdown documentation at {output_path}"); Ok(()) } fn load_section_name_mappings(mappings_file: &str) -> Result> { - let content = fs::read_to_string(mappings_file).with_context(|| { - format!( - "Failed to read section name mappings file: {}", - mappings_file - ) - })?; + let content = fs::read_to_string(mappings_file) + .with_context(|| format!("Failed to read section name mappings file: {mappings_file}"))?; - let mappings: HashMap = serde_json::from_str(&content).with_context(|| { - format!( - "Failed to parse section name mappings JSON: {}", - mappings_file - ) - })?; + let mappings: HashMap = serde_json::from_str(&content) + .with_context(|| format!("Failed to parse section name mappings JSON: {mappings_file}"))?; Ok(mappings) } fn load_template(template_path: &str) -> Result { fs::read_to_string(template_path) - .with_context(|| format!("Failed to read template file: {}", template_path)) + .with_context(|| format!("Failed to read template file: {template_path}")) } fn render_template(template: &str, variables: HashMap) -> String { let mut result = template.to_string(); for (key, value) in variables { - let placeholder = format!("{{{{{}}}}}", key); + let placeholder = format!("{{{{{key}}}}}"); result = result.replace(&placeholder, &value); } @@ -245,7 +234,7 @@ fn generate_struct_section( custom_mappings: &HashMap, ) -> Result<()> { let section_name = struct_to_section_name(&struct_doc.name, custom_mappings); - output.push_str(&format!("## {}\n\n", section_name)); + output.push_str(&format!("## {section_name}\n\n")); // Add struct description if available if let Some(description) = &struct_doc.description { @@ -365,7 +354,7 @@ fn generate_field_row( // Add deprecation warning if present if let Some(deprecated) = &field.deprecated { - description_parts.push(format!("

**⚠️ DEPRECATED:** {}", deprecated)); + description_parts.push(format!("

**⚠️ DEPRECATED:** {deprecated}")); } // Add TOML example if present @@ -385,8 +374,7 @@ fn generate_field_row( .replace('\n', " "); // Use HTML entity for newline to avoid
conversion let example_section = format!( - "

**Example:**
{}
", - escaped_example // HTML entities will be rendered as newlines by
+            "

**Example:**
{escaped_example}
" // HTML entities will be rendered as newlines by
         );
         description_parts.push(example_section);
     }
@@ -394,7 +382,7 @@ fn generate_field_row(
     // Add units information if present
     if let Some(units) = &field.units {
         let units_text = process_intralinks_with_context(units, global_context, struct_name);
-        description_parts.push(format!("

**Units:** {}", units_text)); + description_parts.push(format!("

**Units:** {units_text}")); } let description = if description_parts.is_empty() { @@ -513,11 +501,11 @@ fn process_reference( // Check if it's the same struct or different struct if ref_struct_name == current_struct_name { // Same struct: just show field name - return format!("[{}](#{}) ", field_name, anchor_id); + return format!("[{field_name}](#{anchor_id}) "); } else { // Different struct: show [config_section].field_name as a link let config_section = section_name.trim_start_matches('[').trim_end_matches(']'); - return format!("[[{}].{}](#{}) ", config_section, field_name, anchor_id); + return format!("[[{config_section}].{field_name}](#{anchor_id}) "); } } } @@ -540,11 +528,11 @@ fn process_reference( // Check if it's the same struct or different struct if field_struct_name == current_struct_name { // Same struct: just show field name - return format!("[{}](#{}) ", reference, anchor_id); + return format!("[{reference}](#{anchor_id}) "); } else { // Different struct: show [config_section].field_name as a link let config_section = section_name.trim_start_matches('[').trim_end_matches(']'); - return format!("[[{}].{}](#{}) ", config_section, reference, anchor_id); + return format!("[[{config_section}].{reference}](#{anchor_id}) "); } } } @@ -577,7 +565,7 @@ fn process_hierarchical_lists( let processed_content = process_intralinks_with_context(content, global_context, struct_name); - result.push(format!("{}{}", indent_html, processed_content)); + result.push(format!("{indent_html}{processed_content}")); } else { // Process intra-links in non-bullet lines too let processed_line = process_intralinks_with_context(line, global_context, struct_name);