Skip to main content

ferripfs_pinning/
lib.rs

1// Ported from: kubo/boxo/pinning/pinner
2// Kubo version: v0.39.0
3// Original: https://github.com/ipfs/kubo/tree/v0.39.0/boxo/pinning/pinner
4//
5// Original work: Copyright (c) Protocol Labs, Inc.
6// Port: Copyright (c) 2026 ferripfs contributors
7// SPDX-License-Identifier: MIT OR Apache-2.0
8
9//! Pinning system for ferripfs, ported from Kubo's boxo/pinning.
10//!
11//! Pins prevent blocks from being garbage collected. There are three types:
12//! - **Direct**: Only the root block is protected
13//! - **Recursive**: The root and all blocks it references are protected
14//! - **Indirect**: Created automatically when a block is referenced by a recursive pin
15
16mod pinner;
17mod store;
18
19pub use pinner::*;
20pub use store::*;
21
22use thiserror::Error;
23
24/// Pin mode (type of pin)
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
26pub enum PinMode {
27    /// Direct pin - only the root block is pinned
28    Direct,
29    /// Recursive pin - root and all referenced blocks are pinned
30    Recursive,
31    /// Indirect pin - block is referenced by a recursive pin
32    Indirect,
33}
34
35impl PinMode {
36    /// Parse pin mode from string
37    pub fn parse(s: &str) -> Option<Self> {
38        match s.to_lowercase().as_str() {
39            "direct" => Some(PinMode::Direct),
40            "recursive" => Some(PinMode::Recursive),
41            "indirect" => Some(PinMode::Indirect),
42            "all" => None, // Used for filtering, means all types
43            _ => None,
44        }
45    }
46
47    /// Convert to string
48    pub fn as_str(&self) -> &'static str {
49        match self {
50            PinMode::Direct => "direct",
51            PinMode::Recursive => "recursive",
52            PinMode::Indirect => "indirect",
53        }
54    }
55}
56
57impl std::fmt::Display for PinMode {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        write!(f, "{}", self.as_str())
60    }
61}
62
63/// Information about a pin
64#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
65pub struct PinInfo {
66    /// CID of the pinned block
67    pub cid: String,
68    /// Pin mode
69    pub mode: PinMode,
70    /// Optional name for the pin
71    pub name: Option<String>,
72}
73
74/// Pinning error type
75#[derive(Debug, Error)]
76pub enum PinError {
77    #[error("Block not found: {0}")]
78    BlockNotFound(String),
79
80    #[error("CID not pinned: {0}")]
81    NotPinned(String),
82
83    #[error("CID already pinned: {0}")]
84    AlreadyPinned(String),
85
86    #[error("Cannot unpin indirect pin directly: {0}")]
87    CannotUnpinIndirect(String),
88
89    #[error("Blockstore error: {0}")]
90    Blockstore(#[from] ferripfs_blockstore::BlockstoreError),
91
92    #[error("IO error: {0}")]
93    Io(#[from] std::io::Error),
94
95    #[error("JSON error: {0}")]
96    Json(#[from] serde_json::Error),
97
98    #[error("CID parse error: {0}")]
99    CidParse(String),
100
101    #[error("Pin verification failed: {0}")]
102    VerificationFailed(String),
103}
104
105/// Result type for pinning operations
106pub type PinResult<T> = Result<T, PinError>;
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_pin_mode_from_str() {
114        assert_eq!(PinMode::parse("direct"), Some(PinMode::Direct));
115        assert_eq!(PinMode::parse("recursive"), Some(PinMode::Recursive));
116        assert_eq!(PinMode::parse("indirect"), Some(PinMode::Indirect));
117        assert_eq!(PinMode::parse("all"), None);
118        assert_eq!(PinMode::parse("invalid"), None);
119    }
120
121    #[test]
122    fn test_pin_mode_display() {
123        assert_eq!(PinMode::Direct.to_string(), "direct");
124        assert_eq!(PinMode::Recursive.to_string(), "recursive");
125        assert_eq!(PinMode::Indirect.to_string(), "indirect");
126    }
127}