# Agate

Agate is a Go library for creating, managing, and sharing snapshots of directories. It provides functionality for creating incremental snapshots, storing them efficiently, and sharing them over a network using gRPC.

## Installation

```bash
go get gitea.unprism.ru/KRBL/Agate
```

## Features

- Create snapshots of directories
- Incremental snapshots (only store changes)
- Restore snapshots
- List and manage snapshots
- Share snapshots over a network using gRPC
- Connect to remote snapshot repositories

## Basic Usage

### Creating a Snapshot Repository

To create a snapshot repository, you need to initialize the Agate library with the appropriate options:

```go
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"

	"gitea.unprism.ru/KRBL/Agate"
	"gitea.unprism.ru/KRBL/Agate/stores"
)

func main() {
	// Create directories for your repository
	workDir := "/path/to/your/repository"
	if err := os.MkdirAll(workDir, 0755); err != nil {
		log.Fatalf("Failed to create work directory: %v", err)
	}

	// Initialize the default stores
	metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
	if err != nil {
		log.Fatalf("Failed to initialize stores: %v", err)
	}
	defer metadataStore.Close()

	// Initialize Agate
	agateOptions := agate.AgateOptions{
		WorkDir:       workDir,
		MetadataStore: metadataStore,
		BlobStore:     blobStore,
	}

	ag, err := agate.New(agateOptions)
	if err != nil {
		log.Fatalf("Failed to initialize Agate: %v", err)
	}
	defer ag.Close()

	// Create a snapshot
	ctx := context.Background()
	snapshotID, err := ag.SaveSnapshot(ctx, "My First Snapshot", "")
	if err != nil {
		log.Fatalf("Failed to create snapshot: %v", err)
	}
	fmt.Printf("Created snapshot with ID: %s\n", snapshotID)

	// List snapshots
	snapshots, err := ag.ListSnapshots(ctx)
	if err != nil {
		log.Fatalf("Failed to list snapshots: %v", err)
	}
	fmt.Printf("Found %d snapshots:\n", len(snapshots))
	for _, s := range snapshots {
		fmt.Printf("  - %s: %s (created at %s)\n", s.ID, s.Name, s.CreationTime.Format("2006-01-02 15:04:05"))
	}
}
```

### Hosting a Snapshot Repository

To host a snapshot repository and make it available over the network, you can use the `StartServer` method:

```go
package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"gitea.unprism.ru/KRBL/Agate"
	"gitea.unprism.ru/KRBL/Agate/stores"
)

func main() {
	// Create directories for your repository
	workDir := "/path/to/your/repository"
	if err := os.MkdirAll(workDir, 0755); err != nil {
		log.Fatalf("Failed to create work directory: %v", err)
	}

	// Initialize the default stores
	metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
	if err != nil {
		log.Fatalf("Failed to initialize stores: %v", err)
	}
	defer metadataStore.Close()

	// Initialize Agate
	agateOptions := agate.AgateOptions{
		WorkDir:       workDir,
		MetadataStore: metadataStore,
		BlobStore:     blobStore,
	}

	ag, err := agate.New(agateOptions)
	if err != nil {
		log.Fatalf("Failed to initialize Agate: %v", err)
	}
	defer ag.Close()

	// Start the gRPC server
	ctx := context.Background()
	address := "0.0.0.0:50051" // Listen on all interfaces, port 50051
	if err := ag.StartServer(ctx, address); err != nil {
		log.Fatalf("Failed to start server: %v", err)
	}

	log.Printf("Server started on %s", address)

	// Wait for termination signal
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
	<-sigCh

	log.Println("Shutting down...")
}
```

### Connecting to a Hosted Snapshot Repository

To connect to a hosted snapshot repository and retrieve snapshots:

