Skip to content

Commit a454d6f

Browse files
committed
Implement (maybe inefficient) compilation
1 parent c019ddb commit a454d6f

1 file changed

Lines changed: 89 additions & 30 deletions

File tree

installer/compiler.go

Lines changed: 89 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,93 @@
11
package installer
22

33
import (
4+
"bytes"
45
"io"
6+
"io/ioutil"
57
"os"
68

79
"go.evanpurkhiser.com/dots/config"
810
"go.evanpurkhiser.com/dots/resolver"
911
)
1012

11-
// mustCompile indicates if a dotfile msut be compiled, or if a single-source
12-
// dotfile does not require any transformations and may be directly installed.
13-
func shouldCompile(dotfile *resolver.Dotfile, config config.SourceConfig) bool {
14-
return len(dotfile.Sources) > 1
13+
type dotfileCompiler struct {
14+
dotfile *resolver.Dotfile
15+
content *bytes.Buffer
16+
compiled bool
17+
config config.SourceConfig
18+
files []*os.File
19+
}
20+
21+
// ensureCompiled transforms the source dotfiles into the dotfile output. It
22+
// will not recompile if the dotfile has already been compiled.
23+
func (c *dotfileCompiler) ensureCompiled() error {
24+
if c.compiled {
25+
return nil
26+
}
27+
28+
compiledData := []byte{}
29+
30+
for i, sourceFile := range c.files {
31+
data, err := ioutil.ReadAll(sourceFile)
32+
if err != nil {
33+
return err
34+
}
35+
36+
// 1. Always trim whitespace off of the source file.
37+
data = trimWhitespace(data)
38+
39+
// 2. For any source file that procedes after the first, trim shebang
40+
// markers for cleanlyness of bash configurations. We trim whitespace
41+
// again to remove any space after the shebang.
42+
if i != 0 {
43+
data = trimShebang(data)
44+
data = trimWhitespace(data)
45+
}
46+
47+
// Combine files with *one* blank line between them
48+
if i != 0 {
49+
compiledData = append(compiledData, '\n', '\n')
50+
}
51+
52+
compiledData = append(compiledData, data...)
53+
}
54+
55+
// 3. Expand environment variables if the dotfile was marked.
56+
if c.dotfile.ExpandEnv {
57+
compiledData = expandEnvironment(compiledData)
58+
}
59+
60+
// 4. All files should end with a single newline
61+
compiledData = append(compiledData, '\n')
62+
63+
// Store the compiled dotfile
64+
c.compiled = true
65+
c.content.Reset()
66+
c.content.Write(compiledData)
67+
68+
return nil
69+
}
70+
71+
// Read implements the io.Reader interface. Calling read will compile the
72+
// dotfile into it's final byte slice.
73+
func (c *dotfileCompiler) Read(p []byte) (int, error) {
74+
c.ensureCompiled()
75+
return c.content.Read(p)
76+
}
77+
78+
// Close implments the io.Closer interface. Calling close will close all source
79+
// files associated to the dotfile.
80+
func (c *dotfileCompiler) Close() error {
81+
var err error
82+
83+
for _, file := range c.files {
84+
closeErr := file.Close()
85+
if closeErr != nil {
86+
err = closeErr
87+
}
88+
}
89+
90+
return err
1591
}
1692

1793
// OpenDotfile opens a source dotfile for streaming compilation.
@@ -29,41 +105,24 @@ func OpenDotfile(dotfile *resolver.Dotfile, config config.SourceConfig) (io.Read
29105

30106
compiler := &dotfileCompiler{
31107
dotfile: dotfile,
108+
content: bytes.NewBuffer(nil),
32109
config: config,
33110
files: files,
34111
}
35112

36113
return compiler, nil
37114
}
38115

39-
type dotfileCompiler struct {
40-
dotfile *resolver.Dotfile
41-
config config.SourceConfig
42-
files []*os.File
43-
}
44-
45-
// TODO: If we want we can make this thing do caching of compiled dotfiels if
46-
// we expect to install them later
47-
func (c *dotfileCompiler) Read(p []byte) (n int, err error) {
48-
readers := []io.Reader{}
49-
50-
for _, file := range c.files {
51-
readers = append(readers, file)
116+
// mustCompile indicates if a dotfile must be compiled, or if a single-source
117+
// dotfile does not require any transformations and may be directly installed.
118+
func shouldCompile(dotfile *resolver.Dotfile, config config.SourceConfig) bool {
119+
if len(dotfile.Sources) > 1 {
120+
return true
52121
}
53122

54-
// TODO: Implement filtered reading
55-
return io.MultiReader(readers...).Read(p)
56-
}
57-
58-
func (c *dotfileCompiler) Close() error {
59-
var err error
60-
61-
for _, file := range c.files {
62-
closeErr := file.Close()
63-
if closeErr != nil {
64-
err = closeErr
65-
}
123+
if dotfile.ExpandEnv {
124+
return true
66125
}
67126

68-
return err
127+
return false
69128
}

0 commit comments

Comments
 (0)