github.com/ipld/go-ipld-prime@v0.21.0/adl/rot13adl/rot13reification.go (about)

     1  package rot13adl
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/ipld/go-ipld-prime/datamodel"
     7  )
     8  
     9  // Reify examines data in a Node to see if it matches the shape for valid substrate data for this ADL,
    10  // and if so, synthesizes and returns the high-level view of the ADL.
    11  // If it succeeds in recognizing the raw data as this ADL,
    12  // Reify returns a new Node which exhibits the logical behaviors of the ADL;
    13  // otherwise, it returns an error.
    14  //
    15  // The input data can be any implementation of ipld.Node;
    16  // it will be considered purely through that interface.
    17  //
    18  // If your application is expecting ADL data, this pipeline can be optimized
    19  // by using the SubstratePrototype right from the start when unmarshalling;
    20  // then, Reify can detect if the rawRoot parameter is of that implementation,
    21  // and it can save some processing work internally that can be known to already be done.
    22  //
    23  // Reification will generally operate on the data in a single block
    24  // (e.g. this function will not do any additional block loads and unmarshalling).
    25  // This is important because some ADLs handle data so large that loading it all
    26  // eagerly would be impractical (and in some cases outright impossible).
    27  // However, it also necessarily implies that invalid data may lie beyond
    28  // one of those lazy loads, and it won't be discovered at the time of Reify.
    29  //
    30  // In this demo ADL, we don't have multi-block content at all,
    31  // so of course we don't have any additional block loads!
    32  // However, ADL implementations may vary in their approaches to lazy vs eager loading.
    33  // All ADLs should document their exact semantics regarding this --
    34  // especially if it has any implications for boundaries of data validity checking.
    35  //
    36  // REVIEW: this function is currently not conforming to any particular interface;
    37  // if we evolve the contract for ADLs to include an interface for reficiation functions,
    38  // might we need to add context and link loader systems as parameters to it?
    39  // Not all implementations might need it, as per previous paragraph; but some might.
    40  // Reification for multiblock ADLs might also need link loader systems as a parameter here
    41  // so they can capture them as config and hold them for use in future operations that do lazy loading.
    42  func Reify(maybeSubstrateRoot datamodel.Node) (datamodel.Node, error) {
    43  	// Reify is often very easy to implement,
    44  	//  especially if you have an IPLD Schema that specifies the shape of the substrate data:
    45  	// We can just check if the data in maybeSubstrateRoot happens to already be exactly the right type,
    46  	//  and if so, take very direct shortcuts because we already know its been validated in shape;
    47  	// otherwise, we create a new piece of memory for our native substrate memory layout,
    48  	//  and assign into it from the raw node, validating in the process,
    49  	//   which again just leans directly on the shape validation logic already given to us by the schema logic on that type.
    50  	// (Checking the concrete type of maybeSubstrateRoot in search of a shortcut is seemingly a tad redundant,
    51  	//  because the AssignNode path later also has such a check!
    52  	//  However, doing it earlier allows us to avoid an allocation;
    53  	//   the AssignNode path doesn't become available until after NewBuilder is invoked, and NewBuilder is where allocations happen.)
    54  
    55  	// Check if we can recognize the maybeSubstrateRoot as being our own substrate types;
    56  	//  if it is, we can shortcut pretty drastically.
    57  	if x, ok := maybeSubstrateRoot.(*_Substrate); ok {
    58  		// In this ADL implementation, the high level node has the exact same memory layout as the substrate root,
    59  		//  and so our only remaining processing here is just to cast them, so that
    60  		//   the node we return has the correct methodset exposed.
    61  		return (*_R13String)(x), nil
    62  	}
    63  
    64  	// Shortcut didn't work.  Process via the data model.
    65  	//  The AssignNode method on the substrate type already contains all the logic necessary for this, so we use that.
    66  	nb := Prototype.SubstrateRoot.NewBuilder()
    67  	if err := nb.AssignNode(maybeSubstrateRoot); err != nil {
    68  		return nil, fmt.Errorf("rot13adl.Reify failed: data does not match expected shape for substrate: %w", err)
    69  	}
    70  	return (*_R13String)(nb.Build().(*_Substrate)), nil
    71  }