livebud / bud

The Full-Stack Web Framework for Go
MIT License
5.58k stars 179 forks source link

Trouble with nested routes #135

Closed donovanrost closed 2 years ago

donovanrost commented 2 years ago

I ran:

bud new controller associations index show
bud new controller academies index show
bud new controller associations/academies index show

This laid out out the appropriate controllers in the /controller directory. However, I'm getting an error

bud/.app/controller/controller.go:9:2: imported and not used: "bud-test/controller/associations/academies" as academies1

looking at bud/.app/controller/controller.go, we can see that bud-test/controller/associations/academies is imported at as academies1 but that import isn't used. I can't delete it because the framework automatically generates it again.

Is there a correct way to create nested controllers like this?

package controller

import (
    view "bud-test/bud/.app/view"
    controller "bud-test/controller"
    academies "bud-test/controller/academies"
    members "bud-test/controller/academies/members"
    associations "bud-test/controller/associations"
    academies1 "bud-test/controller/associations/academies"
    users "bud-test/controller/users"
    request "github.com/livebud/bud/runtime/controller/request"
    response "github.com/livebud/bud/runtime/controller/response"
    http "net/http"
)

// Controller struct
type Controller struct {
    Index        *IndexAction
    Show         *ShowAction
    Academies    *AcademiesController
    Associations *AssociationsController
    Users        *UsersController
}

// IndexAction struct
type IndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *IndexAction) Key() string {
    return "/index"
}

// Path is the default RESTful path to this action
func (i *IndexAction) Path() string {
    return "/"
}

// Method is the default RESTful method of this action
func (i *IndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *IndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *IndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    controllerController := loadController()
    fn := controllerController.Index
    // Call the controller
    stories, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/", map[string]interface{}{"stories": stories}),
        JSON: response.JSON(stories),
    }
}

// ShowAction struct
type ShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *ShowAction) Key() string {
    return "/show"
}

// Path is the default RESTful path to this action
func (s *ShowAction) Path() string {
    return "/:id"
}

// Method is the default RESTful method of this action
func (s *ShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *ShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *ShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    controllerController := loadController()
    fn := controllerController.Show
    // Call the controller
    story, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/:id", map[string]interface{}{"story": story}),
        JSON: response.JSON(story),
    }
}

// Controller struct
type AcademiesController struct {
    Index   *AcademiesIndexAction
    Show    *AcademiesShowAction
    Members *AcademiesMembersController
}

// AcademiesIndexAction struct
type AcademiesIndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *AcademiesIndexAction) Key() string {
    return "/academies/index"
}

// Path is the default RESTful path to this action
func (i *AcademiesIndexAction) Path() string {
    return "/academies"
}

// Method is the default RESTful method of this action
func (i *AcademiesIndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *AcademiesIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *AcademiesIndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    academiesController := loadAcademiesController()
    fn := academiesController.Index
    // Call the controller
    academies, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/academies", map[string]interface{}{"academies": academies}),
        JSON: response.JSON(academies),
    }
}

// AcademiesShowAction struct
type AcademiesShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *AcademiesShowAction) Key() string {
    return "/academies/show"
}

// Path is the default RESTful path to this action
func (s *AcademiesShowAction) Path() string {
    return "/academies/:id"
}

// Method is the default RESTful method of this action
func (s *AcademiesShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *AcademiesShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *AcademiesShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    academiesController := loadAcademiesController()
    fn := academiesController.Show
    // Call the controller
    academy, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/academies/:id", map[string]interface{}{"academy": academy}),
        JSON: response.JSON(academy),
    }
}

// Controller struct
type AcademiesMembersController struct {
    Index *AcademiesMembersIndexAction
    Show  *AcademiesMembersShowAction
}

// AcademiesMembersIndexAction struct
type AcademiesMembersIndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *AcademiesMembersIndexAction) Key() string {
    return "/academies/members/index"
}

// Path is the default RESTful path to this action
func (i *AcademiesMembersIndexAction) Path() string {
    return "/academies/:academy_id/members"
}

// Method is the default RESTful method of this action
func (i *AcademiesMembersIndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *AcademiesMembersIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *AcademiesMembersIndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    membersController := loadAcademiesMembersController()
    fn := membersController.Index
    // Call the controller
    members, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/academies/:academy_id/members", map[string]interface{}{"members": members}),
        JSON: response.JSON(members),
    }
}

// AcademiesMembersShowAction struct
type AcademiesMembersShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *AcademiesMembersShowAction) Key() string {
    return "/academies/members/show"
}

// Path is the default RESTful path to this action
func (s *AcademiesMembersShowAction) Path() string {
    return "/academies/:academy_id/members/:id"
}

