Copy a garden (deep duplicate) from the gardens list (#46)
Build image / build-and-push (push) Successful in 16s
Build image / build-and-push (push) Successful in 16s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
This commit was merged in pull request #46.
This commit is contained in:
@@ -156,6 +156,48 @@ func (s *Service) UpdateGarden(ctx context.Context, actorID, gardenID int64, in
|
||||
return updated, err
|
||||
}
|
||||
|
||||
// CopyGarden duplicates a garden into a new one owned by the actor, carrying
|
||||
// over its dimensions, grid settings, objects and currently-planted plops. A
|
||||
// blank name defaults to "<source> (copy)".
|
||||
//
|
||||
// Owner-only, deliberately: a copy's plops keep pointing at the SOURCE's
|
||||
// plant_ids, and a custom plant is owned by one user. Letting a viewer copy
|
||||
// someone else's garden would hand them a garden referencing plants that aren't
|
||||
// in their catalog — and would block the original owner from ever deleting those
|
||||
// plants (plantings.plant_id is ON DELETE RESTRICT). Copying a shared garden
|
||||
// needs a plant-cloning policy first; until then the owner's own copy is the
|
||||
// case that's unambiguously correct.
|
||||
func (s *Service) CopyGarden(ctx context.Context, actorID, gardenID int64, name string) (*domain.Garden, error) {
|
||||
src, err := s.requireGardenRole(ctx, actorID, gardenID, roleOwner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
name = copyName(src.Name)
|
||||
}
|
||||
if len(name) > maxGardenNameLen {
|
||||
return nil, domain.ErrInvalidInput
|
||||
}
|
||||
created, err := s.store.CopyGarden(ctx, gardenID, actorID, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
created.MyRole = roleOwner.String() // the copier owns the copy
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// copyName derives the default name for a copy. A name already at the length cap
|
||||
// is trimmed to make room for the suffix, on a rune boundary so the result stays
|
||||
// valid UTF-8.
|
||||
func copyName(src string) string {
|
||||
const suffix = " (copy)"
|
||||
if room := maxGardenNameLen - len(suffix); len(src) > room {
|
||||
src = strings.TrimSpace(strings.ToValidUTF8(src[:room], ""))
|
||||
}
|
||||
return src + suffix
|
||||
}
|
||||
|
||||
// DeleteGarden removes a garden; only the owner may.
|
||||
func (s *Service) DeleteGarden(ctx context.Context, actorID, gardenID int64) error {
|
||||
if _, err := s.requireGardenRole(ctx, actorID, gardenID, roleOwner); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user