Skip to content
Snippets Groups Projects
sync.go 4.61 KiB
Newer Older
package html

import (
	"fmt"
	"github.com/andybalholm/cascadia"
	"gitlab.schukai.com/oss/bob/types"
	"gitlab.schukai.com/oss/libraries/go/markup/html/engine"
	"golang.org/x/net/html"
	"gopkg.in/yaml.v3"
	"os"
	"path/filepath"
)

type Specification struct {
	Selector string
}

func readHTML(p string) (*html.Node, error) {
	htmlFile, err := os.Open(p)
	if err != nil {
		return nil, err
	}
	defer htmlFile.Close()

	return html.Parse(htmlFile)
}

func SyncHtml(p string) error {

	content, err := os.ReadFile(p)
	if err != nil {
		return err
	}

	currentDir, _ := os.Getwd()
	os.Chdir(filepath.Dir(p))
	defer os.Chdir(currentDir)

	specification := types.SyncSpecification{}

	if err := yaml.Unmarshal(content, &specification); err != nil {
		return err
	}

	sourceFiles := make(map[string]*html.Node)
	destinationFiles := make(map[string]*html.Node)
	destinationFile := make(map[string][]string)

	for _, r := range specification.Sync {

		source := r.Source
		destination := r.Destination
		exclude := r.Destination.Exclude

		absSource, err := filepath.Abs(source.Path)
		if err != nil {
			return err
		}

		if err != nil {
			return err
		}

		if sourceFiles[absSource], err = readHTML(absSource); err != nil {
			return err
		}

		for _, d := range destination.Path {

			d, err := filepath.Abs(d)
			if err != nil {
				return err
			}

			fileinfo, err := os.Stat(d)
			if err != nil {
				return err
			}

			if fileinfo.IsDir() {
				filepath.Walk(d, func(pp string, info os.FileInfo, err error) error {
					if err != nil {
						return err
					}
					if info.IsDir() {
						return nil
					}

					if exclude != nil {
						for _, e := range exclude {
							e, err := filepath.Abs(e)
							if err != nil {
								return err
							}
							if e == pp {
								return nil
							}
						}
					}

					ext := filepath.Ext(pp)
					if ext == ".html" {

						var dd *html.Node
						if dd, err = readHTML(pp); err != nil {
							return err
						}

						destinationFiles[pp] = dd
						destinationFile[absSource] = append(destinationFile[absSource], pp)

					}

					return nil
				})
			} else if filepath.Ext(d) == ".html" {

				if exclude != nil {
					for _, e := range exclude {
						e, err := filepath.Abs(e)
						if err != nil {
							return err
						}
						if e == d {
							return nil
						}
					}
				}

				var dd *html.Node
				if dd, err = readHTML(d); err != nil {
					return err
				}

				destinationFiles[d] = dd
				destinationFile[absSource] = append(destinationFile[absSource], d)

			}

		}

		for _, r := range specification.Sync {
			source := r.Source
			absSource, err := filepath.Abs(source.Path)
			if err != nil {
				return err
			}
			sourceSelector := source.Selector

			query, err := cascadia.Compile(sourceSelector)
			if err != nil {
				return err
			}

			sourceNode := query.MatchFirst(sourceFiles[absSource])

			dp := destinationFile[absSource]
			for _, d := range dp {
				destinationSelector := r.Destination.Selector
				if destinationSelector == "" {
					destinationSelector = sourceSelector
				}

				keepMap := make(map[string][]*html.Node)

				for _, n := range r.Destination.Keep {
					q, err := cascadia.Compile(n)
					if err != nil {
						return err
					}

					kNode := q.MatchAll(destinationFiles[d])
					if kNode == nil {
						return fmt.Errorf("keep node not found: %s", n)
					}

					for _, k := range kNode {
Volker Schukai's avatar
Volker Schukai committed
						keepMap[n] = append(keepMap[n], k)
				query, err := cascadia.Compile(destinationSelector)
				if err != nil {
					return err
				}
				destinationData := query.MatchFirst(destinationFiles[d])

				if destinationData == nil {
					return fmt.Errorf("could not find selector %s in %s", destinationSelector, d)
				}

				n := engine.CloneNode(sourceNode)
				destinationData.Parent.InsertBefore(n, destinationData)
				destinationData.Parent.RemoveChild(destinationData)

				for sel, k := range keepMap {
					cas, err := cascadia.Compile(sel)
					if err != nil {
						return err
					}
					x := cas.MatchAll(destinationFiles[d])
					if x == nil {
						return fmt.Errorf("could not find selector %s in %s", sel, d)
					}

					for _, n1 := range x {

						for _, kk := range k {

							// node already removed, for example, by a previous keep
							if n1.Parent == nil {
								continue
							}

Volker Schukai's avatar
Volker Schukai committed
							n1.Parent.InsertBefore(engine.CloneNode(kk), n1)
			}

		}

		for p, d := range destinationFiles {
			fp, err := os.Create(p)
			if err != nil {
				return err
			}
			err = html.Render(fp, d)
			err2 := fp.Close()
			if err2 != nil {
				return err2
			}

			if err != nil {
				return err
			}

		}

	}

	return nil
}