| /* |
| * 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 provides a Go binding for Apache OpenDAL (Open Data Access Layer). |
| // |
| // OpenDAL is a data access layer that allows users to easily interact with various |
| // storage services using a unified API. This Go binding enables Go developers to |
| // leverage OpenDAL's capabilities without the need for CGO. |
| // |
| // Key features: |
| // - Unified interface for multiple storage backends (e.g., S3, Azure Blob, local filesystem) |
| // - Native Go implementation using purego and libffi |
| // - No CGO dependency, ensuring better portability and easier cross-compilation |
| // - Supports common operations like read, write, delete, list, and metadata retrieval |
| // |
| // Basic usage: |
| // |
| // import ( |
| // "github.com/apache/opendal/bindings/go |
| // "github.com/apache/opendal-go-services/memory |
| // ) |
| // |
| // func main() { |
| // op, err := opendal.NewOperator(memory.Scheme, opendal.OperatorOptions{ |
| // "root": "/path/to/root", |
| // }) |
| // if err != nil { |
| // log.Fatal(err) |
| // } |
| // defer op.Close() |
| // |
| // // Perform operations using the operator |
| // err = op.Write("example.txt", []byte("Hello, OpenDAL!")) |
| // if err != nil { |
| // log.Fatal(err) |
| // } |
| // } |
| // |
| // This package aims to provide a seamless experience for Go developers working with |
| // various storage systems, combining the flexibility of OpenDAL with the performance |
| // and simplicity of native Go code. |
| package opendal |
| |
| import ( |
| "context" |
| ) |
| |
| // Scheme defines the interface for storage scheme implementations. |
| // |
| // A Scheme represents a specific storage backend (e.g., S3, filesystem, memory) |
| // and provides methods to identify and initialize the scheme. |
| // |
| // Implementations of this interface should be thread-safe, especially the LoadOnce method. |
| type Scheme interface { |
| // Name returns the unique identifier of the scheme. |
| Name() string |
| |
| // Path returns the filesystem path where the scheme's shared library (.so) is located. |
| Path() string |
| |
| // LoadOnce initializes the scheme. It ensures that initialization occurs only once, |
| // even if called multiple times. Subsequent calls after the first should be no-ops. |
| // |
| // Returns an error if initialization fails. |
| LoadOnce() error |
| } |
| |
| // OperatorOptions contains configuration parameters for creating an Operator. |
| // |
| // This struct allows users to specify various settings and credentials |
| // required for connecting to and interacting with different storage backends. |
| // |
| // Fields in this struct vary depending on the storage scheme being used. |
| // Refer to the documentation of specific storage backends for details on |
| // required and optional fields. |
| // |
| // Example usage: |
| // |
| // options := opendal.OperatorOptions{ |
| // "root": "/path/to/root", |
| // "endpoint": "https://example.com", |
| // "access_key_id": "your_access_key", |
| // "secret_access_key": "your_secret_key", |
| // } |
| type OperatorOptions map[string]string |
| |
| // Operator is the entry point for all public APIs in OpenDAL. |
| // |
| // Operator provides a unified interface for interacting with various storage services. |
| // It encapsulates the underlying storage operations and presents a consistent API |
| // regardless of the storage backend. |
| // |
| // # Usage |
| // |
| // Create an Operator using NewOperator, perform operations, and always remember |
| // to Close the operator when finished to release resources. |
| // |
| // # Example |
| // |
| // func main() { |
| // // Create a new operator for the memory backend |
| // op, err := opendal.NewOperator(memory.Scheme, opendal.OperatorOptions{}) |
| // if err != nil { |
| // log.Fatal(err) |
| // } |
| // defer op.Close() // Ensure the operator is closed when done |
| // |
| // // Perform operations using the operator |
| // err = op.Write("example.txt", []byte("Hello, OpenDAL!")) |
| // if err != nil { |
| // log.Fatal(err) |
| // } |
| // |
| // data, err := op.Read("example.txt") |
| // if err != nil { |
| // log.Fatal(err) |
| // } |
| // fmt.Println(string(data)) |
| // } |
| // |
| // Note: Always use defer op.Close() to ensure proper resource cleanup. |
| // |
| // # Available Operations |
| // |
| // Operator provides methods for common storage operations including: |
| // - Read: Read data from a path |
| // - Write: Write data to a path |
| // - Stat: Get metadata for a path |
| // - Delete: Remove a file or directory |
| // - List: Enumerate entries in a directory |
| // - and more... |
| // |
| // Refer to the individual method documentation for detailed usage information. |
| type Operator struct { |
| ctx context.Context |
| cancel context.CancelFunc |
| |
| inner *opendalOperator |
| } |
| |
| // NewOperator creates and initializes a new Operator for the specified storage scheme. |
| // |
| // Parameters: |
| // - scheme: The storage scheme (e.g., "memory", "s3", "fs"). |
| // - options: Configuration options for the operator. |
| // |
| // Returns: |
| // - *Operator: A new Operator instance. |
| // - error: An error if initialization fails, or nil if successful. |
| // |
| // Note: Remember to call Close() on the returned Operator when it's no longer needed. |
| func NewOperator(scheme Scheme, opts OperatorOptions) (op *Operator, err error) { |
| err = scheme.LoadOnce() |
| if err != nil { |
| return |
| } |
| |
| ctx, cancel, err := newContext(scheme.Path()) |
| if err != nil { |
| return |
| } |
| defer func() { |
| if err != nil { |
| // cancel must be called after all ffi calls |
| // to prevent `use after free` panics. |
| cancel() |
| } |
| }() |
| |
| options := ffiOperatorOptionsNew.symbol(ctx)() |
| setOptions := ffiOperatorOptionsSet.symbol(ctx) |
| |
| defer ffiOperatorOptionsFree.symbol(ctx)(options) |
| |
| for key, value := range opts { |
| err = setOptions(options, key, value) |
| if err != nil { |
| return |
| } |
| } |
| |
| inner, err := ffiOperatorNew.symbol(ctx)(scheme, options) |
| if err != nil { |
| return |
| } |
| |
| op = &Operator{ |
| inner: inner, |
| ctx: ctx, |
| cancel: cancel, |
| } |
| |
| return |
| } |
| |
| // Close releases all resources associated with the Operator. |
| // |
| // It's important to call this method when the Operator is no longer needed |
| // to ensure proper cleanup of underlying resources. |
| // |
| // Note: It's recommended to use defer op.Close() immediately after creating an Operator. |
| func (op *Operator) Close() { |
| ffiOperatorFree.symbol(op.ctx)(op.inner) |
| op.cancel() |
| } |