package data import ( "fmt" "github.com/anytypeio/go-anytype-infrastructure-experiments/data/threadmodels" ) type AccountData struct { Identity string EncKey threadmodels.EncryptionPrivKey } type Document struct { thread threadmodels.Thread stateProvider InitialStateProvider accountData *AccountData decoder threadmodels.SigningPubKeyDecoder treeBuilder *TreeBuilder aclTreeBuilder *ACLTreeBuilder aclStateBuilder *ACLStateBuilder snapshotValidator *SnapshotValidator docStateBuilder *documentStateBuilder docContext *documentContext } type UpdateResult int const ( UpdateResultNoAction = iota UpdateResultAppend UpdateResultRebuild ) func NewDocument( thread threadmodels.Thread, stateProvider InitialStateProvider, accountData *AccountData) *Document { decoder := threadmodels.NewEd25519Decoder() return &Document{ thread: thread, stateProvider: stateProvider, accountData: accountData, decoder: decoder, aclTreeBuilder: NewACLTreeBuilder(thread, decoder), treeBuilder: NewTreeBuilder(thread, decoder), snapshotValidator: NewSnapshotValidator(decoder, accountData), aclStateBuilder: NewACLStateBuilder(decoder, accountData), docStateBuilder: newDocumentStateBuilder(stateProvider), docContext: &documentContext{}, } } func (d *Document) Update(changes ...*threadmodels.RawChange) (DocumentState, UpdateResult, error) { var treeChanges []*Change for _, ch := range changes { aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch) if err != nil { return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err) } treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange) treeChanges = append(treeChanges, treeChange) // this already sets MaybeHeads to include new changes // TODO: change this behaviour as non-obvious, because it is not evident from the interface err = d.thread.AddChange(ch) if err != nil { return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err) } } for _, ch := range treeChanges { if ch.IsACLChange() { res, err := d.Build() return res, UpdateResultRebuild, err } } prevHeads := d.docContext.fullTree.Heads() mode := d.docContext.fullTree.Add(treeChanges...) switch mode { case Nothing: return d.docContext.docState, UpdateResultNoAction, nil case Rebuild: res, err := d.Build() return res, UpdateResultRebuild, err default: break } // because for every new change we know it was after any of the previous heads // each of previous heads must have same "Next" nodes // so it doesn't matter which one we choose // so we choose first one newState, err := d.docStateBuilder.appendFrom(prevHeads[0]) if err != nil { res, _ := d.Build() return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded") } // setting all heads d.thread.SetHeads(d.docContext.fullTree.Heads()) // this should be the entrypoint when we build the document d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) return newState, UpdateResultAppend, nil } func (d *Document) Build() (DocumentState, error) { return d.build(false) } func (d *Document) build(fromStart bool) (DocumentState, error) { d.treeBuilder.Init() d.aclTreeBuilder.Init() var err error d.docContext.fullTree, err = d.treeBuilder.Build(fromStart) if err != nil { return nil, err } d.docContext.aclTree, err = d.aclTreeBuilder.Build() if err != nil { return nil, err } if !fromStart { err = d.snapshotValidator.Init(d.docContext.aclTree) if err != nil { return nil, err } valid, err := d.snapshotValidator.ValidateSnapshot(d.docContext.fullTree.root) if err != nil { return nil, err } if !valid { return d.build(true) } } err = d.aclStateBuilder.Init(d.docContext.fullTree) if err != nil { return nil, err } d.docContext.aclState, err = d.aclStateBuilder.Build() if err != nil { return nil, err } d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree) d.docContext.docState, err = d.docStateBuilder.build() if err != nil { return nil, err } // setting all heads d.thread.SetHeads(d.docContext.fullTree.Heads()) // this should be the entrypoint when we build the document d.thread.SetMaybeHeads(d.docContext.fullTree.Heads()) return d.docContext.docState, nil } func (d *Document) State() DocumentState { return nil }