// Copyright Earl Warren // Copyright Loïc Dachary // SPDX-License-Identifier: MIT package cmd import ( "context" "fmt" "code.forgejo.org/f3/gof3/v3/logger" "code.forgejo.org/f3/gof3/v3/options" "code.forgejo.org/f3/gof3/v3/path" "code.forgejo.org/f3/gof3/v3/tree/generic" "code.forgejo.org/f3/gof3/v3/util" "github.com/urfave/cli/v3" ) var ( directionFrom = "from" flagFrom = "--" + directionFrom directionTo = "to" flagTo = "--" + directionTo ) func BuildForgePrefix(prefix, forge string) string { return prefix + "-" + forge } func FlagsToTree(ctx context.Context, c *cli.Command, direction string) generic.TreeInterface { forgeType := c.String(ForgeTypeOption(direction)) opts := options.GetFactory(forgeType)() opts.(options.LoggerInterface).SetLogger(logger.ContextGetLogger(ctx)) if o, ok := opts.(options.CLIInterface); ok { o.FromFlags(ctx, c, BuildForgePrefix(direction, forgeType)) } else { panic("not implemented") } return generic.GetFactory("f3")(ctx, opts) } func CreateCmdMirror() *cli.Command { flags := make([]cli.Flag, 0, 10) for _, direction := range []string{"from", "to"} { flags = append(flags, GetFlagsCommon(direction, "common")...) for name, factory := range options.GetFactories() { if opts, ok := factory().(options.CLIInterface); ok { flags = append(flags, opts.GetFlags(BuildForgePrefix(direction, name), name)...) } } } flags = func(flags []cli.Flag) []cli.Flag { dedup := make([]cli.Flag, 0, 10) names := make(map[string]any, 10) flagLoop: for _, flag := range flags { for _, name := range flag.Names() { _, found := names[name] if found { continue flagLoop } } dedup = append(dedup, flag) for _, name := range flag.Names() { names[name] = nil } } return dedup }(flags) return &cli.Command{ Name: "mirror", Usage: "Mirror", Description: "Mirror", Action: func(ctx context.Context, c *cli.Command) error { return util.PanicToError(func() { runMirror(ctx, c) }) }, Flags: flags, } } func runMirror(ctx context.Context, c *cli.Command) { from := FlagsToTree(ctx, c, directionFrom) to := FlagsToTree(ctx, c, directionTo) fromPathString := c.String(BuildForgePrefix(directionFrom, "path")) fromPath := generic.NewPathFromString(fromPathString) toPathString := c.String(BuildForgePrefix(directionTo, "path")) toPath := generic.NewPathFromString(toPathString) log := from.GetLogger() fromURL := "(unset)" if url, ok := from.GetOptions().(options.URLInterface); ok { fromURL = url.GetURL() } toURL := "(unset)" if url, ok := to.GetOptions().(options.URLInterface); ok { toURL = url.GetURL() } log.Info("mirror %s (%s at %s) to %s (%s at %s)", fromPath, c.String(ForgeTypeOption(directionFrom)), fromURL, toPath, c.String(ForgeTypeOption(directionTo)), toURL, ) log.Debug("read %s from %T", fromPath, from) var fromNode generic.NodeInterface fromNode = generic.NilNode walkAndGet := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) { node.WalkAndGet(ctx, parent, generic.NewWalkOptions(nil)) fromNode = node } from.ApplyAndGet(ctx, fromPath, generic.NewApplyOptions(walkAndGet)) if fromNode == generic.NilNode { panic(fmt.Errorf("from %s not found", fromPath)) } from.Debug("copy %s from %T to %T", fromPath, from, to) if toPathString == "" { generic.TreeMirror(ctx, from, to, fromPath, generic.NewMirrorOptions()) } else { toNode := to.FindAndGet(ctx, toPath) if toNode == generic.NilNode { panic(fmt.Errorf("to %s not found", toPath)) } generic.NodeMirror(ctx, fromNode, toNode, generic.NewMirrorOptions()) } }