```go
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"gitea.unprism.ru/KRBL/Agate"
	"gitea.unprism.ru/KRBL/Agate/stores"
)

func main() {
	// Create directories for your local repository
	workDir := "/path/to/your/local/repository"
	if err := os.MkdirAll(workDir, 0755); err != nil {
		log.Fatalf("Failed to create work directory: %v", err)
	}

	// Initialize the default stores
	metadataStore, blobStore, err := stores.InitDefaultStores(workDir)
	if err != nil {
		log.Fatalf("Failed to initialize stores: %v", err)
	}
	defer metadataStore.Close()

	// Initialize Agate
	agateOptions := agate.AgateOptions{
		WorkDir:       workDir,
		MetadataStore: metadataStore,
		BlobStore:     blobStore,
	}

	ag, err := agate.New(agateOptions)
	if err != nil {
		log.Fatalf("Failed to initialize Agate: %v", err)
	}
	defer ag.Close()

	// Connect to a remote server
	ctx := context.Background()
	remoteAddress := "remote-server:50051"

	// List snapshots from the remote server
	snapshots, err := ag.GetRemoteSnapshotList(ctx, remoteAddress)
	if err != nil {
		log.Fatalf("Failed to list remote snapshots: %v", err)
	}

	fmt.Printf("Found %d remote snapshots:\n", len(snapshots))
	for _, s := range snapshots {
		fmt.Printf("  - %s: %s (created at %s)\n", s.ID, s.Name, s.CreationTime.Format("2006-01-02 15:04:05"))
	}

	// Download a specific snapshot
	if len(snapshots) > 0 {
		snapshotID := snapshots[0].ID
		fmt.Printf("Downloading snapshot %s...\n", snapshotID)

		// Download the snapshot (pass empty string as localParentID if this is the first download)
		if err := ag.GetRemoteSnapshot(ctx, remoteAddress, snapshotID, ""); err != nil {
			log.Fatalf("Failed to download snapshot: %v", err)
		}

		fmt.Printf("Successfully downloaded snapshot %s\n", snapshotID)
	}
}
```

## Advanced Usage

### Creating Incremental Snapshots

You can create incremental snapshots by specifying a parent snapshot ID:

```go
// Create a first snapshot
snapshotID1, err := ag.SaveSnapshot(ctx, "First Snapshot", "")
if err != nil {
    log.Fatalf("Failed to create first snapshot: %v", err)
}

// Make some changes to your files...

// Create a second snapshot with the first one as parent
snapshotID2, err := ag.SaveSnapshot(ctx, "Second Snapshot", snapshotID1)
if err != nil {
    log.Fatalf("Failed to create second snapshot: %v", err)
}
```

### Restoring a Snapshot

To restore a snapshot:

```go
if err := ag.RestoreSnapshot(ctx, snapshotID); err != nil {
    log.Fatalf("Failed to restore snapshot: %v", err)
}
```

### Getting Snapshot Details

To get detailed information about a snapshot:

```go
snapshot, err := ag.GetSnapshotDetails(ctx, snapshotID)
if err != nil {
    log.Fatalf("Failed to get snapshot details: %v", err)
}

fmt.Printf("Snapshot: %s\n", snapshot.Name)
fmt.Printf("Created: %s\n", snapshot.CreationTime.Format("2006-01-02 15:04:05"))
fmt.Printf("Files: %d\n", len(snapshot.Files))
```

### Deleting a Snapshot

To delete a snapshot:

```go
if err := ag.DeleteSnapshot(ctx, snapshotID); err != nil {
    log.Fatalf("Failed to delete snapshot: %v", err)
}
```

## API Reference

### Agate

The main entry point for the library.

- `New(options AgateOptions) (*Agate, error)` - Create a new Agate instance
- `SaveSnapshot(ctx context.Context, name string, parentID string) (string, error)` - Create a new snapshot
- `RestoreSnapshot(ctx context.Context, snapshotID string) error` - Restore a snapshot
- `ListSnapshots(ctx context.Context) ([]store.SnapshotInfo, error)` - List all snapshots
- `GetSnapshotDetails(ctx context.Context, snapshotID string) (*store.Snapshot, error)` - Get details of a snapshot
- `DeleteSnapshot(ctx context.Context, snapshotID string) error` - Delete a snapshot
- `StartServer(ctx context.Context, address string) error` - Start a gRPC server to share snapshots
- `ConnectRemote(address string) (*grpc.SnapshotClient, error)` - Connect to a remote server
- `GetRemoteSnapshotList(ctx context.Context, address string) ([]store.SnapshotInfo, error)` - List snapshots from a remote server
- `GetRemoteSnapshot(ctx context.Context, address string, snapshotID string, localParentID string) error` - Download a snapshot from a remote server

### AgateOptions

Configuration options for the Agate library.

- `WorkDir string` - Directory where snapshots will be stored and managed
- `OpenFunc func(dir string) error` - Called after a snapshot is restored
- `CloseFunc func() error` - Called before a snapshot is created or restored
- `MetadataStore store.MetadataStore` - Implementation of the metadata store
- `BlobStore store.BlobStore` - Implementation of the blob store

## License

[Add your license information here]