quip.go 9.43 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
// quip is a package of quick ipld patterns.
//
// Most quip functions take a pointer to an error as their first argument.
// This has two purposes: if there's an error there, the quip function will do nothing;
// and if the quip function does something and creates an error, it puts it there.
// The effect of this is that most logic can be written very linearly.
//
// quip functions can be used to increase brevity without worrying about performance costs.
// None of the quip functions cause additional allocations in the course of their work.
// Benchmarks indicate no measurable speed penalties versus longhand manual error checking.
//
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
// Several functions perform comparable operations but with different arguments,
// and so these function names follow a pattern:
//
//   - `Build*` functions take a NodePrototype and return a Node.
//   - `Assemble*` functions take a NodeAssembler and feed data into it.
//   - There is no analog of `NodeAssembler.Begin*` functions
//     (we simply always use callbacks for structuring, because this is reasonably optimal).
//   - `Assign*` functions handle values of the scalar kinds
//     (these of course also never need callbacks, since there's no possible recursion).
//
// The `Assemble*` functions are used recursively.
// The `Build*` functions can be used instead of `Assemble*` at the top of a tree
// in order to save on writing a few additional lines of NodeBuilder setup and usage.
// (The `Assemble*` functions can also be used at the top of a tree if you
// wish to control the NodeBuilder yourself.  This may be desirable for being
// able to reset and reuse the NodeBuilder when performance is critical, for example.)
//
// The usual IPLD NodeAssembler, MapAssembler, and ListAssembler interfaces are still
// available while using quip functions, should you wish to interact with them directly,
// or compose the use of quip functions with other styles of data manipulation.
//
33 34 35 36
package quip

import (
	"github.com/ipld/go-ipld-prime"
37
	"github.com/ipld/go-ipld-prime/fluent"
38 39
)

40 41 42 43 44 45 46 47 48 49
// - removed the "Begin*" functions; no need to expose that kind of raw operation when the callback forms are zero-cost.
// - renamed functions for consistent "Build" vs "Assemble".
// - added "Assign*" functions for all scalar kinds, which reduces the usage of "AbsorbError" (but also, left AbsorbError in).
// - renamed the ListEntry/MapEntry functions to also have "Assemble*" forms (still callback style).
// - while also adding Assign{Map|List}Entry{Kind} functions (lets you get rid of another callback whenever the value is a scalar).
// - added Assign{|MapEntry|ListEntry} functions, which shell out to fluent.Reflect for even more convenience (at the cost of performance).
// - moved higher level functions like CopyRange to a separate file.
//
// Varations on map key arguments (which could be PathSegment or even Node, in addition to string) still aren't made available this.
// Perhaps that's just okay.  If you're really up some sort of creek where you need that, you can still just use the MapAssembler.AssembleKey system directly.
50 51 52 53 54 55 56 57 58 59

func AbsorbError(e *error, err error) {
	if *e != nil {
		return
	}
	if err != nil {
		*e = err
	}
}

60
func BuildMap(e *error, np ipld.NodePrototype, sizeHint int64, fn func(ma ipld.MapAssembler)) ipld.Node {
61 62 63
	if *e != nil {
		return nil
	}
64 65
	nb := np.NewBuilder()
	ma, err := nb.BeginMap(sizeHint)
66 67 68 69
	if err != nil {
		*e = err
		return nil
	}
70 71 72 73 74 75 76 77 78
	fn(ma)
	if *e != nil {
		return nil
	}
	*e = ma.Finish()
	if *e != nil {
		return nil
	}
	return nb.Build()
79 80
}

81
func AssembleMap(e *error, na ipld.NodeAssembler, sizeHint int64, fn func(ma ipld.MapAssembler)) {
82 83 84 85 86 87 88 89 90
	if *e != nil {
		return
	}
	ma, err := na.BeginMap(sizeHint)
	if err != nil {
		*e = err
		return
	}
	fn(ma)
91 92 93
	if *e != nil {
		return
	}
94 95 96
	*e = ma.Finish()
}

97
func AssembleMapEntry(e *error, ma ipld.MapAssembler, k string, fn func(va ipld.NodeAssembler)) {
98 99 100 101 102 103 104 105 106 107 108
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	fn(va)
}

109
func BuildList(e *error, np ipld.NodePrototype, sizeHint int64, fn func(la ipld.ListAssembler)) ipld.Node {
110 111 112
	if *e != nil {
		return nil
	}
113 114
	nb := np.NewBuilder()
	la, err := nb.BeginList(sizeHint)
115 116 117 118
	if err != nil {
		*e = err
		return nil
	}
119 120 121 122 123 124 125 126 127
	fn(la)
	if *e != nil {
		return nil
	}
	*e = la.Finish()
	if *e != nil {
		return nil
	}
	return nb.Build()
128 129
}

130
func AssembleList(e *error, na ipld.NodeAssembler, sizeHint int64, fn func(la ipld.ListAssembler)) {
131 132 133 134 135 136 137 138 139
	if *e != nil {
		return
	}
	la, err := na.BeginList(sizeHint)
	if err != nil {
		*e = err
		return
	}
	fn(la)
140 141 142
	if *e != nil {
		return
	}
143 144 145
	*e = la.Finish()
}

