main.go (6968B)
1 /* This is a project of a new static site generator */ 2 /* Eventually, this might replace SSG for me */ 3 4 /*****************************************************************/ 5 /* DONE: Basic functionality for converting markdown/org to html */ 6 /* DONE: Recursively mirroring dir structure of source files */ 7 /* DONE: Prepend and append header and footer on HTML files */ 8 /* DONE: Ignore non-valid file types and directories in the src */ 9 /* DONE: Make the template file not be static */ 10 /* TODO: RSS generator built in */ 11 /* TODO: Sitemap generator */ 12 /* TODO: Add some better exit codes that actually make sense */ 13 /*****************************************************************/ 14 /* OPTIONAL ARGUMENTS: */ 15 /* TODO -j: Set how many concurrent jobs can run | DIFFICULT */ 16 /* DONE -h: Display help */ 17 /* TODO -r: Do not generate RSS feed */ 18 /* TODO -s: Do not generate sitemap */ 19 /* DONE -t: Use template file for HTML */ 20 /* DONE -v: Show program version number */ 21 /*****************************************************************/ 22 23 package main 24 25 import ( 26 "fmt" // For formatting some outputs 27 "os" // File system and arguments 28 "os/exec" // For running OS operations 29 "path" // For removing and detecting file extensions 30 "regexp" // For matching directories 31 ) 32 33 /* Version Number */ 34 const version = "0.0.4: Almost" 35 36 /* This is a test function to try to copy a directory */ 37 func DirSetup(source string, destination, invalidDir, template string) error { 38 39 srcInfo, err := os.Stat(source) 40 if os.IsNotExist(err) { 41 fmt.Fprintf(os.Stderr, "No such directory \"%v\" found!\n", source) 42 // Should probably change the exit codes at some point 43 os.Exit(1) 44 } 45 if err != nil { 46 return err 47 } 48 49 err = os.MkdirAll(destination, srcInfo.Mode()) 50 if err != nil { 51 return err 52 } 53 54 entries, err := os.ReadDir(source) 55 if err != nil { 56 return err 57 } 58 59 /* Based around solution presented here: */ 60 /* https://gistlib.com/go/copy-a-directory-in-go */ 61 for _, entry := range entries { 62 srcPath := source + "/" + entry.Name() 63 dstPath := destination + "/" + entry.Name() 64 65 /* Ignore specified directories */ 66 re := regexp.MustCompile(invalidDir) 67 if re.MatchString(destination) { 68 fmt.Println("Skipping directory:", srcPath) 69 return nil 70 } 71 72 if entry.IsDir() { 73 err = DirSetup(srcPath, dstPath, invalidDir, template) 74 if err != nil { 75 return err 76 } 77 } else { 78 err = ConvertFile(source, entry.Name(), destination, template) 79 if err != nil { 80 return err 81 } 82 /* Creating the RSS file that will be appended for each article */ 83 //rssFile, err := os.OpenFile(destination + "/" + "rss.xml", os.O_CREATE|os.O_WRONLY, 0664) 84 if err != nil { 85 return err 86 } 87 88 rssFile := destination + "/" + "rss.xml" 89 openRSS, err := os.OpenFile(destination + "/" + "rss.xml", os.O_CREATE|os.O_WRONLY, 0664) 90 if err != nil { 91 panic(err) 92 } 93 rssHeader := "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\"\n<channel>\n" 94 _, err = openRSS.WriteString(rssHeader) 95 if err != nil { 96 panic(err) 97 } 98 rssGen(source, entry.Name(), rssFile) 99 } 100 } 101 102 return nil 103 } 104 105 func rssGen(filepath, filename, destination string) { 106 107 cmd := exec.Command("pandoc", "-thtml", filepath + "/" + filename) 108 rssFile, err := os.OpenFile(destination, os.O_APPEND|os.O_WRONLY, 0664) 109 if err != nil { 110 panic(err) 111 } 112 113 output, err := cmd.Output() 114 if err != nil { 115 fmt.Println("Error:", err) 116 panic(err) 117 } 118 119 _, err = rssFile.WriteString("<article>\n") 120 if err != nil { 121 panic(err) 122 } 123 _, err = rssFile.WriteString(string(output)) 124 if err != nil { 125 panic(err) 126 } 127 _, err = rssFile.WriteString("</article>\n") 128 if err != nil { 129 panic(err) 130 } 131 132 } 133 134 func RemoveExt(filename string) string { 135 return filename[:len(filename)-len(path.Ext(filename))] 136 } 137 138 /* This function converts the markdown files using pandoc and allows for a user defined template */ 139 /* with pandoc's `--template` flag. A default should be included as an example, and will potentially */ 140 /* need to be maintained over time as pandoc changes. The function will then put the output string */ 141 /* Into the corresponding file */ 142 143 func ConvertFile(filepath, filename, destination, template string) error { 144 145 validFile := path.Ext(filename) 146 147 /* Skipping invalid file types */ 148 /* TODO: Add support for .org files */ 149 if validFile != ".md" { 150 fmt.Println("Skipping file:", filename) 151 return nil 152 } 153 154 fmt.Println("Converting following file to HTML:", filename) 155 oFile := RemoveExt(filename) 156 cmd := exec.Command("pandoc", "-s", "--template", template, filepath + "/" + filename) 157 158 /* Changing execution command if template is NULL */ 159 if template == os.DevNull { 160 cmd = exec.Command("pandoc", filepath + "/" + filename) 161 } 162 163 output, err := cmd.Output() 164 if err != nil { 165 fmt.Println("Error:", err) 166 return err 167 } 168 file, err := os.Create(destination + "/" + oFile + ".html") 169 if err != nil { 170 panic(err) 171 } 172 defer file.Close() 173 174 _, err = file.WriteString(string(output)) 175 if err != nil { 176 panic(err) 177 } 178 179 return nil 180 } 181 182 func CallHelp() { 183 fmt.Println("Usage: gengo [OPTION]... SOURCE DEST") 184 /* Redundant newline is purposeful */ 185 fmt.Println("Automatically convert valid files into HTML using Pandoc.\n") 186 fmt.Println(" -h, --help: Print this help screen") 187 fmt.Println(" -i, --ignore: Don't process directories that match a string") 188 fmt.Println(" -t, --template: Use existing HTML template (Pandoc formatting)") 189 fmt.Println(" -v, --version: Prints version information for gengo") 190 } 191 192 func ArgParser (argument[] string) { 193 194 cArgs := len(argument) 195 /* Setting some default values in case user does not want to use these options */ 196 ignoreDir := os.DevNull 197 template := os.DevNull 198 var srcDir, destDir string 199 200 for i := 0; i < cArgs; i++ { 201 switch argument[i] { 202 case "-h", "--help": 203 CallHelp() 204 os.Exit(0) 205 case "-i", "--ignore": 206 ignoreDir = argument[i + 1] 207 i++ 208 case "-t", "--template": 209 template = argument[i + 1] 210 i++ 211 case "-v", "--version": 212 println("Version:", version) 213 os.Exit(0) 214 default: 215 /* Fail if less than two arguments are passed */ 216 /* Must have at least source and dest for application to work */ 217 if (len(argument) < 2) { 218 println("Not enough arguments!") 219 CallHelp() 220 os.Exit(1) 221 } 222 srcDir = argument[i] 223 destDir = argument[i + 1] 224 i++ 225 } 226 } 227 DirSetup(srcDir, destDir, ignoreDir, template) 228 } 229 230 func main() { 231 arguments := os.Args[1:] 232 233 /* Fail if no arguments are passed */ 234 if (len(arguments) == 0) { 235 println("Not enough arguments!") 236 CallHelp() 237 os.Exit(1) 238 } 239 240 /* Parse the various options to figure out what we are doing */ 241 ArgParser(arguments) 242 }