use std::path::Path;
use crate::config::Config;
use crate::error::{Error, Result};
use crate::project::ProjectLayout;
use crate::store::Store;
use crate::store::hierarchy::Hierarchy;
use crate::store::node::NodeKind;
use crate::store::InsertPosition;
pub fn run(
project: &Path,
kind: NodeKind,
title: &str,
parent_path: Option<&str>,
slug_override: Option<&str>,
after_path: Option<&str>,
) -> Result<()> {
let layout = ProjectLayout::new(project);
layout.require_initialized()?;
let cfg = Config::load(&layout.config_path())?;
let store = Store::open(layout.clone(), &cfg)?;
let hierarchy = Hierarchy::load(&store)?;
let (parent, position) = if let Some(after) = after_path {
let anchor = hierarchy
.find_by_path(after)
.ok_or_else(|| Error::Store(format!("--after anchor not found: `{after}`")))?;
if anchor.kind != kind {
return Err(Error::Store(format!(
"--after expects a sibling of kind `{}`, got `{}`",
kind.as_str(),
anchor.kind.as_str()
)));
}
let parent_node = anchor
.parent_id
.and_then(|pid| hierarchy.get(pid));
(parent_node, InsertPosition::After(anchor.id))
} else {
let parent = match (kind, parent_path) {
(NodeKind::Book, None) => None,
(NodeKind::Book, Some(_)) => {
return Err(Error::Store(
"books are root nodes; do not pass --parent".into(),
));
}
(_, None) => {
return Err(Error::Store(format!(
"--parent is required when adding a {}",
kind.as_str()
)));
}
(_, Some(path)) => Some(
hierarchy
.find_by_path(path)
.ok_or_else(|| Error::Store(format!("parent not found: `{path}`")))?,
),
};
let position = if kind == NodeKind::Book && parent.is_none() {
match hierarchy.iter().find(|n| {
n.kind == NodeKind::Book
&& n.system_tag.as_deref() == Some(crate::store::SYSTEM_TAG_NOTES)
}) {
Some(notes) => InsertPosition::Before(notes.id),
None => InsertPosition::End,
}
} else {
InsertPosition::End
};
(parent, position)
};
let node = store.create_node(
&cfg,
&hierarchy,
kind,
title,
parent,
slug_override,
position,
)?;
store.provision_user_book(&cfg, &node)?;
let display_path = {
let mut parts: Vec<String> = node.path.clone();
parts.push(node.slug.clone());
parts.join("/")
};
let rel = if let Some(file) = &node.file {
file.clone()
} else {
let mut p = std::path::PathBuf::from(crate::project::BOOKS_DIR);
for slug in &node.path {
if let Some(ancestor) = hierarchy
.iter()
.find(|n| n.slug == *slug && n.kind != NodeKind::Paragraph)
{
p.push(ancestor.fs_name());
} else {
p.push(slug);
}
}
p.push(node.fs_name());
p.to_string_lossy().into_owned()
};
eprintln!(
"Added {} `{}` at {}",
kind.as_str(),
node.title,
display_path
);
eprintln!(" uuid: {}", node.id);
eprintln!(" file: {}", rel);
Ok(())
}