2023-03-11 14:13:35 +05:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2023-04-03 19:43:16 +05:00
|
|
|
"strings"
|
2023-03-11 14:13:35 +05:00
|
|
|
"time"
|
|
|
|
|
2023-04-03 19:43:16 +05:00
|
|
|
"github.com/tidwall/match"
|
|
|
|
|
2023-03-11 14:13:35 +05:00
|
|
|
"github.com/BurntSushi/toml"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
// Имя файлов бекапа без расширения
|
|
|
|
FileName string
|
|
|
|
|
|
|
|
// Маски файлов для включения в архив
|
2023-03-19 12:18:33 +05:00
|
|
|
Patterns []*Pattern
|
2023-03-11 14:13:35 +05:00
|
|
|
|
2023-03-19 12:13:32 +05:00
|
|
|
// Маски файлов для исключения
|
2023-03-19 12:18:33 +05:00
|
|
|
GlobalExcludeFileNamePatterns []string
|
2023-03-19 12:13:32 +05:00
|
|
|
|
|
|
|
// Маски путей для исключения
|
2023-03-19 12:18:33 +05:00
|
|
|
GlobalExcludeFilePathPatterns []string
|
2023-03-11 14:13:35 +05:00
|
|
|
|
|
|
|
// Останавливать обработку при любой ошибке
|
|
|
|
StopOnAnyError bool
|
|
|
|
|
2023-04-03 19:43:16 +05:00
|
|
|
// Уровень логирования
|
|
|
|
LogLevel LogLevel
|
2023-03-11 14:13:35 +05:00
|
|
|
|
2023-04-03 19:43:16 +05:00
|
|
|
filePath string
|
2023-03-11 14:13:35 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (config *Config) Save(filepath string) error {
|
|
|
|
f, err := os.Create(filepath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = toml.NewEncoder(f).Encode(config)
|
|
|
|
if err != nil {
|
|
|
|
f.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return f.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func LoadConfig(filePath string) (*Config, error) {
|
|
|
|
f, err := os.Open(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("open file: %v", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
var config Config
|
|
|
|
|
|
|
|
_, err = toml.DecodeReader(f, &config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("decode file: %v", err)
|
|
|
|
}
|
|
|
|
|
2023-03-19 12:18:33 +05:00
|
|
|
for _, mask := range config.Patterns {
|
|
|
|
if len(mask.FilePathPatternList) == 0 {
|
|
|
|
mask.FilePathPatternList = []string{"*"}
|
2023-03-19 12:13:32 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-11 14:13:35 +05:00
|
|
|
configFilePath, err := filepath.Abs(filePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
config.filePath = configFilePath
|
|
|
|
|
|
|
|
return &config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// planChan возвращает канал, в который засылает список файлов для добавления/обновления
|
2023-04-03 19:43:16 +05:00
|
|
|
func (b *Config) planChan(index Index) chan FileInfo {
|
|
|
|
allFilesChan := make(chan FileInfo, 64) // TODO: размер очереди?
|
|
|
|
addFilesChan := make(chan FileInfo, 64) // TODO: размер очереди?
|
2023-03-11 14:13:35 +05:00
|
|
|
|
|
|
|
go func() { b.fileList(allFilesChan) }()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for file := range allFilesChan {
|
|
|
|
// Если индекса нет, добавляются все файлы
|
|
|
|
if index == nil {
|
|
|
|
addFilesChan <- file
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-04-03 19:43:16 +05:00
|
|
|
existingFile, exists := index[file.filePath]
|
2023-03-11 14:13:35 +05:00
|
|
|
if !exists {
|
|
|
|
addFilesChan <- file
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-04-03 19:43:16 +05:00
|
|
|
if file.ModificationTime.Truncate(time.Second).After(existingFile.Latest().ModificationTime.Truncate(time.Second)) {
|
2023-03-11 14:13:35 +05:00
|
|
|
addFilesChan <- file
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
close(addFilesChan)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return addFilesChan
|
|
|
|
}
|
2023-04-03 19:43:16 +05:00
|
|
|
|
|
|
|
// FindAll возвращает индекс файлов, совпавших по маске
|
|
|
|
func (b *Config) FindAll(pattern string) (Index, error) {
|
|
|
|
index, err := b.index(true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("index: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
result := make(Index)
|
|
|
|
|
|
|
|
for path, info := range index {
|
|
|
|
if match.Match(strings.ToLower(path), pattern) {
|
|
|
|
for _, historyItem := range info {
|
|
|
|
result.AddFile(path, historyItem.ArchiveFileName, historyItem.ModificationTime)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|