1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! Filesystem browsing helpers.
use super::utils::normalize_path;
use crate::error::Result;
use crate::fs::node::Node;
use crate::session::Session;
impl Session {
/// List files in a directory.
///
/// # Arguments
/// * `path` - The path to list (e.g., "/", "/Documents")
/// * `recursive` - If true, list all descendants recursively
///
/// # Returns
/// Vector of nodes matching the path
pub fn list(&self, path: &str, recursive: bool) -> Result<Vec<&Node>> {
let normalized_path = normalize_path(path);
let search_prefix = if normalized_path == "/" {
"/".to_string()
} else {
format!("{}/", normalized_path)
};
let mut results = Vec::new();
for node in &self.nodes {
if let Some(node_path) = &node.path {
if recursive {
// Include all nodes under this path
if node_path.starts_with(&search_prefix) || node_path == &normalized_path {
if node_path != &normalized_path {
results.push(node);
}
}
} else {
// Include only direct children
if let Some(stripped) = node_path.strip_prefix(&search_prefix) {
if !stripped.contains('/') && !stripped.is_empty() {
results.push(node);
}
}
}
}
}
Ok(results)
}
/// List all contacts.
///
/// Returns all nodes of type Contact. Contacts are users who have
/// interacted with your shared files or folders.
///
/// # Example
/// ```no_run
/// # use megalib::Session;
/// # async fn example() -> megalib::error::Result<()> {
/// let mut session = Session::login("user@example.com", "password").await?;
/// session.refresh().await?;
/// for contact in session.list_contacts() {
/// println!("Contact: {}", contact.name);
/// }
/// # Ok(())
/// # }
/// ```
pub fn list_contacts(&self) -> Vec<&Node> {
self.nodes.iter().filter(|n| n.is_contact()).collect()
}
/// Get information about a file or folder.
///
/// # Arguments
/// * `path` - The path to stat
///
/// # Returns
/// Node information if found
pub fn stat(&self, path: &str) -> Option<&Node> {
let normalized_path = normalize_path(path);
self.nodes
.iter()
.find(|n| n.path.as_deref() == Some(&normalized_path))
}
/// Get a node by its handle.
///
/// # Arguments
/// * `handle` - The node handle (e.g., "ABC123xyz")
///
/// # Returns
/// Node reference if found
///
/// # Example
/// ```no_run
/// # use megalib::Session;
/// # async fn example() -> megalib::error::Result<()> {
/// let mut session = Session::login("user@example.com", "password").await?;
/// session.refresh().await?;
/// if let Some(node) = session.get_node_by_handle("ABC123") {
/// println!("Found: {}", node.name);
/// }
/// # Ok(())
/// # }
/// ```
pub fn get_node_by_handle(&self, handle: &str) -> Option<&Node> {
self.nodes.iter().find(|n| n.handle == handle)
}
/// Check if a node has a specific ancestor.
///
/// This walks up the parent chain to check if `ancestor` is in
/// the path from `node` to the root.
///
/// # Arguments
/// * `node` - The node to check
/// * `ancestor` - The potential ancestor node
///
/// # Returns
/// `true` if `ancestor` is in `node`'s parent chain
///
/// # Example
/// ```no_run
/// # use megalib::Session;
/// # async fn example() -> megalib::error::Result<()> {
/// let mut session = Session::login("user@example.com", "password").await?;
/// session.refresh().await?;
/// let file = session.stat("/Root/Documents/file.txt").unwrap();
/// let folder = session.stat("/Root/Documents").unwrap();
/// assert!(session.node_has_ancestor(file, folder));
/// # Ok(())
/// # }
/// ```
pub fn node_has_ancestor(&self, node: &Node, ancestor: &Node) -> bool {
let mut current_handle = node.parent_handle.as_ref();
// Walk up the tree (max 100 levels to prevent infinite loops)
for _ in 0..100 {
match current_handle {
Some(handle) if handle == &ancestor.handle => return true,
Some(handle) => {
if let Some(parent) = self.get_node_by_handle(handle) {
current_handle = parent.parent_handle.as_ref();
} else {
return false;
}
}
None => return false,
}
}
false
}
}