photon-api 0.55.0

Solana indexer for general compression
Documentation
fn main() {
    #[cfg(feature = "generate")]
    generate();
}

#[cfg(feature = "generate")]
fn generate() {
    use std::{env, fs, path::PathBuf};

    let manifest_dir =
        PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
    let spec_path = manifest_dir.join("../../external/photon/src/openapi/specs/api.yaml");

    println!("cargo::rerun-if-changed={}", spec_path.display());

    // Read and parse the OpenAPI spec
    let spec_content = fs::read_to_string(&spec_path).expect("Failed to read OpenAPI spec");

    let mut spec: serde_yaml::Value =
        serde_yaml::from_str(&spec_content).expect("Failed to parse OpenAPI spec");

    // Add operationIds to each path's operation
    if let Some(paths) = spec.get_mut("paths").and_then(|p| p.as_mapping_mut()) {
        for (path, methods) in paths.iter_mut() {
            let path_str = path.as_str().unwrap_or("");
            let base_id = path_str.trim_start_matches('/');

            if let Some(methods_map) = methods.as_mapping_mut() {
                for (method, operation) in methods_map.iter_mut() {
                    if method.as_str() == Some("summary") {
                        continue;
                    }

                    if let Some(op_map) = operation.as_mapping_mut() {
                        let method_str = method.as_str().unwrap_or("get");
                        let operation_id = format!("{}_{}", method_str, to_snake_case(base_id));

                        op_map.insert(
                            serde_yaml::Value::String("operationId".to_string()),
                            serde_yaml::Value::String(operation_id),
                        );
                    }
                }
            }
        }
    }

    let modified_spec = serde_yaml::to_string(&spec).expect("Failed to serialize modified spec");

    // Parse for progenitor
    let spec: openapiv3::OpenAPI =
        serde_yaml::from_str(&modified_spec).expect("Failed to parse modified spec as OpenAPI");

    let mut settings = progenitor::GenerationSettings::default();
    settings.with_interface(progenitor::InterfaceStyle::Builder);

    let mut generator = progenitor::Generator::new(&settings);
    let tokens = generator
        .generate_tokens(&spec)
        .expect("Failed to generate client code");

    // Format the generated code
    let ast: syn::File = syn::parse2(tokens).expect("Failed to parse generated code");
    let content = prettyplease::unparse(&ast);

    // Write to src/codegen.rs (checked-in file)
    let dest_path = manifest_dir.join("src/codegen.rs");
    fs::write(&dest_path, &content).expect("Failed to write generated code");

    eprintln!("photon-api: regenerated src/codegen.rs from OpenAPI spec");
}

#[cfg(feature = "generate")]
fn to_snake_case(s: &str) -> String {
    let mut result = String::new();
    let mut prev_is_lower = false;

    for c in s.chars() {
        if c.is_uppercase() {
            if prev_is_lower {
                result.push('_');
            }
            result.extend(c.to_lowercase());
            prev_is_lower = false;
        } else {
            result.push(c);
            prev_is_lower = c.is_lowercase();
        }
    }

    result
}