use crate::cli::{InspectTarget, OutputFormat};
use crate::error::CommandError;
use crate::metadata::{FieldShapeEntry, ModuleEntry, RouteEntry, Uag};
use crate::project;
use serde::Serialize;
pub(crate) fn execute(target: InspectTarget) -> Result<(), CommandError> {
let project = project::discover()?;
let artifact = crate::metadata::load(&project).map_err(CommandError::Metadata)?;
match target {
InspectTarget::App(format) => print_app(&artifact, format),
InspectTarget::Route { name, format } => print_route(&artifact, &name, format),
InspectTarget::Module { name, format } => print_module(&artifact, &name, format),
}
}
fn print_app(artifact: &Uag, format: OutputFormat) -> Result<(), CommandError> {
match format {
OutputFormat::Human => {
println!(
"Application: {} modules, {} routes, {} services",
artifact.modules.len(),
artifact.routes.len(),
artifact.services.len(),
);
for module in artifact.modules.values() {
println!(
" {} — {} controllers, {} services, {} routes",
module.name,
module.controllers.len(),
module.services.len(),
module.routes.len(),
);
}
}
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(artifact)?);
}
}
Ok(())
}
fn print_route(artifact: &Uag, name: &str, format: OutputFormat) -> Result<(), CommandError> {
let route: Vec<&RouteEntry> = artifact.routes.iter().filter(|r| r.name == name).collect();
if route.is_empty() {
return Err(CommandError::Metadata(format!(
"no route named `{name}` in the application graph"
)));
}
match format {
OutputFormat::Human => {
for r in &route {
println!(
"{:<8} {:<20} {:<20} {}",
r.method.to_uppercase(),
r.path,
r.name,
r.handler,
);
print_route_detail(r);
}
}
OutputFormat::Json => {
#[derive(Serialize)]
struct Out<'a> {
routes: &'a [&'a RouteEntry],
}
println!("{}", serde_json::to_string_pretty(&Out { routes: &route })?,);
}
}
Ok(())
}
fn print_route_detail(route: &RouteEntry) {
if !route.pages.is_empty() {
println!(" pages: {}", route.pages.join(", "));
}
if !route.action_type.is_empty() {
println!(
" action: {type_name}",
type_name = route.action_type
);
if !route.action_fields.is_empty() {
println!(
" fields: {}",
field_shape_lines(&route.action_fields)
);
}
}
if !route.query_type.is_empty() {
let kind = if route.query_array {
"collection"
} else {
"single"
};
println!(
" query: {type_name} ({kind})",
type_name = route.query_type
);
if !route.query_fields.is_empty() {
println!(
" fields: {}",
field_shape_lines(&route.query_fields)
);
}
}
if !route.query_string_type.is_empty() {
println!(
" query-string: {type_name}",
type_name = route.query_string_type
);
if !route.query_string_fields.is_empty() {
println!(
" fields: {}",
field_shape_lines(&route.query_string_fields)
);
}
}
}
fn field_shape_lines(fields: &[FieldShapeEntry]) -> String {
fields
.iter()
.map(|f| {
if f.validates.is_empty() {
format!("{}: {}", f.name, f.ty)
} else {
format!("{}: {} [{}]", f.name, f.ty, f.validates.join(", "))
}
})
.collect::<Vec<_>>()
.join(", ")
}
fn print_module(artifact: &Uag, name: &str, format: OutputFormat) -> Result<(), CommandError> {
let module: Option<&ModuleEntry> = artifact.modules.values().find(|m| m.name == name);
let module = match module {
Some(m) => m,
None => {
return Err(CommandError::Metadata(format!(
"no module named `{name}` in the application graph"
)));
}
};
match format {
OutputFormat::Human => {
println!("Module: {}", module.name);
if !module.imports.is_empty() {
println!(" imports: {}", module.imports.join(", "));
}
if !module.exports.is_empty() {
println!(" exports: {}", module.exports.join(", "));
}
if !module.controllers.is_empty() {
println!(" controllers: {}", module.controllers.join(", "));
}
if !module.services.is_empty() {
println!(" services: {}", module.services.join(", "));
}
if !module.policies.is_empty() {
println!(" policies: {}", module.policies.join(", "));
}
if !module.routes.is_empty() {
println!(" routes:");
for r in &module.routes {
println!(" {:<8} {}", r.method.to_uppercase(), r.path);
}
}
if !module.listeners.is_empty() {
println!(" listeners:");
for l in &module.listeners {
println!(" {} -> {}", l.event, l.listener);
}
}
if !module.jobs.is_empty() {
println!(" jobs:");
for j in &module.jobs {
println!(" {} (v{}) -> {}", j.kind, j.version, j.handler);
}
}
if !module.commands.is_empty() {
println!(" commands:");
for c in &module.commands {
println!(" {} -> {}", c.name, c.function);
}
}
if !module.schedules.is_empty() {
println!(" schedules:");
for s in &module.schedules {
println!(" {} (v{}) {:?}", s.job, s.version, s.cadence);
}
}
}
OutputFormat::Json => {
println!("{}", serde_json::to_string_pretty(module)?);
}
}
Ok(())
}