// Method is the default RESTful method of this action
func (s *AcademiesMembersShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *AcademiesMembersShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *AcademiesMembersShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    membersController := loadAcademiesMembersController()
    fn := membersController.Show
    // Call the controller
    member, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/academies/:academy_id/members/:id", map[string]interface{}{"member": member}),
        JSON: response.JSON(member),
    }
}

// Controller struct
type AssociationsController struct {
    Index     *AssociationsIndexAction
    Show      *AssociationsShowAction
    Academies *AssociationsAcademiesController
}

// AssociationsIndexAction struct
type AssociationsIndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *AssociationsIndexAction) Key() string {
    return "/associations/index"
}

// Path is the default RESTful path to this action
func (i *AssociationsIndexAction) Path() string {
    return "/associations"
}

// Method is the default RESTful method of this action
func (i *AssociationsIndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *AssociationsIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *AssociationsIndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    associationsController := loadAssociationsController()
    fn := associationsController.Index
    // Call the controller
    associations, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/associations", map[string]interface{}{"associations": associations}),
        JSON: response.JSON(associations),
    }
}

// AssociationsShowAction struct
type AssociationsShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *AssociationsShowAction) Key() string {
    return "/associations/show"
}

// Path is the default RESTful path to this action
func (s *AssociationsShowAction) Path() string {
    return "/associations/:id"
}

// Method is the default RESTful method of this action
func (s *AssociationsShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *AssociationsShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *AssociationsShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    associationsController := loadAssociationsController()
    fn := associationsController.Show
    // Call the controller
    association, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/associations/:id", map[string]interface{}{"association": association}),
        JSON: response.JSON(association),
    }
}

// Controller struct
type AssociationsAcademiesController struct {
    Index *AssociationsAcademiesIndexAction
    Show  *AssociationsAcademiesShowAction
}

// AssociationsAcademiesIndexAction struct
type AssociationsAcademiesIndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *AssociationsAcademiesIndexAction) Key() string {
    return "/associations/academies/index"
}

// Path is the default RESTful path to this action
func (i *AssociationsAcademiesIndexAction) Path() string {
    return "/associations/:association_id/academies"
}

// Method is the default RESTful method of this action
func (i *AssociationsAcademiesIndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *AssociationsAcademiesIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *AssociationsAcademiesIndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    academiesController := loadAssociationsAcademiesController()
    fn := academiesController.Index
    // Call the controller
    academies, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/associations/:association_id/academies", map[string]interface{}{"academies": academies}),
        JSON: response.JSON(academies),
    }
}

// AssociationsAcademiesShowAction struct
type AssociationsAcademiesShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *AssociationsAcademiesShowAction) Key() string {
    return "/associations/academies/show"
}

// Path is the default RESTful path to this action
func (s *AssociationsAcademiesShowAction) Path() string {
    return "/associations/:association_id/academies/:id"
}

// Method is the default RESTful method of this action
func (s *AssociationsAcademiesShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *AssociationsAcademiesShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *AssociationsAcademiesShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    academiesController := loadAssociationsAcademiesController()
    fn := academiesController.Show
    // Call the controller
    academy, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/associations/:association_id/academies/:id", map[string]interface{}{"academy": academy}),
        JSON: response.JSON(academy),
    }
}

// Controller struct
type UsersController struct {
    Index *UsersIndexAction
    Show  *UsersShowAction
}

// UsersIndexAction struct
type UsersIndexAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (i *UsersIndexAction) Key() string {
    return "/users/index"
}

// Path is the default RESTful path to this action
func (i *UsersIndexAction) Path() string {
    return "/users"
}

// Method is the default RESTful method of this action
func (i *UsersIndexAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (i *UsersIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    i.handler(r).ServeHTTP(w, r)
}

// Handler function
func (i *UsersIndexAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    usersController := loadUsersController()
    fn := usersController.Index
    // Call the controller
    users, err := fn(
        httpRequest.Context(),
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: i.View.Handler("/users", map[string]interface{}{"users": users}),
        JSON: response.JSON(users),
    }
}

// UsersShowAction struct
type UsersShowAction struct {
    View *view.Server
}

// Key is a unique identifier of this action
func (s *UsersShowAction) Key() string {
    return "/users/show"
}

// Path is the default RESTful path to this action
func (s *UsersShowAction) Path() string {
    return "/users/:id"
}

// Method is the default RESTful method of this action
func (s *UsersShowAction) Method() string {
    return "GET"
}

// ServeHTTP fn
func (s *UsersShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    s.handler(r).ServeHTTP(w, r)
}

// Handler function
func (s *UsersShowAction) handler(httpRequest *http.Request) http.Handler {
    // Define the input struct
    var in struct {
        ID int `json:"id,omitempty"`
    }
    // Unmarshal the request body
    if err := request.Unmarshal(httpRequest, &in); err != nil {
        return &response.Format{
            JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }
    usersController := loadUsersController()
    fn := usersController.Show
    // Call the controller
    user, err := fn(
        httpRequest.Context(),
        in.ID,
    )
    if err != nil {
        return &response.Format{
            JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}),
        }
    }

    // Respond
    return &response.Format{
        HTML: s.View.Handler("/users/:id", map[string]interface{}{"user": user}),
        JSON: response.JSON(user),
    }
}

