// Файл: internal/commands/export_import.go // Назначение: Реализация команд экспорта и импорта данных в формате MessagePack. // Синтаксис: export "Имя_слайса" "название_экспортируемого_файла".msgpack // import "Имя_слайса" "название_импортируемого_файла".msgpack package commands import ( "fmt" "os" "strings" "futriis/internal/storage" "futriis/pkg/utils" "futriis/internal/serializer" ) // ExportData экспортирует данные из слайса (базы данных) в файл MessagePack func ExportData(store *storage.Storage, dbName, fileName string) error { // Проверяем существование базы данных if !store.ExistsDatabase(dbName) { return fmt.Errorf("database '%s' not found", dbName) } // Получаем базу данных db, err := store.GetDatabase(dbName) if err != nil { return fmt.Errorf("failed to get database: %v", err) } // Собираем все данные из всех коллекций exportData := make(map[string]interface{}) collections := db.ListCollections() for _, collName := range collections { coll, err := db.GetCollection(collName) if err != nil { continue } // Получаем все документы коллекции docs := coll.GetAllDocuments() // Сериализуем документы в формат для экспорта collData := make([]map[string]interface{}, 0, len(docs)) for _, doc := range docs { docData := map[string]interface{}{ "_id": doc.ID, "fields": doc.GetFields(), "created_at": doc.CreatedAt, "updated_at": doc.UpdatedAt, "version": doc.Version, } collData = append(collData, docData) } exportData[collName] = collData } // Добавляем метаданные exportData["_metadata"] = map[string]interface{}{ "database": dbName, "export_time": fmt.Sprintf("%d", utils.GetCurrentTimestamp()), "version": "1.0", } // Сериализуем в MessagePack data, err := serializer.Marshal(exportData) if err != nil { return fmt.Errorf("failed to marshal export data: %v", err) } // Записываем в файл if err := os.WriteFile(fileName, data, 0644); err != nil { return fmt.Errorf("failed to write export file: %v", err) } fmt.Printf("✓ Database '%s' exported successfully to %s\n", dbName, fileName) fmt.Printf(" Collections exported: %d\n", len(collections)) return nil } // ImportData импортирует данные из файла MessagePack в слайс (базу данных) func ImportData(store *storage.Storage, dbName, fileName string) error { // Проверяем существование файла if _, err := os.Stat(fileName); os.IsNotExist(err) { return fmt.Errorf("import file '%s' not found", fileName) } // Читаем файл data, err := os.ReadFile(fileName) if err != nil { return fmt.Errorf("failed to read import file: %v", err) } // Десериализуем из MessagePack var importData map[string]interface{} if err := serializer.Unmarshal(data, &importData); err != nil { return fmt.Errorf("failed to unmarshal import data: %v", err) } // Проверяем метаданные metadata, ok := importData["_metadata"].(map[string]interface{}) if !ok { return fmt.Errorf("invalid import file format: missing metadata") } sourceDB, _ := metadata["database"].(string) fmt.Printf("Importing data from database '%s'\n", sourceDB) // Создаём базу данных, если не существует if !store.ExistsDatabase(dbName) { if err := store.CreateDatabase(dbName); err != nil { return fmt.Errorf("failed to create database: %v", err) } fmt.Printf("Created database '%s'\n", dbName) } // Получаем базу данных db, err := store.GetDatabase(dbName) if err != nil { return fmt.Errorf("failed to get database: %v", err) } importedCollections := 0 importedDocuments := 0 // Импортируем коллекции for key, value := range importData { if key == "_metadata" { continue } collName := key collData, ok := value.([]interface{}) if !ok { continue } // Создаём коллекцию, если не существует if _, err := db.GetCollection(collName); err != nil { if err := db.CreateCollection(collName); err != nil { fmt.Printf(" Warning: failed to create collection '%s': %v\n", collName, err) continue } } coll, err := db.GetCollection(collName) if err != nil { fmt.Printf(" Warning: failed to get collection '%s': %v\n", collName, err) continue } // Импортируем документы for _, docRaw := range collData { docMap, ok := docRaw.(map[string]interface{}) if !ok { continue } // Создаём документ doc := storage.NewDocument() if id, ok := docMap["_id"].(string); ok { doc.ID = id } if fields, ok := docMap["fields"].(map[string]interface{}); ok { for k, v := range fields { doc.SetField(k, v) } } if createdAt, ok := docMap["created_at"].(int64); ok { doc.CreatedAt = createdAt } if updatedAt, ok := docMap["updated_at"].(int64); ok { doc.UpdatedAt = updatedAt } if version, ok := docMap["version"].(uint64); ok { doc.Version = version } // Вставляем документ if err := coll.Insert(doc); err != nil { fmt.Printf(" Warning: failed to insert document %s: %v\n", doc.ID, err) continue } importedDocuments++ } importedCollections++ } fmt.Printf("✓ Database '%s' imported successfully from %s\n", dbName, fileName) fmt.Printf(" Collections imported: %d\n", importedCollections) fmt.Printf(" Documents imported: %d\n", importedDocuments) return nil } // ExecuteExport выполняет команду экспорта func ExecuteExport(store *storage.Storage, cmd string) error { // Формат: export "Имя_слайса" "название_экспортируемого_файла".msgpack parts := strings.SplitN(cmd, " ", 3) if len(parts) < 3 { return fmt.Errorf("usage: export \"database_name\" \"filename.msgpack\"") } dbName := strings.Trim(parts[1], "\"") fileName := strings.Trim(parts[2], "\"") // Проверяем расширение файла if !strings.HasSuffix(fileName, ".msgpack") { fileName = fileName + ".msgpack" } return ExportData(store, dbName, fileName) } // ExecuteImport выполняет команду импорта func ExecuteImport(store *storage.Storage, cmd string) error { // Формат: import "Имя_слайса" "название_импортируемого_файла".msgpack parts := strings.SplitN(cmd, " ", 3) if len(parts) < 3 { return fmt.Errorf("usage: import \"database_name\" \"filename.msgpack\"") } dbName := strings.Trim(parts[1], "\"") fileName := strings.Trim(parts[2], "\"") // Проверяем расширение файла if !strings.HasSuffix(fileName, ".msgpack") { fileName = fileName + ".msgpack" } return ImportData(store, dbName, fileName) }