// Main is an entry point of the subcommand (tool). func main(hs []tool.Handler, i int, args tool.Data) { // The first argument in the list is a path. // If it's missing use an empty string instead. p := args.GetDefault(0, "") // Prepare source and destination directory paths. src, err := path.ImportToAbsolute("github.com/colegion/goal/internal/skeleton") log.AssertNil(err) destImp, err := path.CleanImport(p) log.AssertNil(err) dest, err := path.ImportToAbsolute(destImp) log.AssertNil(err) // Make sure the requested import path (dest) does not exist yet. if _, err := os.Stat(dest); !os.IsNotExist(err) { log.Error.Panicf(`Cannot use "%s", such import path already exists.`, destImp) } // Scan the skeleton directory and get a list of directories / files // to be copied / processed. res, err := walk(src) log.AssertNil(err) // Create the directories in destination path. for i := 0; i < len(res.dirs); i++ { err = os.MkdirAll(filepath.Join(dest, res.dirs[i]), 0755) log.AssertNil(err) } // Copy static files to the destination directories. for i := 0; i < len(res.files); i++ { copyFile(res.files[i].absolute, filepath.Join(dest, res.files[i].relative)) } // Process source files and copy to the destination directories. for i := 0; i < len(res.srcs); i++ { copyModifiedFile( res.srcs[i].absolute, filepath.Join(dest, res.srcs[i].relative), [][][]byte{ { []byte("github.com/colegion/goal/internal/skeleton"), []byte(destImp), }, }, ) } log.Info.Printf(info, destImp) }
func TestStart(t *testing.T) { dst := "./testdata/project" main(handlers, 0, tool.Data{dst}) rs1, fn1 := walkFunc(dst) filepath.Walk(dst, fn1) p, err := path.ImportToAbsolute("github.com/colegion/goal/internal/skeleton") if err != nil { t.Error(err) t.FailNow() } rs2, err := walk(p) if err != nil { t.Error(err) t.FailNow() } if len(rs1.dirs) != len(rs2.dirs) || len(rs1.files) != len(rs2.files) || len(rs1.srcs) != len(rs2.srcs) { t.Error("Looks like not all go files, static files, and/or directories are copied.") } // Remove the directory we have created. os.RemoveAll(dst) }
// Context returns mappings between types that can be parsed using // strconv package and functions for that conversions. // All conversion functions meet the following criteria: // 1. They are exported. // 2. They expect 3 arguments: url.Values, string, ...int. // 3. They return 1 argument. // This is useful for code generation. func Context() FnMap { p, _ := path.ImportToAbsolute("github.com/colegion/goal/strconv") fs := FnMap{} pkg := reflect.ParseDir(p, false) for i := range pkg.Funcs { if !strconvFunc(pkg.Funcs[i]) { continue } fs[pkg.Funcs[i].Results[0].Type.String()] = pkg.Funcs[i] } return fs }
// processPackage gets an import path of a package, processes it, and // extracts controllers + actions. func (ps packages) processPackage(importPath string) { log.Trace.Printf(`Parsing "%s"...`, importPath) dir, err := path.ImportToAbsolute(importPath) log.AssertNil(err) p := reflect.ParseDir(dir, false) cs := ps.extractControllers(p) if len(cs.data) > 0 { ps[importPath] = controllers{ data: cs.data, init: ps.extractInitFunc(p), } } }
func createConfig(t *testing.T) []byte { p, _ := path.ImportToAbsolute("github.com/colegion/goal/tools/run") bs, err := ioutil.ReadFile( filepath.Join(p, "./testdata/configs/goal.src.yml"), ) if err != nil { t.Error(err) } err = ioutil.WriteFile( filepath.Join(p, "./testdata/configs/goal.yml"), bs, 0666, ) if err != nil { t.Error(err) } return bs }
// main is an entry point of the "run" subcommand (tool). func main(hs []tool.Handler, i int, args tool.Data) { // The first argument in the list is a path. // If it's missing use an empty string instead. p := args.GetDefault(0, "") // Determine import path and absolute path of the project to run. imp, err := path.CleanImport(p) log.AssertNil(err) dir, err := path.ImportToAbsolute(imp) log.AssertNil(err) // Prepare a path of configuration file. cf := filepath.Join(dir, ConfigFile) // Start a user tasks runner and instances controller. go instanceController() // Start a configuration file watcher. go configDaemon(imp, cf) // Show user friendly errors and terminate subprograms // in case of panics. defer func() { channel <- message{ action: "exit", } <-stopped log.Trace.Panicln("Application has been terminated.") }() // Execute all commands from the requested directory. curr, _ := os.Getwd() os.Chdir(dir) // pushd defer func() { // Going back to the initial directory. os.Chdir(curr) // popd }() // Load the configuration. reloadConfig() // Cleaning up after we are done. signal.Notify(notify, os.Interrupt, syscall.SIGTERM) <-notify }
// start is an entry point of the generate handlers command. func start() { // Clean the out directory. log.Trace.Printf(`Removing "%s" directory if already exists...`, *output) err := os.RemoveAll(*output) log.AssertNil(err) // Start processing of controllers. ps := packages{} absInput, err := path.ImportToAbsolute(*input) log.AssertNil(err) absImport, err := path.AbsoluteToImport(absInput) log.AssertNil(err) absOutput, err := path.ImportToAbsolute(*output) log.AssertNil(err) absImportOut, err := path.AbsoluteToImport(absOutput) log.AssertNil(err) log.Trace.Printf(`Processing "%s" package...`, absImport) ps.processPackage(absImport) // Start generation of handler packages. tpl, err := path.ImportToAbsolute("github.com/colegion/goal/tools/generate/handlers/handlers.go.template") log.AssertNil(err) t := generation.NewType("", tpl) t.Extension = ".go" // Save generated files as a .go source. // Iterate through all available packages and generate handlers for them. log.Trace.Printf(`Starting generation of "%s" package...`, *pkg) for imp := range ps { // Check whether current package is the main one // and should be stored at the root directory or it is a subpackage. // // I.e. if --input is "./controllers" and --output is "./assets/handlers", // we are saving processed "./controllers" package to "./assets/handlers" // and some "github.com/colegion/smth" to "./assets/handlers/github.com/colegion/smth". out := *output if imp != absImport { out = filepath.Join(out, imp) } t.CreateDir(out) // Iterate over all available controllers, generate handlers package on // every of them. n := 0 for name := range ps[imp].data { // Find parent controllers of this controller. cs := []parent{} for i, p := range ps[imp].data[name].Parents { // Make sure it is a controller rather than just some embedded struct. check := p.Import if check == "" { // Embedded parent is a local structure. check = absImport } if _, ok := ps[check]; !ok { // Such package is not in the list of scanned ones. continue } if _, ok := ps[check].data[p.Name]; !ok { // There is no such controller. continue } // It is a valid parent controller, add it to the list. cs = append(cs, parent{ ID: i, Import: p.Import, Name: p.Name, }) } // Initialize parameters and generate a package. t.Package = strings.ToLower(name) t.Context = map[string]interface{}{ "after": action.MethodAfter, "before": action.MethodBefore, "initially": method.InitiallyName, "finally": method.FinallyName, "controller": ps[imp].data[name], "controllers": ps[imp].data, "import": imp, "input": input, "name": name, "outputImport": absImportOut, "output": output, "package": pkg, "parents": cs, "initFunc": ps[imp].init, "num": n, "actionImport": action.InterfaceImport, "actionInterface": action.Interface, "strconv": action.StrconvContext, } t.Generate() n++ } } }