func loadAcademiesController() *academies.Controller {
    academiesController := &academies.Controller{}
    return academiesController
}

func loadAcademiesMembersController() *members.Controller {
    membersController := &members.Controller{}
    return membersController
}

func loadAssociationsAcademiesController() *academies.Controller {
    academiesController := &academies.Controller{}
    return academiesController
}

func loadAssociationsController() *associations.Controller {
    associationsController := &associations.Controller{}
    return associationsController
}

func loadController() *controller.Controller {
    controllerController := &controller.Controller{}
    return controllerController
}

func loadUsersController() *users.Controller {
    usersController := &users.Controller{}
    return usersController
}
matthewmueller commented 2 years ago

Hey @donovanrost! I just tried running those commands and unfortunately I'm not able to reproduce the error you mentioned above.

Can you share what's in your controllers/ directory? Function bodies aren't necessary if its private code. I bet that will point to the root cause.

This is what I see in the generated code for bud/.app/controller/controller.go ``` package controller import ( http "net/http" request "github.com/livebud/bud/runtime/controller/request" response "github.com/livebud/bud/runtime/controller/response" view "github.com/livebud/issue-135/bud/.app/view" associations "github.com/livebud/issue-135/controller/associations" academies "github.com/livebud/issue-135/controller/associations/academies" ) // Controller struct type Controller struct { Associations *AssociationsController } // Controller struct type AssociationsController struct { Index *AssociationsIndexAction Show *AssociationsShowAction Academies *AssociationsAcademiesController } // AssociationsIndexAction struct type AssociationsIndexAction struct { View *view.Server } // Key is a unique identifier of this action func (i *AssociationsIndexAction) Key() string { return "/associations/index" } // Path is the default RESTful path to this action func (i *AssociationsIndexAction) Path() string { return "/associations" } // Method is the default RESTful method of this action func (i *AssociationsIndexAction) Method() string { return "GET" } // ServeHTTP fn func (i *AssociationsIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) { i.handler(r).ServeHTTP(w, r) } // Handler function func (i *AssociationsIndexAction) handler(httpRequest *http.Request) http.Handler { // Define the input struct var in struct { } // Unmarshal the request body if err := request.Unmarshal(httpRequest, &in); err != nil { return &response.Format{ JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } associationsController := loadAssociationsController() fn := associationsController.Index // Call the controller associations, err := fn( httpRequest.Context(), ) if err != nil { return &response.Format{ JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } // Respond return &response.Format{ HTML: i.View.Handler("/associations", map[string]interface{}{"associations": associations}), JSON: response.JSON(associations), } } // AssociationsShowAction struct type AssociationsShowAction struct { View *view.Server } // Key is a unique identifier of this action func (s *AssociationsShowAction) Key() string { return "/associations/show" } // Path is the default RESTful path to this action func (s *AssociationsShowAction) Path() string { return "/associations/:id" } // Method is the default RESTful method of this action func (s *AssociationsShowAction) Method() string { return "GET" } // ServeHTTP fn func (s *AssociationsShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.handler(r).ServeHTTP(w, r) } // Handler function func (s *AssociationsShowAction) handler(httpRequest *http.Request) http.Handler { // Define the input struct var in struct { ID int `json:"id,omitempty"` } // Unmarshal the request body if err := request.Unmarshal(httpRequest, &in); err != nil { return &response.Format{ JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } associationsController := loadAssociationsController() fn := associationsController.Show // Call the controller association, err := fn( httpRequest.Context(), in.ID, ) if err != nil { return &response.Format{ JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } // Respond return &response.Format{ HTML: s.View.Handler("/associations/:id", map[string]interface{}{"association": association}), JSON: response.JSON(association), } } // Controller struct type AssociationsAcademiesController struct { Index *AssociationsAcademiesIndexAction Show *AssociationsAcademiesShowAction } // AssociationsAcademiesIndexAction struct type AssociationsAcademiesIndexAction struct { View *view.Server } // Key is a unique identifier of this action func (i *AssociationsAcademiesIndexAction) Key() string { return "/associations/academies/index" } // Path is the default RESTful path to this action func (i *AssociationsAcademiesIndexAction) Path() string { return "/associations/:association_id/academies" } // Method is the default RESTful method of this action func (i *AssociationsAcademiesIndexAction) Method() string { return "GET" } // ServeHTTP fn func (i *AssociationsAcademiesIndexAction) ServeHTTP(w http.ResponseWriter, r *http.Request) { i.handler(r).ServeHTTP(w, r) } // Handler function func (i *AssociationsAcademiesIndexAction) handler(httpRequest *http.Request) http.Handler { // Define the input struct var in struct { } // Unmarshal the request body if err := request.Unmarshal(httpRequest, &in); err != nil { return &response.Format{ JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } academiesController := loadAssociationsAcademiesController() fn := academiesController.Index // Call the controller academies, err := fn( httpRequest.Context(), ) if err != nil { return &response.Format{ JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } // Respond return &response.Format{ HTML: i.View.Handler("/associations/:association_id/academies", map[string]interface{}{"academies": academies}), JSON: response.JSON(academies), } } // AssociationsAcademiesShowAction struct type AssociationsAcademiesShowAction struct { View *view.Server } // Key is a unique identifier of this action func (s *AssociationsAcademiesShowAction) Key() string { return "/associations/academies/show" } // Path is the default RESTful path to this action func (s *AssociationsAcademiesShowAction) Path() string { return "/associations/:association_id/academies/:id" } // Method is the default RESTful method of this action func (s *AssociationsAcademiesShowAction) Method() string { return "GET" } // ServeHTTP fn func (s *AssociationsAcademiesShowAction) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.handler(r).ServeHTTP(w, r) } // Handler function func (s *AssociationsAcademiesShowAction) handler(httpRequest *http.Request) http.Handler { // Define the input struct var in struct { ID int `json:"id,omitempty"` } // Unmarshal the request body if err := request.Unmarshal(httpRequest, &in); err != nil { return &response.Format{ JSON: response.Status(400).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } academiesController := loadAssociationsAcademiesController() fn := academiesController.Show // Call the controller academy, err := fn( httpRequest.Context(), in.ID, ) if err != nil { return &response.Format{ JSON: response.Status(500).Set("Content-Type", "application/json").JSON(map[string]string{"error": err.Error()}), } } // Respond return &response.Format{ HTML: s.View.Handler("/associations/:association_id/academies/:id", map[string]interface{}{"academy": academy}), JSON: response.JSON(academy), } } func loadAssociationsAcademiesController() *academies.Controller { academiesController := &academies.Controller{} return academiesController } func loadAssociationsController() *associations.Controller { associationsController := &associations.Controller{} return associationsController } ```
donovanrost commented 2 years ago

