bindings/go/lister.go (183 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package opendal import ( "context" "unsafe" "github.com/jupiterrider/ffi" ) // Check verifies if the operator is functioning correctly. // // This function performs a health check on the operator by sending a `list` request // to the root path. It returns any errors encountered during this process. // // # Returns // // - error: An error if the check fails, or nil if the operator is working correctly. // // # Details // // The check is performed by attempting to list the contents of the root directory. // This operation tests the basic functionality of the operator, including // connectivity and permissions. // // # Example // // func exampleCheck(op *opendal.Operator) { // err = op.Check() // if err != nil { // log.Printf("Operator check failed: %v", err) // } else { // log.Println("Operator is functioning correctly") // } // } // // Note: This example assumes proper error handling and import statements. func (op *Operator) Check() (err error) { ds, err := op.List("/") if err != nil { return } defer ds.Close() ds.Next() err = ds.Error() if err, ok := err.(*Error); ok && err.Code() == CodeNotFound { return nil } return } // List returns a Lister to iterate over entries that start with the given path in the parent directory. // // This function creates a new Lister to enumerate entries in the specified path. // // # Parameters // // - path: The starting path for listing entries. // // # Returns // // - *Lister: A new Lister instance for iterating over entries. // - error: An error if the listing operation fails, or nil if successful. // // # Notes // // 1. List is a wrapper around the C-binding function `opendal_operator_list`. Recursive listing is not currently supported. // 2. Returned entries do not include metadata information. Use op.Stat to fetch metadata for individual entries. // // # Example // // func exampleList(op *opendal.Operator) { // lister, err := op.List("test") // if err != nil { // log.Fatal(err) // } // // for lister.Next() { // entry := lister.Entry() // // meta, err := op.Stat(entry.Path()) // if err != nil { // log.Printf("Error fetching metadata for %s: %v", entry.Path(), err) // continue // } // // fmt.Printf("Name: %s\n", entry.Name()) // fmt.Printf("Length: %d\n", meta.ContentLength()) // fmt.Printf("Last Modified: %s\n", meta.LastModified()) // fmt.Printf("Is Directory: %v, Is File: %v\n", meta.IsDir(), meta.IsFile()) // fmt.Println("---") // } // if err := lister.Err(); err != nil { // log.Printf("Error during listing: %v", err) // } // } // // Note: Always check lister.Err() after the loop to catch any errors that // occurred during iteration. func (op *Operator) List(path string) (*Lister, error) { list := getFFI[operatorList](op.ctx, symOperatorList) inner, err := list(op.inner, path) if err != nil { return nil, err } lister := &Lister{ inner: inner, ctx: op.ctx, } return lister, nil } // Lister provides an mechanism for listing entries at a specified path. // // Lister is a wrapper around the C-binding function `opendal_operator_list`. It allows // for efficient iteration over entries in a storage system. // // # Limitations // // - The current implementation does not support the `list_with` functionality. // // # Usage // // Lister should be used in conjunction with its Next() and Entry() methods to // iterate through entries. The iteration ends when there are no more entries // or when an error occurs. // // # Behavior // // - Next() returns false when there are no more entries or if an error has occurred. // - Entry() returns nil if there are no more entries or if an error has been encountered. // // # Example // // lister, err := op.List("path/to/list") // if err != nil { // log.Fatal(err) // } // // for lister.Next() { // entry := lister.Entry() // // Process the entry // fmt.Println(entry.Name()) // } type Lister struct { inner *opendalLister ctx context.Context entry *Entry err error } // This method implements the io.Closer interface. It should be called when // the Lister is no longer needed to ensure proper resource cleanup. func (l *Lister) Close() error { free := getFFI[listerFree](l.ctx, symListerFree) free(l.inner) return nil } func (l *Lister) Error() error { return l.err } // Next advances the Lister to the next entry in the list. // // This method must be called before accessing the current entry. It prepares // the next entry for reading and indicates whether there are more entries // to process. // // # Returns // // - bool: true if there is another entry to process, false if the end of the list // has been reached or an error occurred. // // # Usage // // Next should be used in a loop condition to iterate through all entries: // // for lister.Next() { // entry := lister.Entry() // // Process the entry // } // // # Error Handling // // If an error occurs during iteration, Next will return false. The error // can then be retrieved by calling the Err method on the Lister. // // # Example // // lister, err := op.List("path/to/list") // if err != nil { // log.Fatal(err) // } // // for lister.Next() { // entry := lister.Entry() // fmt.Println(entry.Name()) // } func (l *Lister) Next() bool { next := getFFI[listerNext](l.ctx, symListerNext) inner, err := next(l.inner) if inner == nil || err != nil { l.err = err l.entry = nil return false } entry := newEntry(l.ctx, inner) l.entry = entry return true } // Entry returns the current Entry in the list. // Returns nil if there are no more entries func (l *Lister) Entry() *Entry { return l.entry } // Entry represents a path and its associated metadata as returned by Lister. // // An Entry provides basic information about a file or directory encountered // during a list operation. It contains the path of the item and minimal metadata. // // # Limitations // // The Entry itself does not contain comprehensive metadata. For detailed // metadata information, use the op.Stat() method with the Entry's path. // // # Usage // // Entries are typically obtained through iteration of a Lister: // // for lister.Next() { // entry := lister.Entry() // // Process the entry // fmt.Println(entry.Name()) // } // // # Fetching Detailed Metadata // // To obtain comprehensive metadata for an Entry, use op.Stat(): // // meta, err := op.Stat(entry.Path()) // if err != nil { // log.Printf("Error fetching metadata: %v", err) // return // } // fmt.Printf("Size: %d, Last Modified: %s\n", meta.ContentLength(), meta.LastModified()) // // # Methods // // Entry provides methods to access basic information: // - Path(): Returns the full path of the entry. // - Name(): Returns the name of the entry (last component of the path). type Entry struct { name string path string } func newEntry(ctx context.Context, inner *opendalEntry) *Entry { name := getFFI[entryName](ctx, symEntryName) path := getFFI[entryPath](ctx, symEntryPath) free := getFFI[entryFree](ctx, symEntryFree) defer free(inner) return &Entry{ name: name(inner), path: path(inner), } } // Name returns the last component of the entry's path. func (e *Entry) Name() string { return e.name } // Path returns the full path of the entry. func (e *Entry) Path() string { return e.path } const symOperatorList = "opendal_operator_list" type operatorList func(op *opendalOperator, path string) (*opendalLister, error) var withOperatorList = withFFI(ffiOpts{ sym: symOperatorList, rType: &typeResultList, aTypes: []*ffi.Type{&ffi.TypePointer, &ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) operatorList { return func(op *opendalOperator, path string) (*opendalLister, error) { bytePath, err := BytePtrFromString(path) if err != nil { return nil, err } var result opendalResultList ffiCall( unsafe.Pointer(&result), unsafe.Pointer(&op), unsafe.Pointer(&bytePath), ) if result.err != nil { return nil, parseError(ctx, result.err) } return result.lister, nil } }) const symListerFree = "opendal_lister_free" type listerFree func(l *opendalLister) var withListerFree = withFFI(ffiOpts{ sym: symListerFree, rType: &ffi.TypeVoid, aTypes: []*ffi.Type{&ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) listerFree { return func(l *opendalLister) { ffiCall( nil, unsafe.Pointer(&l), ) } }) const symListerNext = "opendal_lister_next" type listerNext func(l *opendalLister) (*opendalEntry, error) var withListerNext = withFFI(ffiOpts{ sym: symListerNext, rType: &typeResultListerNext, aTypes: []*ffi.Type{&ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) listerNext { return func(l *opendalLister) (*opendalEntry, error) { var result opendalResultListerNext ffiCall( unsafe.Pointer(&result), unsafe.Pointer(&l), ) if result.err != nil { return nil, parseError(ctx, result.err) } return result.entry, nil } }) const symEntryFree = "opendal_entry_free" type entryFree func(e *opendalEntry) var withEntryFree = withFFI(ffiOpts{ sym: symEntryFree, rType: &ffi.TypePointer, aTypes: []*ffi.Type{&ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) entryFree { return func(e *opendalEntry) { ffiCall( nil, unsafe.Pointer(&e), ) } }) const symEntryName = "opendal_entry_name" type entryName func(e *opendalEntry) string var withEntryName = withFFI(ffiOpts{ sym: symEntryName, rType: &ffi.TypePointer, aTypes: []*ffi.Type{&ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) entryName { return func(e *opendalEntry) string { var bytePtr *byte ffiCall( unsafe.Pointer(&bytePtr), unsafe.Pointer(&e), ) return BytePtrToString(bytePtr) } }) const symEntryPath = "opendal_entry_path" type entryPath func(e *opendalEntry) string var withEntryPath = withFFI(ffiOpts{ sym: symEntryPath, rType: &ffi.TypePointer, aTypes: []*ffi.Type{&ffi.TypePointer}, }, func(ctx context.Context, ffiCall func(rValue unsafe.Pointer, aValues ...unsafe.Pointer)) entryPath { return func(e *opendalEntry) string { var bytePtr *byte ffiCall( unsafe.Pointer(&bytePtr), unsafe.Pointer(&e), ) return BytePtrToString(bytePtr) } })