pub(crate) mod channels;
pub(crate) mod config;
pub(crate) mod findings;
pub(crate) mod graph;
pub(crate) mod order;
pub(crate) mod partition;
pub(crate) mod render;
pub(crate) mod surface;
pub(crate) mod view;
use std::io::{self, Write};
use std::path::PathBuf;
use crate::listing::{Format, RenderOpts};
pub(crate) const NEXT_LIMIT_DEFAULT: usize = 20;
pub(crate) const SURVEY_LIMIT_DEFAULT: usize = 20;
fn root(path: Option<PathBuf>) -> anyhow::Result<std::path::PathBuf> {
crate::root::find(path, &crate::root::default_markers())
}
pub(crate) fn resolve_page_offset(
page: Option<usize>,
limit: usize,
offset: usize,
) -> anyhow::Result<usize> {
if page == Some(0) {
anyhow::bail!("--page must be >= 1");
}
if limit == 0 && page.is_some() {
anyhow::bail!("--page requires a positive --limit");
}
Ok(match page {
Some(p) => (p - 1) * limit,
None => offset,
})
}
#[expect(
clippy::too_many_arguments,
reason = "CLI surface: path+all+hide_blocked+format+json+render+limit+offset = 8 — each is independently meaningful, no natural sub-struct"
)]
pub(crate) fn run_survey(
path: Option<PathBuf>,
all: bool,
hide_blocked: bool,
format: Format,
json: bool,
render: RenderOpts,
limit: usize,
offset: usize,
) -> anyhow::Result<()> {
let root = root(path)?;
let rows = surface::survey(&root, all, hide_blocked)?;
let out = if json || format == Format::Json {
render::survey_json(&rows)?
} else {
render::survey_human(&rows, render, limit, offset)
};
write!(io::stdout(), "{out}")?;
Ok(())
}
pub(crate) fn run_next(
path: Option<PathBuf>,
format: Format,
json: bool,
render: RenderOpts,
columns: Option<&Vec<String>>,
limit: usize,
offset: usize,
) -> anyhow::Result<()> {
let root = root(path)?;
let rows = surface::next(&root)?;
let out = if json || format == Format::Json {
render::next_json(&rows)?
} else {
render::next_human(&rows, render, columns.map(Vec::as_slice), limit, offset)?
};
write!(io::stdout(), "{out}")?;
Ok(())
}
pub(crate) fn run_blockers(
path: Option<PathBuf>,
id: &str,
transitive: bool,
format: Format,
json: bool,
_render: RenderOpts,
) -> anyhow::Result<()> {
let root = root(path)?;
let view = surface::blockers(&root, id, transitive)?;
let out = if json || format == Format::Json {
render::blockers_json(&view)?
} else {
render::blockers_human(&view)
};
write!(io::stdout(), "{out}")?;
Ok(())
}
pub(crate) fn run_findings(
path: Option<PathBuf>,
format: Format,
json: bool,
_render: RenderOpts,
) -> anyhow::Result<()> {
let root = root(path)?;
let findings = surface::findings(&root)?;
let out = if json || format == Format::Json {
render::findings_json(&findings)?
} else {
render::findings_human(&findings)
};
write!(io::stdout(), "{out}")?;
Ok(())
}
pub(crate) fn run_explain(
path: Option<PathBuf>,
id: &str,
format: Format,
json: bool,
_render: RenderOpts,
) -> anyhow::Result<()> {
let root = root(path)?;
let ex = surface::explain(&root, id)?;
let out = if json || format == Format::Json {
render::explain_json(&ex)?
} else {
render::explain_human(&ex)
};
write!(io::stdout(), "{out}")?;
Ok(())
}