Skip to content

Commit f179129

Browse files
authored
Add command to create a .md file including the entire contents (#156)
1 parent 32ff2cd commit f179129

File tree

19 files changed

+234
-27
lines changed

19 files changed

+234
-27
lines changed

.github/cmd/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package main
22

3-
import "github.com/spring1843/go-dsa/.github/cmd/cmd"
3+
import "github.com/spring1843/go-dsa/.github/cmd/pkg/cmd"
44

55
func main() {
66
cmd.Execute()

.github/cmd/cmd/count_problems.go renamed to .github/cmd/pkg/cmd/count_problems.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"path/filepath"
88

9+
"github.com/spring1843/go-dsa/.github/cmd/pkg/problems"
10+
911
"github.com/spf13/cobra"
1012
)
1113

@@ -20,7 +22,7 @@ var countRehearsalsCommand = &cobra.Command{
2022
}
2123

2224
count := 0
23-
for _, section := range sections {
25+
for _, section := range problems.OrderedSections {
2426
matches, err := filepath.Glob(filepath.Join(dir, section, "*_test.go"))
2527
if err != nil {
2628
log.Fatalf("Error while globbing: %s", err)

.github/cmd/cmd/export_md.go renamed to .github/cmd/pkg/cmd/export_md.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"os"
77
"path/filepath"
88

9+
"github.com/spring1843/go-dsa/.github/cmd/pkg/problems"
10+
911
"github.com/spf13/cobra"
1012
)
1113

@@ -19,21 +21,26 @@ var exportMDCommand = &cobra.Command{
1921
log.Fatalf("Error finding current directory: %s", err)
2022
}
2123

22-
allFiles := []string{
24+
nonSectionMDFiles := []string{
2325
"README.md",
2426
"preface.md",
2527
"complexity.md",
2628
}
27-
for _, section := range sections {
28-
allFiles = append(allFiles, filepath.Join(section, "README.md"))
29-
}
3029

31-
for _, file := range allFiles {
30+
for _, file := range nonSectionMDFiles {
3231
content, err := os.ReadFile(filepath.Join(dir, file))
3332
if err != nil {
3433
log.Fatalf("Error reading file: %s", err)
3534
}
3635
fmt.Println(string(content))
3736
}
37+
38+
for _, section := range problems.OrderedSections {
39+
parsedSection, err := problems.ParseSection(dir, section)
40+
if err != nil {
41+
log.Fatalf("Error parsing section: %s", err)
42+
}
43+
fmt.Println(parsedSection)
44+
}
3845
},
3946
}

.github/cmd/cmd/random_challenge.go renamed to .github/cmd/pkg/cmd/random_challenge.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"path/filepath"
88

99
"github.com/spf13/cobra"
10+
"github.com/spring1843/go-dsa/.github/cmd/pkg/problems"
1011
)
1112

1213
const (
@@ -39,7 +40,7 @@ var randomChallengeCommand = &cobra.Command{
3940
}
4041

4142
allChallenges := []string{}
42-
for _, section := range sections {
43+
for _, section := range problems.OrderedSections {
4344
matches, err := filepath.Glob(filepath.Join(dir, section, "*_test.go"))
4445
if err != nil {
4546
log.Fatalf("Error while globbing: %s", err)
File renamed without changes.

.github/cmd/pkg/problems/parse.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package problems
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"log"
7+
"os"
8+
"path/filepath"
9+
"regexp"
10+
)
11+
12+
func ParseSection(dir, section string) (string, error) {
13+
content, err := os.ReadFile(filepath.Join(dir, section, "README.md"))
14+
if err != nil {
15+
return "", fmt.Errorf("error reading file: %w", err)
16+
}
17+
18+
preparedContent, err := replaceRehearsal(string(content), dir, section)
19+
if err != nil {
20+
return "", fmt.Errorf("error replacing the rehearsal section in file: %w", err)
21+
}
22+
23+
return preparedContent, nil
24+
}
25+
26+
func replaceRehearsal(input, dir, section string) (string, error) {
27+
rehearsalRegex := regexp.MustCompile(`(?s)(## Rehearsal\n.*?)(\n##|\z)`)
28+
29+
match := rehearsalRegex.FindStringSubmatch(input)
30+
if match == nil {
31+
return "", errors.New("no rehearsal section found")
32+
}
33+
34+
rehearsalContent := match[1]
35+
newRehearsals, err := newRehearsalEntry(rehearsalContent)
36+
if err != nil {
37+
return "", fmt.Errorf("error parsing rehearsal entry: %w", err)
38+
}
39+
if len(newRehearsals) == 0 {
40+
log.Printf("no rehearsals found %s, %s", section, rehearsalContent)
41+
}
42+
replacement := fmt.Sprintf("## Rehearsal\n%s", stringRehearsalEntries(dir, section, newRehearsals))
43+
return rehearsalRegex.ReplaceAllString(input, replacement), nil
44+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package problems
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"regexp"
8+
"strings"
9+
)
10+
11+
type rehearsalEntry struct {
12+
Name string
13+
TestFileName string
14+
SolutionFileName string
15+
}
16+
17+
func (r *rehearsalEntry) String() string {
18+
return fmt.Sprintf("### %s\n", r.Name)
19+
}
20+
21+
/*
22+
newRehearsalEntry parses the rehearsal section of the README.md file that looks like:
23+
24+
## Rehearsal
25+
26+
* [Some Name 1](./problem_test1.go), [Solution](./solution1.go)
27+
* [Some Name 2](./problem_test2.go), [Solution](./solution2.go).
28+
*/
29+
func newRehearsalEntry(input string) ([]rehearsalEntry, error) {
30+
lines := strings.Split(input, "\n")
31+
entries := []rehearsalEntry{}
32+
re := regexp.MustCompile(`\* \[([^\]]+)\]\(\.\/([^\)]+)\), \[Solution\]\(\.\/([^\)]+)\)`)
33+
34+
for _, line := range lines {
35+
line = strings.TrimSpace(line)
36+
if line == "" || strings.HasPrefix(line, "##") {
37+
continue // Skip empty lines and heading lines
38+
}
39+
matches := re.FindStringSubmatch(line)
40+
if len(matches) != 4 {
41+
return nil, fmt.Errorf("invalid line format: %s, %d", line, len(matches))
42+
}
43+
44+
entry := rehearsalEntry{
45+
Name: matches[1],
46+
TestFileName: matches[2],
47+
SolutionFileName: matches[3],
48+
}
49+
entries = append(entries, entry)
50+
}
51+
52+
return entries, nil
53+
}
54+
55+
func stringRehearsalEntries(dir, section string, entries []rehearsalEntry) string {
56+
output := ""
57+
for _, entry := range entries {
58+
output += fmt.Sprintf("\n### %s\n", entry.Name)
59+
60+
testFileContent, err := os.ReadFile(filepath.Join(dir, section, entry.TestFileName))
61+
if err != nil {
62+
output += fmt.Sprintf("Error reading test file: %s\n", err)
63+
continue
64+
}
65+
output += "```GO\n" + string(testFileContent) + "\n```\n"
66+
}
67+
68+
output += "\n## Rehearsal Solutions\n"
69+
70+
for _, entry := range entries {
71+
output += fmt.Sprintf("\n### %s\n", entry.Name)
72+
73+
testFileContent, err := os.ReadFile(filepath.Join(dir, section, entry.SolutionFileName))
74+
if err != nil {
75+
output += fmt.Sprintf("Error reading test file: %s\n", err)
76+
continue
77+
}
78+
output += "```GO\n" + string(testFileContent) + "\n```\n"
79+
}
80+
81+
return output
82+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package problems
2+
3+
import "testing"
4+
5+
func TestNewRehearsalEntry(t *testing.T) {
6+
input := `## Rehearsal
7+
8+
* [Bubble Sort](./bubble_sort_test.go), [Solution](./bubble_sort.go)
9+
* [Reverse Array In-place](./reverse_inplace_test.go), [Solution](./reverse_inplace.go)
10+
* [Add Two Numbers](./add_two_numbers_test.go), [Solution](./add_two_numbers.go)
11+
* [Find Duplicate in Array](./find_duplicate_in_array_test.go), [Solution](./find_duplicate_in_array.go)
12+
* [Zero Sum Triplets](./zero_sum_triplets_test.go), [Solution](./zero_sum_triplets.go)
13+
* [Product of All Other Elements](./product_of_all_other_elements_test.go), [Solution](./product_of_all_other_elements.go)`
14+
15+
entries, err := newRehearsalEntry(input)
16+
if err != nil {
17+
t.Fatalf("unexpected error: %v", err)
18+
}
19+
20+
if len(entries) != 6 {
21+
t.Fatalf("expected 5 entries, got %d", len(entries))
22+
}
23+
24+
strOutput := stringRehearsalEntries(entries)
25+
if strOutput == "" {
26+
t.Fatal("expected non-empty string, got empty string")
27+
}
28+
}

.github/cmd/cmd/sections.go renamed to .github/cmd/pkg/problems/sections.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
package cmd
1+
package problems
22

3-
var sections = []string{
3+
// OrderedSections list of sections in go-dsa corresponding to directory names.
4+
var OrderedSections = []string{
45
"array",
56
"strings",
67
"linkedlist",

.github/workflows/tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
steps:
3333
- uses: actions/checkout@v4
3434
- uses: actions/setup-go@v5
35-
- run: go test -v -coverprofile=profile.cov ./...
35+
- run: make ci
3636
- uses: shogo82148/actions-goveralls@v1
3737
with:
3838
path-to-profile: profile.cov

0 commit comments

Comments
 (0)