@matthewmueller I just created a new project and ran those generators to reproduce.

Under controllers/ I have:

I'm not sure what you mean by:

Function bodies aren't necessary if its private code. I bet that will point to the root cause.

matthewmueller commented 2 years ago

Hmm, okay. Can you try again and make sure you're on the latest?

$ bud version
     bud: 0.1.7
  svelte: 3.47.0
   react: 18.0.0

I also just ran those 3 commands:

bud new controller associations index show
bud new controller academies index show
bud new controller associations/academies index show

But it was working for me when I ran bud run. If you're able to reproduce, please post exact steps and I'll have another look!

donovanrost commented 2 years ago

@matthewmueller I emailed you a video of me reproducing the issue. I hope that's ok

012e commented 2 years ago

I got the same issue on linux

# 1/bud/.app/controller
bud/.app/controller/controller.go:7:2: imported and not used: "1/controller/associations/academies" as academies1
| exit status 2
matthewmueller commented 2 years ago

I'm able to reproduce this now! Definitely a bug.

The problem is that the following should refer to academies1, not academies.

func loadAssociationsAcademiesController() *academies.Controller {
    academiesController := &academies.Controller{}
    return academiesController
}

This happens because these generated functions are unaware of previous functions. The previous function is already using the acadamies package, so this one needs to use the academies1 package.

I think the solution is to allow the dependency injection provider to change import names after wiring but before generating code. This would allow the generator to take into account existing imports.

Maybe something like this:

    // Add generated imports
    for i, imp := range provider.Imports {
        provider.Imports[i].Name = l.imports.Add(imp.Path)
    }

In https://github.com/livebud/bud/blob/683a5528065f11a239d52a4babcb151f6c96ceb6/runtime/generator/controller/loader.go#L494-L496

Unfortunately, this doesn't work yet. package/di would need to be adjusted.

In the meantime, avoiding duplicate package names in the controller/ directory is the workaround.