Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
wisp.place
1use jacquard_common::types::blob::BlobRef;
2use jacquard_common::IntoStatic;
3use std::collections::HashMap;
4
5use crate::place_wisp::fs::{Directory, EntryNode};
6
7/// Extract blob information from a directory tree
8/// Returns a map of file paths to their blob refs and CIDs
9///
10/// This mirrors the TypeScript implementation in src/lib/wisp-utils.ts lines 275-302
11pub fn extract_blob_map(
12 directory: &Directory,
13) -> HashMap<String, (BlobRef<'static>, String)> {
14 extract_blob_map_recursive(directory, String::new())
15}
16
17fn extract_blob_map_recursive(
18 directory: &Directory,
19 current_path: String,
20) -> HashMap<String, (BlobRef<'static>, String)> {
21 let mut blob_map = HashMap::new();
22
23 for entry in &directory.entries {
24 let full_path = if current_path.is_empty() {
25 entry.name.to_string()
26 } else {
27 format!("{}/{}", current_path, entry.name)
28 };
29
30 match &entry.node {
31 EntryNode::File(file_node) => {
32 // Extract CID from blob ref
33 // BlobRef is an enum with Blob variant, which has a ref field (CidLink)
34 let blob_ref = &file_node.blob;
35 let cid_string = blob_ref.blob().r#ref.to_string();
36
37 // Store with full path (mirrors TypeScript implementation)
38 blob_map.insert(
39 full_path,
40 (blob_ref.clone().into_static(), cid_string)
41 );
42 }
43 EntryNode::Directory(subdir) => {
44 let sub_map = extract_blob_map_recursive(subdir, full_path);
45 blob_map.extend(sub_map);
46 }
47 EntryNode::Subfs(_) => {
48 // Subfs nodes don't contain blobs directly - they reference other records
49 // Skip them in blob map extraction
50 }
51 EntryNode::Unknown(_) => {
52 // Skip unknown node types
53 }
54 }
55 }
56
57 blob_map
58}
59
60/// Normalize file path by removing base folder prefix
61/// Example: "cobblemon/index.html" -> "index.html"
62///
63/// Note: This function is kept for reference but is no longer used in production code.
64/// The TypeScript server has a similar normalization (src/routes/wisp.ts line 291) to handle
65/// uploads that include a base folder prefix, but our CLI doesn't need this since we
66/// track full paths consistently.
67#[allow(dead_code)]
68pub fn normalize_path(path: &str) -> String {
69 // Remove base folder prefix (everything before first /)
70 if let Some(idx) = path.find('/') {
71 path[idx + 1..].to_string()
72 } else {
73 path.to_string()
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_normalize_path() {
83 assert_eq!(normalize_path("index.html"), "index.html");
84 assert_eq!(normalize_path("cobblemon/index.html"), "index.html");
85 assert_eq!(normalize_path("folder/subfolder/file.txt"), "subfolder/file.txt");
86 assert_eq!(normalize_path("a/b/c/d.txt"), "b/c/d.txt");
87 }
88}
89