Skip to content

Commit 9d5b2da

Browse files
committed
feat(internal/librarian/java): add README partials loader
1 parent 97ba296 commit 9d5b2da

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

internal/librarian/java/readme.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ import (
2626
"strings"
2727
"unicode"
2828
"unicode/utf8"
29+
30+
"github.com/googleapis/librarian/internal/yaml"
31+
)
32+
33+
const (
34+
readmePartialsFile = ".readme-partials.yaml"
2935
)
3036

3137
var (
@@ -51,6 +57,9 @@ var (
5157

5258
// errEmptyFile indicates an empty file path was provided.
5359
errEmptyFile = errors.New("file cannot be empty")
60+
61+
// errInvalidYAML indicates the YAML file syntax is invalid or cannot be unmarshaled.
62+
errInvalidYAML = errors.New("invalid yaml syntax")
5463
)
5564

5665
// codeSample represents a discovered Java code sample along with its derived title.
@@ -200,6 +209,35 @@ func parseRepoShortName(repo string) string {
200209
return repo
201210
}
202211

212+
// loadReadmePartials loads and camel-cases README partials from .readme-partials.yaml.
213+
func loadReadmePartials(dir string) (map[string]interface{}, error) {
214+
if dir == "" {
215+
return nil, errEmptyDir
216+
}
217+
partialsBytes, err := os.ReadFile(filepath.Join(dir, readmePartialsFile))
218+
if err != nil {
219+
if errors.Is(err, fs.ErrNotExist) {
220+
return nil, nil
221+
}
222+
return nil, fmt.Errorf("failed to read partials file: %w", err)
223+
}
224+
if len(partialsBytes) == 0 {
225+
return nil, nil
226+
}
227+
rawPartials, err := yaml.Unmarshal[map[string]interface{}](partialsBytes)
228+
if err != nil {
229+
return nil, fmt.Errorf("%w: failed to unmarshal partials: %w", errInvalidYAML, err)
230+
}
231+
if rawPartials == nil || len(*rawPartials) == 0 {
232+
return nil, nil
233+
}
234+
result := make(map[string]interface{}, len(*rawPartials))
235+
for k, v := range *rawPartials {
236+
result[toCamelCase(k)] = v
237+
}
238+
return result, nil
239+
}
240+
203241
// collectSnippetFiles recursively scans dir/samples for Java and XML files containing snippets.
204242
func collectSnippetFiles(dir string) ([]string, error) {
205243
samplesDir := filepath.Join(dir, "samples")

internal/librarian/java/readme_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,103 @@ func TestParseRepoShortName(t *testing.T) {
588588
}
589589
}
590590

591+
func TestLoadReadmePartials(t *testing.T) {
592+
for _, test := range []struct {
593+
name string
594+
setupFiles func(t *testing.T, dir string)
595+
want map[string]interface{}
596+
}{
597+
{
598+
name: "loads yaml partials with camel case conversion",
599+
setupFiles: func(t *testing.T, dir string) {
600+
path := filepath.Join(dir, readmePartialsFile)
601+
content := `about_text: "Custom about"`
602+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
603+
t.Fatal(err)
604+
}
605+
},
606+
want: map[string]interface{}{"AboutText": "Custom about"},
607+
},
608+
{
609+
name: "missing partials file returns nil",
610+
setupFiles: func(t *testing.T, dir string) {
611+
// No file written.
612+
},
613+
want: nil,
614+
},
615+
{
616+
name: "empty partials file returns nil",
617+
setupFiles: func(t *testing.T, dir string) {
618+
path := filepath.Join(dir, readmePartialsFile)
619+
if err := os.WriteFile(path, []byte(""), 0644); err != nil {
620+
t.Fatal(err)
621+
}
622+
},
623+
want: nil,
624+
},
625+
{
626+
name: "partials file with only comments returns nil",
627+
setupFiles: func(t *testing.T, dir string) {
628+
path := filepath.Join(dir, readmePartialsFile)
629+
if err := os.WriteFile(path, []byte("# only comments\n# no keys defined"), 0644); err != nil {
630+
t.Fatal(err)
631+
}
632+
},
633+
want: nil,
634+
},
635+
} {
636+
t.Run(test.name, func(t *testing.T) {
637+
dir := t.TempDir()
638+
test.setupFiles(t, dir)
639+
got, err := loadReadmePartials(dir)
640+
if err != nil {
641+
t.Fatal(err)
642+
}
643+
if diff := cmp.Diff(test.want, got); diff != "" {
644+
t.Errorf("mismatch (-want +got):\n%s", diff)
645+
}
646+
})
647+
}
648+
}
649+
650+
func TestLoadReadmePartials_Error(t *testing.T) {
651+
for _, test := range []struct {
652+
name string
653+
dir string
654+
setupFiles func(t *testing.T, dir string)
655+
wantErr error
656+
}{
657+
{
658+
name: "empty directory parameter returns error",
659+
dir: "",
660+
wantErr: errEmptyDir,
661+
},
662+
{
663+
name: "invalid yaml syntax",
664+
setupFiles: func(t *testing.T, dir string) {
665+
path := filepath.Join(dir, readmePartialsFile)
666+
content := `key: [unclosed list`
667+
if err := os.WriteFile(path, []byte(content), 0644); err != nil {
668+
t.Fatal(err)
669+
}
670+
},
671+
wantErr: errInvalidYAML,
672+
},
673+
} {
674+
t.Run(test.name, func(t *testing.T) {
675+
dir := test.dir
676+
if test.setupFiles != nil {
677+
dir = t.TempDir()
678+
test.setupFiles(t, dir)
679+
}
680+
_, err := loadReadmePartials(dir)
681+
if !errors.Is(err, test.wantErr) {
682+
t.Errorf("loadReadmePartials() error = %v, wantErr %v", err, test.wantErr)
683+
}
684+
})
685+
}
686+
}
687+
591688
func TestCollectSnippetFiles(t *testing.T) {
592689
for _, test := range []struct {
593690
name string

0 commit comments

Comments
 (0)