func TopologicalSort(vertices []*vertex.Vertex) ([]*vertex.Vertex, error) {
	const op = errors.Op("graph_topological_sort")
	var ord []*vertex.Vertex
	verticesCopy := vertices
	buf := new(strings.Builder)
	defer buf.Reset()
	for len(verticesCopy) != 0 {
		vrtx := verticesCopy[len(verticesCopy)-1]
		verticesCopy = verticesCopy[:len(verticesCopy)-1]
		containsCycle := dfs(vrtx, &ord)
		if containsCycle {
			buf.WriteString("The following vertices involved:\n")
			for i := len(vertices) - 1; i > 0; i-- {
				if !vertices[i].Visited {
					buf.WriteString(fmt.Sprintf("vertex: %s -> ", vertices[i].ID))
				}
			}
			return nil, errors.E(op, errors.Errorf("cycle detected, please, check the path: %s", strings.TrimRight(buf.String(), "-> ")))
		}
	}
	return ord, nil
}
TopologicalSort topologically sort the graph and return slice of the sorted vertices
package graph
import (
	"fmt"
	"reflect"
	"strings"
	"github.com/spiral/endure/pkg/fsm"
	"github.com/spiral/endure/pkg/vertex"
	"github.com/spiral/errors"
)
type Kind int
const (
	Init Kind = iota
	Collects
)
type Graph struct {
	VerticesMap map[string]*vertex.Vertex
	Vertices []*vertex.Vertex
	Providers map[string]reflect.Value
	InitialProviders map[string]reflect.Value
}
func NewGraph() *Graph {
	return &Graph{
		VerticesMap: make(map[string]*vertex.Vertex),
		Providers:   make(map[string]reflect.Value),
		Vertices:    make([]*vertex.Vertex, 0),
	}
}
func (g *Graph) AddGlobalProvider(providedID string, val reflect.Value) {
	g.Providers[providedID] = val
}
func (g *Graph) HasVertex(name string) bool {
	_, ok := g.VerticesMap[name]
	return ok
}
func (g *Graph) ClearState() {
	g.Providers = make(map[string]reflect.Value)
	g.VerticesMap = make(map[string]*vertex.Vertex)
	g.Vertices = make([]*vertex.Vertex, 0)
}
func (g *Graph) AddInterfaceDep(vertex *vertex.Vertex, depID string, method Kind, isRef bool) error {
	const op = errors.Op("endure_add_interface_dep")
	depVertex := g.FindProviders(depID)
	if depVertex == nil {
		return errors.E(op, errors.Errorf("can't find dependency: %s for the vertex: %s", depID, vertex.ID))
	}
	if depVertex.ID == vertex.ID {
		return nil
	}
	if !g.addToList(method, vertex, depID, isRef, depVertex.ID, reflect.Interface) {
		return nil
	}
	for j := 0; j < len(depVertex.Dependencies); j++ {
		tmpID := depVertex.Dependencies[j].ID
		if tmpID == vertex.ID {
			return nil
		}
	}
	vertex.NumOfDeps++
	vertex.Dependencies = append(vertex.Dependencies, depVertex)
	return nil
}
func (g *Graph) AddStructureDep(vertex *vertex.Vertex, depID string, method Kind, isRef bool) error {
	const op = errors.Op("endure_add_structure_dep")
	depVertex := g.GetVertex(depID)
	if depVertex == nil {
		tmp := g.FindProviders(depID)
		if tmp == nil {
			return errors.E(op, errors.Errorf("can't find dep: %s for the vertex: %s", depID, vertex.ID))
		}
		depVertex = tmp
	}
	if !g.addToList(method, vertex, depID, isRef, depVertex.ID, reflect.Struct) {
		return nil
	}
	for i := 0; i < len(depVertex.Dependencies); i++ {
		tmpID := depVertex.Dependencies[i].ID
		if tmpID == vertex.ID {
			return nil
		}
	}
	vertex.NumOfDeps++
	vertex.Dependencies = append(vertex.Dependencies, depVertex)
	return nil
}
func (g *Graph) addToList(method Kind, vrtx *vertex.Vertex, depID string, isRef bool, refID string, kind reflect.Kind) bool {
	switch method {
	case Init:
		addInit(vrtx, refID, depID, isRef, kind)
	case Collects:
		if _, ok := vrtx.Meta.CollectsDepsToInvoke[refID]; ok {
			return false
		}
		vrtx.Meta.CollectsDepsToInvoke[refID] = append(vrtx.Meta.CollectsDepsToInvoke[refID], vertex.Entry{
			RefID:       refID,
			Name:        depID,
			IsReference: &isRef,
			Kind:        kind,
		})
		contains := false
		for _, v := range vrtx.Meta.InitDepsOrd {
			if v == refID {
				contains = true
			}
		}
		if !contains {
			vrtx.Meta.InitDepsOrd = append(vrtx.Meta.InitDepsOrd, refID)
		}
	}
	return true
}
func addInit(vrtx *vertex.Vertex, refID string, depID string, isRef bool, kind reflect.Kind) {
	vrtx.Meta.InitDepsToInvoke[refID] = append(vrtx.Meta.InitDepsToInvoke[refID], vertex.Entry{
		RefID:       refID,
		Name:        depID,
		IsReference: &isRef,
		Kind:        kind,
	})
	contains := false
	for _, v := range vrtx.Meta.InitDepsOrd {
		if v == refID {
			contains = true
		}
	}
	if !contains {
		vrtx.Meta.InitDepsOrd = append(vrtx.Meta.InitDepsOrd, refID)
	}
}
func (g *Graph) Reset(vrtx *vertex.Vertex) []*vertex.Vertex {
	vrtx.NumOfDeps = len(vrtx.Dependencies)
	vrtx.Visiting = false
	vrtx.Visited = false
	vrtx.SetState(fsm.Uninitialized)
	vertices := make([]*vertex.Vertex, 0, 5)
	vertices = append(vertices, vrtx)
	tmp := make(map[string]*vertex.Vertex)
	g.depthFirstSearch(vrtx.Dependencies, tmp)
	for _, v := range tmp {
		vertices = append(vertices, v)
	}
	return vertices
}
func (g *Graph) depthFirstSearch(deps []*vertex.Vertex, tmp map[string]*vertex.Vertex) {
	for i := 0; i < len(deps); i++ {
		deps[i].Visited = false
		deps[i].Visiting = false
		deps[i].NumOfDeps = len(deps)
		tmp[deps[i].ID] = deps[i]
		g.depthFirstSearch(deps[i].Dependencies, tmp)
	}
}
func (g *Graph) AddVertex(vertexID string, vertexIface interface{}) {
	v := vertex.NewVertex()
	v.ID = vertexID
	v.Iface = vertexIface
	v.SetState(fsm.Uninitialized)
	m := vertex.NewMeta()
	v.Meta = m
	g.VerticesMap[vertexID] = v
	g.Vertices = append(g.Vertices, g.VerticesMap[vertexID])
}
func (g *Graph) GetVertex(id string) *vertex.Vertex {
	return g.VerticesMap[id]
}
func (g *Graph) FindProviders(depID string) *vertex.Vertex {
	for i := 0; i < len(g.Vertices); i++ {
		for providerID := range g.Vertices[i].Provides {
			if depID == providerID {
				return g.Vertices[i]
			}
		}
	}
	if _, ok := g.VerticesMap[depID]; ok {
		return g.VerticesMap[depID]
	}
	return nil
}