doc-sync 0.1.2

A CLI to convert rust documentation to markdown files and then back to rust documentation.
use std::error::Error;
use std::path::PathBuf;

use rustdoc_types::{Crate, Id, ItemEnum, VariantKind};
use tracing::trace;
use xshell::Shell;

use super::handle_item::handle_item;
use super::item_enum_ext::ItemEnumExt;

pub fn iterate_children<'rustdoc>(
    sh: &Shell,
    json: &'rustdoc Crate,
    output_dir: &PathBuf,
    handled_ids: &mut Vec<&'rustdoc String>,
    parent_path: &Vec<String>,
    item: &'rustdoc ItemEnum,
) -> Result<(), Box<dyn Error>> {
    match item {
        ItemEnum::Module(item) => iterate_ids(
            sh,
            json,
            output_dir,
            handled_ids,
            parent_path,
            item.items.iter(),
        ),
        ItemEnum::ExternCrate { .. } => Ok(()),
        ItemEnum::Import(_) => Ok(()),

        ItemEnum::Union(item) => {
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.fields.iter(),
            )?;
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.impls.iter(),
            )
        }
        ItemEnum::Struct(item) => iterate_ids(
            sh,
            json,
            output_dir,
            handled_ids,
            parent_path,
            item.impls.iter(),
        ),
        ItemEnum::StructField(_) => Ok(()),
        ItemEnum::Enum(item) => {
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.variants.iter(),
            )?;
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.impls.iter(),
            )
        }
        ItemEnum::Variant(item) => match &item.kind {
            VariantKind::Plain => Ok(()),
            VariantKind::Tuple(fields) => iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                fields.iter().filter_map(|f| f.as_ref()),
            ),
            VariantKind::Struct { fields, .. } => iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                fields.iter(),
            ),
        },

        ItemEnum::Function(_) => Ok(()),

        ItemEnum::Trait(item) => {
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.items.iter(),
            )?;
            iterate_ids(
                sh,
                json,
                output_dir,
                handled_ids,
                parent_path,
                item.implementations.iter(),
            )
        }
        ItemEnum::TraitAlias(_) => Ok(()),
        ItemEnum::Impl(item) => iterate_ids(
            sh,
            json,
            output_dir,
            handled_ids,
            parent_path,
            item.items.iter(),
        ),

        ItemEnum::Typedef(_) => Ok(()),
        ItemEnum::OpaqueTy(_) => Ok(()),
        ItemEnum::Constant(_) => Ok(()),

        ItemEnum::Static(_) => Ok(()),

        ItemEnum::ForeignType => Ok(()),

        ItemEnum::Macro(_) => Ok(()),
        ItemEnum::ProcMacro(_) => Ok(()),

        ItemEnum::Primitive(_) => Ok(()),

        ItemEnum::AssocConst { .. } => Ok(()),
        ItemEnum::AssocType { .. } => Ok(()),
    }
}

fn iterate_ids<'rustdoc>(
    sh: &Shell,
    json: &'rustdoc Crate,
    output_dir: &PathBuf,
    handled_ids: &mut Vec<&'rustdoc String>,
    parent_path: &Vec<String>,
    ids: impl Iterator<Item = &'rustdoc Id>,
) -> Result<(), Box<dyn Error>> {
    for id in ids {
        let item = json.index.get(id).expect("rustdoc JSON output is invalid?");
        let kind = item.inner.to_item_kind();
        let Some(name) = item.name.to_owned() else {
            trace!(
                item_kind = debug(kind),
                span = debug(&item.span),
                parent = debug(parent_path),
                id = &id.0,
                "Skipping child item because it doesn't have a name"
            );
            continue;
        };
        let mut path = parent_path.clone();
        path.push(name);

        handle_item(sh, json, output_dir, handled_ids, id, item, &path, &kind)?;
    }

    Ok(())
}