манифесты стали данными, заведён convy sync

- убраны комментарии из suite.toml и .conventions.toml: файл, который
  машина переписывает, комментарий через круг не проносит; объяснения
  ушли в README рядом, который suite init теперь заводит
- удалена текстовая правка манифеста целиком — 520 строк ручного
  лексера TOML вместе со всем классом ошибок порчи данных
- запись идёт из структур энкодером; ключ, которого инструмент не
  знает, запись останавливает, а не теряется молча
- convy sync сверяет манифест и подводит под него раскладку файлов:
  чего не хватает — собирает, что осиротело — удаляет, копию с
  локальной частью не трогает никогда
This commit is contained in:
av
2026-07-28 09:45:10 +03:00
parent b6b0976c19
commit 92bd1f463d
18 changed files with 851 additions and 1055 deletions
+24 -2
View File
@@ -26,8 +26,8 @@ const ProjectName = ".conventions.toml"
// written in one of them, which is what a component exists to separate.
type Component struct {
Dir string `toml:"dir"`
Lang []string `toml:"lang"`
Stack []string `toml:"stack"`
Lang []string `toml:"lang,omitempty"`
Stack []string `toml:"stack,omitempty"`
Topics []string `toml:"topics"`
}
@@ -71,6 +71,28 @@ func LoadProject(root string) (*Project, error) {
return &p, nil
}
// Save writes the project manifest back to where it was read from.
func (p *Project) Save() error {
return save(p.Path, p, p.Undecoded)
}
// Subscribe adds a topic to a component, keeping the list sorted so that the
// file does not churn on the order things were added in.
func (p *Project) Subscribe(name, topic string) {
c := p.Components[name]
c.Topics = append(c.Topics, topic)
sort.Strings(c.Topics)
p.Components[name] = c
}
// Unsubscribe drops a topic from a component. Nothing is kept behind: a
// subscription is a choice of the project, not a name anyone else may reuse.
func (p *Project) Unsubscribe(name, topic string) {
c := p.Components[name]
c.Topics = slices.DeleteFunc(c.Topics, func(t string) bool { return t == topic })
p.Components[name] = c
}
// FindProject walks up from start looking for a project manifest, so that a
// command works from any subdirectory of a repository.
func FindProject(start string) (string, error) {