jet icon indicating copy to clipboard operation
jet copied to clipboard

Avoid deleting non-generated files when re-generating.

Open cgruber opened this issue 5 months ago • 3 comments

Is your feature request related to a problem? Please describe. Adding convenience methods to generated model types is possible, by locating a file alongside the generated file. This allows me to, for example, create

// foo_conversion.go beside /db/jet/my_db/public/model/foo.go
func (a *Foo) ToProto() *pb.FooMessage {
	return &pb.FooMessage{
		PropA:        a.PropA,
		EnumB:   a.EnumB.String(),
		SomeNumber: a.SomeNumber.BigInt().Uint64(),
		//...
	}
}

so in my code, when I'm transforming a database row (model type) into a proto for a gRPC response, I can just say myFoo.ToProto(). Dealing with model types becomes vastly cleaner in calling code, when I can add these methods to do conversions, or other operations on them.

However, when I regenerate the files, the generator just blows away the jet directory entirely, forcing me to ensure I don't have uncommitted/indexed changes in my files

Describe the solution you'd like I would like the generator, when re-generating, to be more careful about what it deletes - instead of a bulk deletion of the directory, scan the directory and delete empty directories and files which are marked as generated by jet.

cgruber avatar Jul 09 '25 21:07 cgruber

An alternative would be a list of paths or globs to ignore.

cgruber avatar Jul 09 '25 21:07 cgruber

Instead of os.RemoveAll(schemaPath) here we could do something along the lines of:

func deleteIf(rootPath string, shouldDeleteFn func(path string) (bool, error)) error {
	// Add appropriate error handling and maybe some logging
	walkErr := filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error {
		// do something with err

		// Skip symbolic links to avoid infinite loops or deleting outside the intended scope
		if d.Type()&fs.ModeSymlink != 0 {
			return nil
		}

		// Can lookup the breadcrumbs, 
		shouldDelete, err := shouldDeleteFn(path) 
		if err != nil {
			return nil // keep walking - probably log this.
		}
		if shouldDelete {
			err := os.Remove(path)
			if err != nil {
				log.Printf("Failed to delete %q: %v\n", path, err)
			}
		} else {
			log.Printf("Keeping file: %s \n", path)
		}
		return nil
	})
	return walkErr // Returns any non-nil error from WalkDir itself
}

And then implement the shouldDeleteFn appropriately.

cgruber avatar Jul 09 '25 21:07 cgruber

It is generally considered bad practice to keep code-generated files and regular code files in the same directory.

Cleaner patter to extend generated model functionalities is to wrap generated models.

For instance, you create a new package called domain, and import it instead of model package. The rest of code remains the same.

package domain

type Foo struct{
    model.Foo
}

func (a *Foo) ToProto() *pb.FooMessage {
	return &pb.FooMessage{
		PropA:        a.PropA,
		EnumB:   a.EnumB.String(),
		SomeNumber: a.SomeNumber.BigInt().Uint64(),
		//...
	}
}

From the jet perspective model.Foo and domain.Foo are the same type and can be used interchangeably.

go-jet avatar Jul 11 '25 11:07 go-jet