///|
pub struct DirectoryCursor {
priv client : Client
priv handle : Handle
priv plus : Bool
priv mut cookie : UInt64
priv mut verifier : Bytes
priv seen : Map[UInt64, Bool]
priv mut batch : Array[DirectoryEntry]
priv mut index : Int
priv mut eof : Bool
priv mut count : Int
priv mut busy : Bool
priv mut closed : Bool
}
///|
pub fn Client::scan_directory(
self : Client,
handle : Handle,
plus? : Bool = true,
) -> DirectoryCursor {
{
client: self,
handle,
plus,
cookie: 0UL,
verifier: b"\x00\x00\x00\x00\x00\x00\x00\x00",
seen: Map([]),
batch: [],
index: 0,
eof: false,
count: 0,
busy: false,
closed: false,
}
}
///|
pub async fn Client::open_directory(
self : Client,
path : String,
plus? : Bool = true,
) -> DirectoryCursor {
self.scan_directory(self.resolve(path), plus~)
}
///|
pub fn DirectoryCursor::close(self : DirectoryCursor) -> Unit {
self.closed = true
self.batch.clear()
}
///|
pub async fn DirectoryCursor::next(self : DirectoryCursor) -> DirectoryEntry? {
if self.closed {
return None
}
if self.busy {
raise InvalidArgument("concurrent directory cursor use")
}
self.busy = true
defer {
self.busy = false
}
errdefer self.close()
if self.index >= self.batch.length() {
if self.eof {
self.close()
return None
}
let page = self.client.read_directory(
self.handle,
cookie=self.cookie,
verifier=self.verifier,
plus=self.plus,
)
if self.cookie != 0UL && page.verifier != self.verifier {
raise ProtocolError("directory verifier changed during traversal")
}
if page.entries.length() > self.client.options.max_entries - self.count {
raise ProtocolError("directory traversal exceeds entry budget")
}
if !page.eof {
if page.entries.length() == 0 {
raise ProtocolError("directory pagination made no progress")
}
let cookie = page.entries[page.entries.length() - 1].cookie
if cookie == 0UL || cookie == self.cookie || self.seen.contains(cookie) {
raise ProtocolError("directory cookie loop")
}
self.seen[cookie] = true
self.cookie = cookie
}
self.verifier = page.verifier
self.batch = page.entries
self.count += page.entries.length()
self.index = 0
self.eof = page.eof
}
if self.index == self.batch.length() {
self.close()
return None
}
let entry = self.batch[self.index]
self.index += 1
Some(entry)
}