Files
convy/internal/cli/list.go
T
av 23d88c4048 проектные команды и ссылки на источник
- заведён internal/source: уровни ссылаются друг на друга путём на диске
  или git-репозиторием, ревизия закрепляется хвостом #ref; клон делается
  заново и удаляется, кэша нет
- добавлены init, add, pull, list, check в проекте — манифест
  .conventions.toml, сборка копий по разу на компонент, маркер локальной
  части, READING.md рядом
- проверки формы развязаны с набором: принимают lang.Vocabulary, а язык
  копии узнаётся по строке о версии — манифеста рядом с ней нет
2026-07-27 20:42:18 +03:00

109 lines
3.1 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
}
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, " ") + ")"
}