use std::io::Write;
use crate::cli::{Cli, OutputMode};
use crate::error::{GroxError, Result};
use crate::resolve::{CrateSource, ProjectContext};
use crate::types::{DisplayItem, DisplayLimits, DocIndex, ItemKind, QueryResult};
use crate::{cli::FeatureFlags, query, reexport, render, types};
pub(crate) struct RenderContext<'a> {
pub index: &'a DocIndex,
pub limits: DisplayLimits,
pub mode: OutputMode,
pub kind_filter: Option<ItemKind>,
pub include_private: bool,
}
impl<'a> RenderContext<'a> {
pub fn from_cli(index: &'a DocIndex, cli: &Cli) -> Self {
Self {
index,
limits: DisplayLimits::default(),
mode: cli.output_mode(),
kind_filter: cli.kind.map(ItemKind::from),
include_private: cli.private,
}
}
}
pub(crate) fn render_recursive(
w: &mut impl Write,
ctx: &RenderContext<'_>,
idx: usize,
include_docs: bool,
) -> Result<()> {
let mut items = render::collect_children_recursive(ctx.index, idx, ctx.include_private);
if let Some(filter) = ctx.kind_filter {
items.retain(|item| item.kind.matches_filter(filter));
}
let root_path = &ctx.index.get(idx).path;
let output = match ctx.mode {
OutputMode::Json => render::json::render_json_recursive(&items),
OutputMode::Brief => render::brief::render_brief_recursive(&items, root_path),
OutputMode::Text if include_docs => render::docs::render_docs_recursive(&items, root_path),
OutputMode::Text => render::list::render_list_recursive(&items, root_path),
};
writeln!(w, "{output}").map_err(GroxError::Io)?;
Ok(())
}
pub(crate) fn render_recursive_source(
w: &mut impl Write,
ctx: &RenderContext<'_>,
idx: usize,
source: &CrateSource,
include_docs: bool,
) -> Result<()> {
let mut items = render::collect_children_recursive(ctx.index, idx, ctx.include_private);
if let Some(filter) = ctx.kind_filter {
items.retain(|item| item.kind.matches_filter(filter));
}
items.retain(|item| item.kind != ItemKind::Module);
let mut first = true;
for child in &items {
if !first {
writeln!(w).map_err(GroxError::Io)?;
writeln!(w, "────────────────────────────────────────").map_err(GroxError::Io)?;
writeln!(w).map_err(GroxError::Io)?;
}
first = false;
let content = crate::source::read_source_content(child, source);
let rendered = render::ambiguous::render_source(child, content.as_deref(), include_docs);
writeln!(w, "{rendered}").map_err(GroxError::Io)?;
}
Ok(())
}
pub(crate) fn handle_output(
w: &mut impl Write,
result: &QueryResult,
ctx: &RenderContext<'_>,
cli: &Cli,
project_ctx: Option<&ProjectContext>,
features: &FeatureFlags,
feature_suffix: &str,
) -> Result<()> {
match result {
QueryResult::Found { index: idx } => {
let item = ctx.index.get(*idx);
let (effective_index, effective_idx) = if query::is_reexport_stub(item) {
if let Some((source_index, canonical_idx)) = reexport::try_follow_reexport(
item,
project_ctx,
features,
feature_suffix,
ctx.include_private,
) {
(Some(source_index), canonical_idx)
} else {
(None, *idx)
}
} else {
(None, *idx)
};
let using_index = effective_index.as_ref().unwrap_or(ctx.index);
if cli.recursive {
let effective_ctx = RenderContext {
index: using_index,
limits: DisplayLimits::default(),
mode: ctx.mode,
kind_filter: ctx.kind_filter,
include_private: ctx.include_private,
};
return render_recursive(w, &effective_ctx, effective_idx, cli.docs);
}
let display = render::build_display_item(
using_index,
effective_idx,
ctx.include_private,
ctx.kind_filter,
);
if cli.impls || cli.impls_of.is_some() {
let trait_filter = cli.impls_of.as_deref();
let output = render_impls(&display, trait_filter);
writeln!(w, "{output}").map_err(GroxError::Io)?;
} else {
let output = match ctx.mode {
OutputMode::Json => render::json::render_json(&display),
OutputMode::Brief => render::brief::render_brief(&display),
OutputMode::Text => {
let reexport_info = if effective_index.is_some() {
let source_path =
reexport::parse_reexport_source(item).unwrap_or_default();
Some((item.path.clone(), source_path))
} else {
None
};
let info_refs = reexport_info
.as_ref()
.map(|(s, p)| (s.as_str(), p.as_str()));
render::text::render_text(&display, &ctx.limits, info_refs)
}
};
writeln!(w, "{output}").map_err(GroxError::Io)?;
}
Ok(())
}
QueryResult::Ambiguous { indices, query } => {
let output = match ctx.mode {
OutputMode::Json => {
let items: Vec<&types::IndexItem> =
indices.iter().map(|&i| ctx.index.get(i)).collect();
render::json::render_json_ambiguous(&items)
}
OutputMode::Brief | OutputMode::Text => {
render::ambiguous::render_ambiguous(ctx.index, indices, query)
}
};
writeln!(w, "{output}").map_err(GroxError::Io)?;
Ok(())
}
QueryResult::NotFound {
query, suggestions, ..
} => Err(GroxError::ItemNotFound {
query: query.clone(),
crate_name: Some(ctx.index.crate_name.clone()),
suggestions: suggestions.clone(),
}),
}
}
pub(crate) fn render_impls(display: &DisplayItem<'_>, trait_filter: Option<&str>) -> String {
match display {
DisplayItem::Type {
item, trait_impls, ..
} => render::ambiguous::render_impls_type(item, trait_impls, trait_filter),
DisplayItem::Trait { item, .. } => {
render::ambiguous::render_impls_trait(item, &[])
}
DisplayItem::Crate { item, .. }
| DisplayItem::Module { item, .. }
| DisplayItem::Leaf { item } => render::ambiguous::render_impls_other(item),
}
}