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}