-
Notifications
You must be signed in to change notification settings - Fork 196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Config Packages] Enable optional map spec #1351
Conversation
Current dependencies on/for this PR:
This comment was auto-generated by Graphite. |
93e1315
to
22fa857
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really nice implementation! great encapsulation, super easy to read.
internal/devconfig/packages.go
Outdated
} | ||
|
||
// If we have a regular package, we want to marshal the entire struct: | ||
type Alias Package // Use an alias-type to avoid infinite recursion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is cool! I've gotten into infinite recursion issue when implementing custom marshaling and I don't remember the solution being this clean.
nit, make lower case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, and lower cased
} | ||
|
||
// parseVersionedName parses the name and version from package@version representation | ||
func parseVersionedName(versionedName string) (name, version string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see searcher.ParseVersionedPackage
this does a little more, but hopefully you can use it to prevent some code duplication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohh good call. Yes, I can use it. There are some subtle differences so I still need this function here, but can call into searcher.ParseVersionedPackage
in the implementation.
I've augmented the searcher.ParseVersionedPackage
to handle a couple cases I do here. And added test-cases.
internal/devconfig/packages.go
Outdated
type Alias Package // Use an alias-type to avoid infinite recursion | ||
alias := &Alias{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit lower case, maybe
type packageAlias Package // Use an alias-type to avoid infinite recursion
alias := &packageAlias{}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lowercased
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tests! I also like the idea of aliasing the type for (un)marshalling.
The map order mattering is unfortunate. Maybe we can eventually stop relying on that.
internal/devconfig/packages.go
Outdated
// Remove removes a package from the list of packages | ||
func (pkgs *Packages) Remove(versionedName string) error { | ||
name, version := parseVersionedName(versionedName) | ||
for idx, pkg := range pkgs.Collection { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
slices.DeleteFunc
might be a good fit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice! done.
orderedmap "github.com/wk8/go-ordered-map/v2" | ||
) | ||
|
||
type jsonKind int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could reuse json.Delim
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving in place for now, since it feels cleaner.
|
||
func (pkgs *Packages) UnmarshalJSON(data []byte) error { | ||
|
||
// First, attempt to unmarshal as a list of strings (legacy format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also check the first byte for '[' or '{'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving in place, since its cleaner
@@ -38,13 +38,21 @@ require ( | |||
github.com/spf13/cobra v1.6.1 | |||
github.com/spf13/pflag v1.0.5 | |||
github.com/stretchr/testify v1.8.2 | |||
github.com/wk8/go-ordered-map/v2 v2.1.8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there two orderedmap dependencies?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I added the iancoleman
one first, and then switched. This wk8
one is more modern and has a nicer API.
I'll remove the iancoleman
one.
// We use orderedmap to preserve the order of the packages. While the JSON | ||
// specification specifies that maps are unordered, we do rely on the order | ||
// for certain functionality. | ||
orderedMap := orderedmap.New[string, Package]() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also use a json.Decoder
to preserve the order and assign the key to the name field as you go.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the interests of time, I'm going to keep orderedmap for now. It seems to work well, and has a clean api.
The other library I used (see here) used json.Decoder in its implementation and its fairly non-trivial.
internal/devconfig/packages.go
Outdated
|
||
func (pkgs *Packages) MarshalJSON() ([]byte, error) { | ||
if pkgs.jsonKind == jsonList { | ||
packagesList := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
packagesList := []string{} | |
packagesList := make([]string, 0, len(pkgs.Collection)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
internal/devconfig/packages.go
Outdated
return errors.WithStack(err) | ||
} | ||
|
||
// more robust way to copy all fields from alias? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try *p = Package(*alias)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh thanks!!! Didn't know there was a built-in struct-copy function
… devconfig.parseVersionedName
6e6e9bd
to
c806c1f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the comments! responses inline, and updates made.
@@ -38,13 +38,21 @@ require ( | |||
github.com/spf13/cobra v1.6.1 | |||
github.com/spf13/pflag v1.0.5 | |||
github.com/stretchr/testify v1.8.2 | |||
github.com/wk8/go-ordered-map/v2 v2.1.8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I added the iancoleman
one first, and then switched. This wk8
one is more modern and has a nicer API.
I'll remove the iancoleman
one.
internal/devconfig/packages.go
Outdated
// Remove removes a package from the list of packages | ||
func (pkgs *Packages) Remove(versionedName string) error { | ||
name, version := parseVersionedName(versionedName) | ||
for idx, pkg := range pkgs.Collection { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice! done.
internal/devconfig/packages.go
Outdated
|
||
func (pkgs *Packages) MarshalJSON() ([]byte, error) { | ||
if pkgs.jsonKind == jsonList { | ||
packagesList := []string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// We use orderedmap to preserve the order of the packages. While the JSON | ||
// specification specifies that maps are unordered, we do rely on the order | ||
// for certain functionality. | ||
orderedMap := orderedmap.New[string, Package]() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the interests of time, I'm going to keep orderedmap for now. It seems to work well, and has a clean api.
The other library I used (see here) used json.Decoder in its implementation and its fairly non-trivial.
internal/devconfig/packages.go
Outdated
type Alias Package // Use an alias-type to avoid infinite recursion | ||
alias := &Alias{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lowercased
internal/devconfig/packages.go
Outdated
} | ||
|
||
// If we have a regular package, we want to marshal the entire struct: | ||
type Alias Package // Use an alias-type to avoid infinite recursion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, and lower cased
} | ||
|
||
// parseVersionedName parses the name and version from package@version representation | ||
func parseVersionedName(versionedName string) (name, version string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohh good call. Yes, I can use it. There are some subtle differences so I still need this function here, but can call into searcher.ParseVersionedPackage
in the implementation.
I've augmented the searcher.ParseVersionedPackage
to handle a couple cases I do here. And added test-cases.
orderedmap "github.com/wk8/go-ordered-map/v2" | ||
) | ||
|
||
type jsonKind int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving in place for now, since it feels cleaner.
|
||
func (pkgs *Packages) UnmarshalJSON(data []byte) error { | ||
|
||
// First, attempt to unmarshal as a list of strings (legacy format) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leaving in place, since its cleaner
Summary
Motivation
User feedback is leading us to add more parameters besides version to the packages config.
For instance, some packages may only need to be added on certain platforms.
Previously, packages were specified as
[]string
. For example:In the new format, they can also be specified as:
NOTE:
python
is an inline-version, whilecowsay
has a struct value which canaccomodate other fields like
platforms
.Approach
For now, we enable both packages as List and Map to work. The default continues to be List.
We do this by adding a
Packages
struct andPackage
struct with custom JSON marshalling and unmarshalling.Future PRs
Packages.platforms
and/orPackages.excludedPlatforms
fields. Use them when installing to nix profile, and generating the flake.nix.devbox add
anddevbox rm
flags for platform-specific packages.How was it tested?
Added
TestJsonifyConfigPackages
and other unit-teststestscripts pass
devbox init
produces the same output as before.