146
func AssembleListEntry(e *error, la ipld.ListAssembler, fn func(va ipld.NodeAssembler)) {
147 148 149 150 151 152
	if *e != nil {
		return
	}
	fn(la.AssembleValue())
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
func AssignNull(e *error, na ipld.NodeAssembler) {
	if *e != nil {
		return
	}
	*e = na.AssignNull()
}
func AssignBool(e *error, na ipld.NodeAssembler, x bool) {
	if *e != nil {
		return
	}
	*e = na.AssignBool(x)
}
func AssignInt(e *error, na ipld.NodeAssembler, x int64) {
	if *e != nil {
		return
	}
	*e = na.AssignInt(x)
}
func AssignFloat(e *error, na ipld.NodeAssembler, x float64) {
	if *e != nil {
		return
	}
	*e = na.AssignFloat(x)
}
func AssignString(e *error, na ipld.NodeAssembler, x string) {
	if *e != nil {
		return
	}
	*e = na.AssignString(x)
}
func AssignBytes(e *error, na ipld.NodeAssembler, x []byte) {
	if *e != nil {
		return
	}
	*e = na.AssignBytes(x)
}
func AssignLink(e *error, na ipld.NodeAssembler, x ipld.Link) {
	if *e != nil {
		return
	}
	*e = na.AssignLink(x)
}
func AssignNode(e *error, na ipld.NodeAssembler, x ipld.Node) {
	if *e != nil {
		return
	}
	*e = na.AssignNode(x)
}

// Assign takes any value and attempts to turn it into something we can reparse as Node-like,
// using the same logic as fluent.Reflect.
// It's not particularly performant, so use it only when convenience matters more than performance.
func Assign(e *error, na ipld.NodeAssembler, x interface{}) {
	if *e != nil {
		return
	}
	*e = fluent.ReflectIntoAssembler(na, x)
}

func AssignMapEntryNull(e *error, ma ipld.MapAssembler, k string) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignNull()
}
func AssignMapEntryBool(e *error, ma ipld.MapAssembler, k string, v bool) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignBool(v)
}
func AssignMapEntryInt(e *error, ma ipld.MapAssembler, k string, v int64) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignInt(v)
}
func AssignMapEntryFloat(e *error, ma ipld.MapAssembler, k string, v float64) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignFloat(v)
}
func AssignMapEntryString(e *error, ma ipld.MapAssembler, k string, v string) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignString(v)
}
func AssignMapEntryBytes(e *error, ma ipld.MapAssembler, k string, v []byte) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignBytes(v)
}
func AssignMapEntryLink(e *error, ma ipld.MapAssembler, k string, v ipld.Link) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignLink(v)
}
func AssignMapEntryNode(e *error, ma ipld.MapAssembler, k string, v ipld.Node) {
	if *e != nil {
		return
	}
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = va.AssignNode(v)
}

// AssignMapEntry takes any value and attempts to turn it into something we can reparse as Node-like,
// using the same logic as fluent.Reflect.
// It's not particularly performant, so use it only when convenience matters more than performance.
func AssignMapEntry(e *error, ma ipld.MapAssembler, k string, x interface{}) {
305 306 307
	if *e != nil {
		return
	}
308 309 310 311 312 313 314 315 316 317
	va, err := ma.AssembleEntry(k)
	if err != nil {
		*e = err
		return
	}
	*e = fluent.ReflectIntoAssembler(va, x)
}

func AssignListEntryNull(e *error, la ipld.ListAssembler) {
	if *e != nil {
318 319
		return
	}
320 321 322 323 324
	*e = la.AssembleValue().AssignNull()
}
func AssignListEntryBool(e *error, la ipld.ListAssembler, v bool) {
	if *e != nil {
		return
325
	}
326 327 328 329
	*e = la.AssembleValue().AssignBool(v)
}
func AssignListEntryInt(e *error, la ipld.ListAssembler, v int64) {
	if *e != nil {
330 331
		return
	}
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
	*e = la.AssembleValue().AssignInt(v)
}
func AssignListEntryFloat(e *error, la ipld.ListAssembler, v float64) {
	if *e != nil {
		return
	}
	*e = la.AssembleValue().AssignFloat(v)
}
func AssignListEntryString(e *error, la ipld.ListAssembler, v string) {
	if *e != nil {
		return
	}
	*e = la.AssembleValue().AssignString(v)
}
func AssignListEntryBytes(e *error, la ipld.ListAssembler, v []byte) {
	if *e != nil {
		return
	}
	*e = la.AssembleValue().AssignBytes(v)
}
func AssignListEntryLink(e *error, la ipld.ListAssembler, v ipld.Link) {
	if *e != nil {
		return
	}
	*e = la.AssembleValue().AssignLink(v)
}
func AssignListEntryNode(e *error, la ipld.ListAssembler, v ipld.Node) {
	if *e != nil {
		return
	}
	*e = la.AssembleValue().AssignNode(v)
}

// AssignListEntry takes any value and attempts to turn it into something we can reparse as Node-like,
// using the same logic as fluent.Reflect.
// It's not particularly performant, so use it only when convenience matters more than performance.
func AssignListEntry(e *error, la ipld.ListAssembler, x interface{}) {
	if *e != nil {
		return
371
	}
372
	*e = fluent.ReflectIntoAssembler(la.AssembleValue(), x)
373
}