load_document.go

  1package main
  2
  3import (
  4	"fmt"
  5	"log"
  6	"os"
  7
  8	ygo "git.kilimanjaro.io/ygo"
  9)
 10
 11// loadAndDisplayDocument reads a Yjs document from a binary file and displays its contents
 12func loadAndDisplayDocument(filePath string) error {
 13	// Read the binary file containing V1 encoded Yjs state
 14	data, err := os.ReadFile(filePath)
 15	if err != nil {
 16		return fmt.Errorf("failed to read file: %w", err)
 17	}
 18
 19	fmt.Printf("Loaded %d bytes from %s\n", len(data), filePath)
 20
 21	// Create a new document
 22	doc, err := ygo.NewDoc()
 23	if err != nil {
 24		return fmt.Errorf("failed to create doc: %w", err)
 25	}
 26	defer doc.Destroy()
 27
 28	// IMPORTANT: Initialize shared types BEFORE applying update
 29	// This creates the type entries that the update will populate
 30	txt, err := doc.GetText("content")
 31	if err != nil {
 32		return fmt.Errorf("failed to get text: %w", err)
 33	}
 34	defer txt.Destroy()
 35
 36	arr, err := doc.GetArray("items")
 37	if err != nil {
 38		return fmt.Errorf("failed to get array: %w", err)
 39	}
 40	defer arr.Destroy()
 41
 42	m, err := doc.GetMap("metadata")
 43	if err != nil {
 44		return fmt.Errorf("failed to get map: %w", err)
 45	}
 46	defer m.Destroy()
 47
 48	// Apply the update to the document using UnmarshalBinary
 49	if err := doc.UnmarshalBinary(data); err != nil {
 50		return fmt.Errorf("failed to unmarshal document: %w", err)
 51	}
 52
 53	fmt.Println("✓ Successfully loaded document from binary file")
 54
 55	// Read and display the document contents using a read transaction
 56	var content string
 57	err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
 58		// Display document info
 59		fmt.Printf("Document ID: %d\n", doc.ClientID())
 60		fmt.Printf("Document GUID: %s\n", doc.GUID())
 61
 62		// Access text field
 63		if txt != nil {
 64			var err error
 65			content, err = txt.String(txn)
 66			if err != nil {
 67				return fmt.Errorf("failed to get text: %w", err)
 68			}
 69		}
 70
 71		// Access array field
 72		if arr != nil {
 73			arrLen := arr.Len()
 74			fmt.Printf("Array \"items\" length: %d\n", arrLen)
 75
 76			// Read first few items
 77			for i := uint32(0); i < arrLen && i < 5; i++ {
 78				item, err := arr.Get(txn, i)
 79				if err != nil {
 80					return fmt.Errorf("failed to get array item: %w", err)
 81				}
 82				defer item.Destroy()
 83
 84				// Try to read the value based on its type
 85				switch item.Tag() {
 86				case ygo.TagJSONInt:
 87					if val, ok := item.Int(); ok {
 88						fmt.Printf("  [%d]: int=%d\n", i, val)
 89					}
 90				case ygo.TagJSONNum:
 91					if val, ok := item.Float(); ok {
 92						fmt.Printf("  [%d]: float=%f\n", i, val)
 93					}
 94				case ygo.TagJSONStr:
 95					if val, ok := item.String(); ok {
 96						fmt.Printf("  [%d]: string=%s\n", i, val)
 97					}
 98				default:
 99					fmt.Printf("  [%d]: tag=%d (other type)\n", i, item.Tag())
100				}
101			}
102		}
103
104		// Access map field
105		if m != nil {
106			mLen := m.Len(txn)
107			fmt.Printf("Map \"metadata\" entries: %d\n", mLen)
108		}
109
110		return nil
111	})
112	if err != nil {
113		return fmt.Errorf("failed to execute read transaction: %w", err)
114	}
115
116	// Display text content outside the transaction
117	if content != "" {
118		fmt.Printf("Text field \"content\": %s\n", content)
119	}
120
121	return nil
122}
123
124// createSampleDocument creates a sample Yjs document and saves it to a file
125func createSampleDocument(filePath string) error {
126	doc, err := ygo.NewDoc()
127	if err != nil {
128		return fmt.Errorf("failed to create doc: %w", err)
129	}
130	defer doc.Destroy()
131
132	// IMPORTANT: Initialize shared types BEFORE creating transaction
133	txt, err := doc.GetText("content")
134	if err != nil {
135		return fmt.Errorf("failed to get text: %w", err)
136	}
137	defer txt.Destroy()
138
139	arr, err := doc.GetArray("items")
140	if err != nil {
141		return fmt.Errorf("failed to get array: %w", err)
142	}
143	defer arr.Destroy()
144
145	// Now populate the document using a write transaction
146	err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
147		// Add content
148		txt.Insert(txn, 0, "Hello from Yjs!")
149
150		// Add array items
151		items := []ygo.Input{
152			ygo.Int(42),
153			ygo.String("sample"),
154			ygo.Float(3.14),
155		}
156		arr.InsertRange(txn, 0, items)
157
158		return nil
159	})
160	if err != nil {
161		return fmt.Errorf("failed to execute transaction: %w", err)
162	}
163
164	// Marshal the document to binary
165	data, err := doc.MarshalBinary()
166	if err != nil {
167		return fmt.Errorf("failed to marshal document: %w", err)
168	}
169
170	// Save to file
171	if err := os.WriteFile(filePath, data, 0644); err != nil {
172		return fmt.Errorf("failed to write file: %w", err)
173	}
174
175	fmt.Printf("Created sample document: %s (%d bytes)\n", filePath, len(data))
176	return nil
177}
178
179func main() {
180	filePath := "document.yjs"
181
182	// If file doesn't exist, create a sample
183	if _, err := os.Stat(filePath); os.IsNotExist(err) {
184		fmt.Println("Sample file not found, creating one...")
185		if err := createSampleDocument(filePath); err != nil {
186			log.Fatalf("Failed to create sample: %v", err)
187		}
188	}
189
190	// Load and display
191	if err := loadAndDisplayDocument(filePath); err != nil {
192		log.Fatalf("Failed to load document: %v", err)
193	}
194}