commit b48ba649f0349b6a4bd60f5aba8ba2ac00d5f9f9
parent 6d684d1c56f5ff14f07ff3dc0c2c9e8dafad0098
Author: Andy Khramtsov <>
Date: Sat, 7 Feb 2026 13:18:34 +0300
feat: add inventory
Diffstat:
7 files changed, 84 insertions(+), 30 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -471,6 +471,7 @@ dependencies = [
"dialoguer",
"indexmap",
"serde",
+ "serde_json",
"test-log",
"thiserror",
"tokio",
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,6 +9,7 @@ config = "0.15.19"
dialoguer = "0.12.0"
indexmap = "2.13.0"
serde = { version = "1.0.228", features = ["derive"] }
+serde_json = "1.0.149"
test-log = { version = "0.2.19", features = ["trace"] }
thiserror = "2.0.17"
tokio = { version = "1.49.0", features = ["rt-multi-thread", "fs"] }
diff --git a/config.yaml b/config.yaml
@@ -13,6 +13,8 @@ filters:
type: blacklist
- path: my-space/projects/rust
type: whitelist
+ - path: my-space/projects/openscad
+ type: whitelist
- path: my-space/projects/rust/jannie
type: blacklist
- path: my-space/projects/rust/jannie/log
diff --git a/inventory.json b/inventory.json
@@ -0,0 +1,7 @@
+{
+ "items": [
+ {
+ "path": "/home/andy/my-space/projects/openscad/cable-holder"
+ }
+ ]
+}
diff --git a/src/inventory.rs b/src/inventory.rs
@@ -0,0 +1,11 @@
+use std::path::PathBuf;
+
+#[derive(serde::Deserialize, Debug)]
+pub struct Inventory {
+ pub items: Vec<Item>,
+}
+
+#[derive(serde::Deserialize, Debug)]
+pub struct Item {
+ pub path: PathBuf,
+}
diff --git a/src/lib.rs b/src/lib.rs
@@ -12,6 +12,7 @@ use crate::{
pub mod args;
pub mod config;
pub mod filetree;
+pub mod inventory;
pub mod logging;
pub mod state;
@@ -34,11 +35,18 @@ pub fn result_main() -> Result<(), Error> {
#[derive(Clone, Debug)]
struct Meta {
+ inventory: Option<bool>,
blacklist: Option<bool>,
}
async fn read(state: &State) {
- let root = Node::new(state.config.root.clone(), Meta { blacklist: None });
+ let root = Node::new(
+ state.config.root.clone(),
+ Meta {
+ blacklist: None,
+ inventory: None,
+ },
+ );
let mut filetree = Filetree::new(root);
let mut buffer = Vec::new();
@@ -68,6 +76,10 @@ async fn read(state: &State) {
}
let allowed = allowed(state, &dir);
+ let in_inventory = state
+ .inventory_tree
+ .node_by_path(&dir)
+ .is_some_and(|node| node.borrow().is_leaf());
if base != dir {
let mut suffix = dir.strip_prefix(&base).unwrap().to_owned();
@@ -76,7 +88,13 @@ async fn read(state: &State) {
for component in suffix.components() {
base.push(component);
filetree
- .insert(Node::new(base.clone(), Meta { blacklist: None }))
+ .insert(Node::new(
+ base.clone(),
+ Meta {
+ blacklist: None,
+ inventory: None,
+ },
+ ))
.unwrap();
}
filetree
@@ -84,6 +102,7 @@ async fn read(state: &State) {
dir.clone(),
Meta {
blacklist: Some(!allowed),
+ inventory: Some(in_inventory),
},
))
.unwrap();
@@ -158,28 +177,22 @@ fn print(tree: &Filetree<Meta>) -> Result<(), Error> {
print_buffer.push((0, tree.root().borrow().id()));
while let Some((offset, node_id)) = print_buffer.pop() {
let node = tree.node(node_id).expect("Shold have the node");
- match node.borrow().meta().blacklist {
- Some(true) => println!(
- "{}| {}, BL",
- " ".repeat(offset),
- node.borrow()
- .path()
- .file_name()
- .map(|name| name.to_str())
- .flatten()
- .unwrap_or("UNKNOWN")
- ),
- _ => println!(
- "{}| {}",
- " ".repeat(offset),
- node.borrow()
- .path()
- .file_name()
- .map(|name| name.to_str())
- .flatten()
- .unwrap_or("UNKNOWN")
- ),
- };
+ let name = node
+ .borrow()
+ .path()
+ .file_name()
+ .map(|name| name.to_str())
+ .flatten()
+ .unwrap_or("UNKNOWN")
+ .to_owned();
+ let offset_text = " ".repeat(offset);
+ if let Some(true) = node.borrow().meta().inventory {
+ println!("{}\x1b[2m|\x1b[0m \x1b[32m{}\x1b[0m", offset_text, name);
+ } else if let Some(true) = node.borrow().meta().blacklist {
+ println!("{}\x1b[2m|\x1b[0m \x1b[2;9m{}\x1b[0m", offset_text, name);
+ } else {
+ println!("{}\x1b[2m|\x1b[0m {}", offset_text, name);
+ }
print_buffer.extend(
node.borrow()
.children()
diff --git a/src/state.rs b/src/state.rs
@@ -5,12 +5,14 @@ use tokio::runtime::Runtime;
use crate::{
config::Config,
filetree::{self, Filetree, node::Node},
+ inventory::Inventory,
};
pub struct State {
pub config_dir: PathBuf,
pub config: Config,
pub runtime: Runtime,
+ pub inventory: Inventory,
pub blacklist_tree: Filetree<()>,
pub whitelist_tree: Filetree<()>,
pub inventory_tree: Filetree<()>,
@@ -18,7 +20,19 @@ pub struct State {
impl State {
pub fn new(config_dir: &Path, config: Config) -> Result<Self, Error> {
- // todo: read inventory
+ let runtime = tokio::runtime::Builder::new_current_thread()
+ .enable_all()
+ .build()
+ .map_err(Error::Runtime)?;
+
+ let inventory_path = config_dir.join("inventory.json");
+
+ let inventory = runtime
+ .block_on(tokio::fs::read(inventory_path))
+ .map_err(Error::ReadFile)?;
+ let inventory: Inventory =
+ serde_json::from_slice(inventory.as_slice()).map_err(Error::Json)?;
+
let mut blacklist_tree = Filetree::new(Node::new("/".into(), ()));
let mut whitelist_tree = Filetree::new(Node::new("/".into(), ()));
@@ -47,14 +61,15 @@ impl State {
}
}
- let inventory_tree = Filetree::new(Node::new("/".into(), ()));
+ let mut inventory_tree = Filetree::new(Node::new("/".into(), ()));
+ for item in &inventory.items {
+ add_to_tree(&mut inventory_tree, &item.path)?
+ }
Ok(Self {
config_dir: config_dir.into(),
config,
- runtime: tokio::runtime::Builder::new_current_thread()
- .enable_all()
- .build()
- .map_err(Error::Runtime)?,
+ runtime,
+ inventory,
blacklist_tree,
whitelist_tree,
inventory_tree,
@@ -87,6 +102,10 @@ fn add_to_tree(tree: &mut Filetree<()>, path: &Path) -> Result<(), Error> {
pub enum Error {
#[error("Error building runtime: {0}")]
Runtime(tokio::io::Error),
+ #[error("Error reading file: {0}")]
+ ReadFile(std::io::Error),
+ #[error("Error parsing json: {0}")]
+ Json(serde_json::Error),
#[error("Error building filetree: {0}")]
Filetree(#[from] filetree::Error),
}