Files
av b6b0976c19 исправлены находки ревью проектной стороны
- манифест читается так, как записан: решётка внутри строки не открывает
  комментарий, скобка внутри комментария не закрывает массив, имя внутри
  комментария не становится подпиской; новый ключ встаёт после массива,
  а не внутрь него
- всё записываемое проходит через manifest.Quote — обратный слэш в пути
  делал файл, который инструмент сам не читает
- маркер локальной части переехал в doc и пропускает огороженные блоки:
  процитированный в примере маркер больше не считается границей, а копия
  без маркера не перезаписывается молча
- лишний позиционный аргумент отсекается: flag прекращал разбор и прятал
  флаги после себя, из-за чего pull, list и check игнорировали --for
- заведены тесты проверок копий, включая молчание на исправной копии
2026-07-27 21:16:29 +03:00

112 lines
3.2 KiB
Go

package cli
import (
"flag"
"fmt"
"path/filepath"
"strings"
"git.vakhrushev.me/av/convy/internal/manifest"
"git.vakhrushev.me/av/convy/internal/project"
)
// convy list answers two questions in one view: what this repository takes and
// what the suite has that it does not. The second half is the reason the
// command reaches the suite at all — a listing of the manifest alone is the
// manifest, and reading it needs no tool.
func runList(env Env, args []string) ExitCode {
fs := flag.NewFlagSet("convy list", flag.ContinueOnError)
fs.SetOutput(env.Err)
root := fs.String("root", "", "root of the project; it is looked up upwards by default")
forComponent := fs.String("for", "", "component to show; every one of them by default")
if err := fs.Parse(args); err != nil {
return Usage
}
if code := noStrayArgs(env, "convy list", fs.Args()); code != OK {
return code
}
o, code := openProject(env, *root)
if code != OK {
return code
}
defer o.Close()
names, code := components(env, o.Manifest, *forComponent)
if code != OK {
return code
}
fmt.Fprintf(env.Out, "source %s\n", o.Manifest.Source)
fmt.Fprintf(env.Out, "%s, language version %d (%s)\n",
plural(len(o.Suite.Manifest.LiveTopics()), "topic"),
o.Suite.Manifest.Language.Version, o.Suite.Manifest.Language.Lang)
for _, name := range names {
c := o.Manifest.Components[name]
fmt.Fprintf(env.Out, "\n%s — %s%s\n", name, c.Dir, axisOf(c))
listComponent(env, o, c)
}
return OK
}
func listComponent(env Env, o *opened, c manifest.Component) {
if len(c.Topics) == 0 {
fmt.Fprintln(env.Out, " subscribed to nothing yet")
}
for _, topic := range c.Topics {
if !o.Suite.Manifest.TopicLive(topic) {
fmt.Fprintf(env.Out, " %-20s the suite declares no such topic any more\n", topic)
continue
}
taken, _ := o.Suite.Assemble(topic, project.Axis(c))
file := filepath.ToSlash(filepath.Join(c.Dir, topic+".md"))
mark := " "
if !exists(filepath.Join(o.Root, filepath.FromSlash(file))) {
mark = "!"
}
fmt.Fprintf(env.Out, "%s %-20s %-16s %s\n", mark, topic, plural(len(taken), "layer"),
o.Suite.Manifest.Topics.Live[topic])
if mark == "!" {
fmt.Fprintf(env.Out, " %-20s subscribed, and no file: convy pull assembles it\n", "")
}
}
var free []string
for _, topic := range o.Suite.Manifest.LiveTopics() {
if !c.Subscribed(topic) {
free = append(free, topic)
}
}
if len(free) == 0 {
return
}
fmt.Fprintln(env.Out, "\n not taken:")
for _, topic := range free {
taken, _ := o.Suite.Assemble(topic, project.Axis(c))
if len(taken) == 0 {
fmt.Fprintf(env.Out, "· %-20s %-16s %s\n", topic, "no layer fits",
o.Suite.Manifest.Topics.Live[topic])
continue
}
fmt.Fprintf(env.Out, "· %-20s %-16s %s\n", topic, plural(len(taken), "layer"),
o.Suite.Manifest.Topics.Live[topic])
}
}
// axisOf describes the axis of a component the way the suite writes it.
func axisOf(c manifest.Component) string {
var parts []string
if len(c.Lang) > 0 {
parts = append(parts, "lang="+strings.Join(c.Lang, ","))
}
if len(c.Stack) > 0 {
parts = append(parts, "stack="+strings.Join(c.Stack, ","))
}
if len(parts) == 0 {
return ""
}
return " (" + strings.Join(parts, " ") + ")"
}