aboutsummaryrefslogtreecommitdiffstats
path: root/config/modules.go
blob: 4d98b16e0c14ea5f1cf74a5075203f46aecfd35f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package config

import (
	"bytes"
	"errors"

	format "srcd.works/go-git.v4/plumbing/format/config"
)

var (
	ErrModuleEmptyURL  = errors.New("module config: empty URL")
	ErrModuleEmptyPath = errors.New("module config: empty path")
)

// Modules defines the submodules properties, represents a .gitmodules file
// https://www.kernel.org/pub/software/scm/git/docs/gitmodules.html
type Modules struct {
	// Submodules is a map of submodules being the key the name of the submodule
	Submodules map[string]*Submodule

	raw *format.Config
}

// NewModules returns a new empty Modules
func NewModules() *Modules {
	return &Modules{
		Submodules: make(map[string]*Submodule, 0),
		raw:        format.New(),
	}
}

const (
	submoduleSection = "submodule"
	pathKey          = "path"
	branchKey        = "branch"
)

// Unmarshal parses a git-config file and stores it
func (m *Modules) Unmarshal(b []byte) error {
	r := bytes.NewBuffer(b)
	d := format.NewDecoder(r)

	m.raw = format.New()
	if err := d.Decode(m.raw); err != nil {
		return err
	}

	s := m.raw.Section(submoduleSection)
	for _, sub := range s.Subsections {
		mod := &Submodule{}
		mod.unmarshal(sub)

		m.Submodules[mod.Path] = mod
	}

	return nil
}

// Marshal returns Modules encoded as a git-config file
func (m *Modules) Marshal() ([]byte, error) {
	s := m.raw.Section(submoduleSection)
	s.Subsections = make(format.Subsections, len(m.Submodules))

	var i int
	for _, r := range m.Submodules {
		s.Subsections[i] = r.marshal()
		i++
	}

	buf := bytes.NewBuffer(nil)
	if err := format.NewEncoder(buf).Encode(m.raw); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// Submodule defines a submodule
type Submodule struct {
	// Name module name
	Name string
	// Path defines the path, relative to the top-level directory of the Git
	// working tree,
	Path string
	// URL defines a URL from which the submodule repository can be cloned.
	URL string
	// Branch is a remote branch name for tracking updates in the upstream
	// submodule. Optional value.
	Branch string

	// raw representation of the subsection, filled by marshal or unmarshal are
	// called
	raw *format.Subsection
}

// Validate validates the fields and sets the default values
func (m *Submodule) Validate() error {
	if m.Path == "" {
		return ErrModuleEmptyPath
	}

	if m.URL == "" {
		return ErrModuleEmptyURL
	}

	return nil
}

func (m *Submodule) unmarshal(s *format.Subsection) {
	m.raw = s

	m.Name = m.raw.Name
	m.Path = m.raw.Option(pathKey)
	m.URL = m.raw.Option(urlKey)
	m.Branch = m.raw.Option(branchKey)
}

func (m *Submodule) marshal() *format.Subsection {
	if m.raw == nil {
		m.raw = &format.Subsection{}
	}

	m.raw.Name = m.Name
	if m.raw.Name == "" {
		m.raw.Name = m.Path
	}

	m.raw.SetOption(pathKey, m.Path)
	m.raw.SetOption(urlKey, m.URL)

	if m.Branch != "" {
		m.raw.SetOption(branchKey, m.Branch)
	}

	return m.raw
}