package shroom import ( "errors" "fmt" "github.com/bouncepaw/mycorrhiza/hyphae/backlinks" "regexp" "github.com/bouncepaw/mycorrhiza/history" "github.com/bouncepaw/mycorrhiza/hyphae" "github.com/bouncepaw/mycorrhiza/l18n" "github.com/bouncepaw/mycorrhiza/user" "github.com/bouncepaw/mycorrhiza/util" ) func canRenameThisToThat(oh hyphae.Hypher, nh hyphae.Hypher, u *user.User, lc *l18n.Localizer) (errtitle string, err error) { switch nh.(type) { case *hyphae.EmptyHypha: default: rejectRenameLog(oh, u, fmt.Sprintf("name ‘%s’ taken already", nh.CanonicalName())) return lc.Get("ui.rename_taken"), fmt.Errorf(lc.Get("ui.rename_taken_tip", &l18n.Replacements{"name": "%[1]s"}), nh.CanonicalName()) } if nh.CanonicalName() == "" { rejectRenameLog(oh, u, "no new name given") return lc.Get("ui.rename_noname"), errors.New(lc.Get("ui.rename_noname_tip")) } if !hyphae.IsValidName(nh.CanonicalName()) { rejectRenameLog(oh, u, fmt.Sprintf("new name ‘%s’ invalid", nh.CanonicalName())) return lc.Get("ui.rename_badname"), errors.New(lc.Get("ui.rename_badname_tip", &l18n.Replacements{"chars": "^?!:#@><*|\"\\'&%"})) } return "", nil } // RenameHypha renames hypha from old name `hyphaName` to `newName` and makes a history record about that. If `recursive` is `true`, its subhyphae will be renamed the same way. func RenameHypha(h hyphae.Hypher, newHypha hyphae.Hypher, recursive bool, u *user.User, lc *l18n.Localizer) (hop *history.Op, errtitle string) { newHypha.Lock() defer newHypha.Unlock() hop = history.Operation(history.TypeRenameHypha) if errtitle, err := CanRename(u, h, lc); errtitle != "" { hop.WithErrAbort(err) return hop, errtitle } if errtitle, err := canRenameThisToThat(h, newHypha, u, lc); errtitle != "" { hop.WithErrAbort(err) return hop, errtitle } var ( re = regexp.MustCompile(`(?i)` + h.CanonicalName()) replaceName = func(str string) string { return re.ReplaceAllString(util.CanonicalName(str), newHypha.CanonicalName()) } hyphaeToRename = findHyphaeToRename(h, recursive) renameMap, err = renamingPairs(hyphaeToRename, replaceName) renameMsg = "Rename ‘%s’ to ‘%s’" ) if err != nil { hop.Errs = append(hop.Errs, err) return hop, hop.FirstErrorText() } if recursive && len(hyphaeToRename) > 0 { renameMsg += " recursively" } hop.WithFilesRenamed(renameMap). WithMsg(fmt.Sprintf(renameMsg, h.CanonicalName(), newHypha.CanonicalName())). WithUser(u). Apply() if len(hop.Errs) == 0 { for _, h := range hyphaeToRename { h := h.(*hyphae.NonEmptyHypha) // ontology think oldName := h.CanonicalName() hyphae.RenameHyphaTo(h, replaceName(h.CanonicalName())) h.Lock() h.TextPath = replaceName(h.TextPath) h.SetBinaryPath(replaceName(h.BinaryPath())) h.Unlock() backlinks.UpdateBacklinksAfterRename(h, oldName) } } return hop, "" } func findHyphaeToRename(superhypha hyphae.Hypher, recursive bool) []hyphae.Hypher { hyphaList := []hyphae.Hypher{superhypha} if recursive { hyphaList = append(hyphaList, hyphae.Subhyphae(superhypha)...) } return hyphaList } func renamingPairs(hyphaeToRename []hyphae.Hypher, replaceName func(string) string) (map[string]string, error) { renameMap := make(map[string]string) newNames := make([]string, len(hyphaeToRename)) for _, h := range hyphaeToRename { h.Lock() newNames = append(newNames, replaceName(h.CanonicalName())) if h.HasTextPart() { renameMap[h.TextPartPath()] = replaceName(h.TextPartPath()) } switch h := h.(type) { case *hyphae.NonEmptyHypha: if h.Kind() == hyphae.HyphaMedia { // ontology think renameMap[h.BinaryPath()] = replaceName(h.BinaryPath()) } } h.Unlock() } if firstFailure, ok := hyphae.AreFreeNames(newNames...); !ok { return nil, errors.New("NonEmptyHypha " + firstFailure + " already exists") } return renameMap, nil }