Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,24 @@ code-path: path/to/code/root
docs-path: path/to/docs/root
code-includes: "**/*.java,**/*.gradle"
doc-excludes: "**/*-old.*,**/deprecated/*.*"
embed-mappings:
- code-path: path/to/code/root/kotlin
docs-path: path/to/other/docs
```

For multiple independent documentation targets, use `embeddings` instead:

```yaml
embeddings:
- name: java
code-path: path/to/code/root/java
docs-path: path/to/java/docs
code-includes: "**/*.java"
- name: kotlin
code-path:
- name: samples
path: path/to/code/root/kotlin-samples
- name: runtime
path: path/to/code/root/kotlin-runtime
docs-path: path/to/kotlin/docs
separator: "---"
```

The available fields for the configuration file are:
Expand Down Expand Up @@ -131,7 +146,10 @@ The available fields for the configuration file are:
It may be represented as a comma-separated string list or as a YAML sequence.
* `fragments-path`: (Optional) Directory for code fragments.
* `separator`: (Optional) Separator for fragments.
* `embed-mappings`: (Optional) A list of custom mappings, each containing `code-path` and `docs-path`.
* `embeddings`: (Optional) A list of complete embedding configurations for multiple
documentation targets. When `embeddings` is set, do not set root-level `code-path`
or `docs-path`. Define `code-path`, `docs-path`, and optional settings inside each entry.
Each entry must set a unique `name`.
Comment on lines +149 to +152
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both options set, it should throw with meaningfull message to make configuration issues clear.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does.

image


These settings have the same role as the command-line arguments.

Expand Down
73 changes: 47 additions & 26 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ package cli
import (
_type "embed-code/embed-code-go/type"
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"embed-code/embed-code-go/analyzing"
Expand Down Expand Up @@ -61,14 +61,13 @@ import (
// Separator — a string that's inserted between multiple partitions of a single fragment.
// The default value is "...".
//
// EmbedMappings — an additional optional list of configs, which will be executed together with the
// main one. A config written here has higher priority and may overwrite the base one.
// Embeddings — independent configurations for embedding multiple documentation targets.
//
// Info - specifies whether info-level logs should be shown.
//
// Stacktrace - specifies whether error stack traces should be shown.
//
// ConfigPath — a path to a yaml configuration file which contains the roots.
// ConfigPath — a path to a yaml configuration file which contains roots or embeddings.
//
// Mode — defines the mode of embed-code execution.
type Config struct {
Expand All @@ -79,17 +78,23 @@ type Config struct {
DocExcludes _type.StringList `yaml:"doc-excludes"`
FragmentsPath string `yaml:"fragments-path"`
Separator string `yaml:"separator"`
EmbedMappings []EmbedMapping `yaml:"embed-mappings"`
Embeddings []EmbeddingConfig `yaml:"embeddings"`
Info bool `yaml:"info"`
Stacktrace bool `yaml:"stacktrace"`
ConfigPath string
Mode string
}

// EmbedMapping is a pair of a source code path and a destination docs path to perform an embedding.
type EmbedMapping struct {
CodePath _type.NamedPathList `yaml:"code-path"`
DocsPath string `yaml:"docs-path"`
// EmbeddingConfig contains a complete configuration for one embedding target.
type EmbeddingConfig struct {
Name string `yaml:"name"`
CodePaths _type.NamedPathList `yaml:"code-path"`
DocsPath string `yaml:"docs-path"`
CodeIncludes _type.StringList `yaml:"code-includes"`
DocIncludes _type.StringList `yaml:"doc-includes"`
DocExcludes _type.StringList `yaml:"doc-excludes"`
FragmentsPath string `yaml:"fragments-path"`
Separator string `yaml:"separator"`
}

// EmbedCodeSamplesResult is result of the EmbedCodeSamples method.
Expand Down Expand Up @@ -193,8 +198,8 @@ func FillArgsFromConfigFile(args Config) (Config, error) {
if len(configFields.CodeIncludes) > 0 {
args.CodeIncludes = configFields.CodeIncludes
}
if len(configFields.EmbedMappings) > 0 {
args.EmbedMappings = configFields.EmbedMappings
if len(configFields.Embeddings) > 0 {
args.Embeddings = configFields.Embeddings
}
if len(configFields.DocIncludes) > 0 {
args.DocIncludes = configFields.DocIncludes
Expand All @@ -219,34 +224,50 @@ func FillArgsFromConfigFile(args Config) (Config, error) {
// userArgs — a Config with user-provided args.
func BuildEmbedCodeConfiguration(userArgs Config) []configuration.Configuration {
embedCodeConfigs := make([]configuration.Configuration, 0)
excludedConfigs := make([]string, 0)

if len(userArgs.EmbedMappings) > 0 {
for _, mapping := range userArgs.EmbedMappings {
embedCodeConfig := configWithOptionalParams(userArgs)
embedCodeConfig.CodeRoots = mapping.CodePath
embedCodeConfig.DocumentationRoot = mapping.DocsPath

// As the top config may overwrite those files, we need to exclude it from the embedding
excludedConfigs = append(excludedConfigs, fmt.Sprintf("%s**/*.*", mapping.DocsPath))
embedCodeConfigs = append(embedCodeConfigs, embedCodeConfig)
if len(userArgs.Embeddings) > 0 {
for _, embedding := range userArgs.Embeddings {
embedCodeConfigs = append(embedCodeConfigs, configFromEmbedding(embedding))
}
return embedCodeConfigs
}

embedCodeConfig := configWithOptionalParams(userArgs)
embedCodeConfig.CodeRoots = userArgs.BaseCodePaths
embedCodeConfig.DocumentationRoot = userArgs.BaseDocsPath

if len(userArgs.DocExcludes) > 0 {
embedCodeConfig.DocExcludes = append(embedCodeConfig.DocExcludes, excludedConfigs...)
} else {
embedCodeConfig.DocExcludes = excludedConfigs
}
embedCodeConfigs = append(embedCodeConfigs, embedCodeConfig)

return embedCodeConfigs
}

// Creates a new Configuration from one complete embedding config.
func configFromEmbedding(embedding EmbeddingConfig) configuration.Configuration {
embedCodeConfig := configuration.NewConfiguration()
embedCodeConfig.Name = embedding.Name
embedCodeConfig.CodeRoots = embedding.CodePaths
embedCodeConfig.DocumentationRoot = embedding.DocsPath

if len(embedding.CodeIncludes) > 0 {
embedCodeConfig.CodeIncludes = embedding.CodeIncludes
}
if len(embedding.DocIncludes) > 0 {
embedCodeConfig.DocIncludes = embedding.DocIncludes
}
if len(embedding.DocExcludes) > 0 {
embedCodeConfig.DocExcludes = embedding.DocExcludes
}
if isNotEmpty(embedding.FragmentsPath) {
embedCodeConfig.FragmentsDir = embedding.FragmentsPath
}
embedCodeConfig.FragmentsDir = filepath.Join(embedCodeConfig.FragmentsDir, embedding.Name)
if isNotEmpty(embedding.Separator) {
embedCodeConfig.Separator = embedding.Separator
}

return embedCodeConfig
}

// Creates a new Configuration with the filled optional properties from the user args.
func configWithOptionalParams(userArgs Config) configuration.Configuration {
embedCodeConfig := configuration.NewConfiguration()
Expand Down
102 changes: 99 additions & 3 deletions cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package cli_test

import (
"embed-code/embed-code-go/cli"
"embed-code/embed-code-go/configuration"
_type "embed-code/embed-code-go/type"
"os"
"path/filepath"
Expand Down Expand Up @@ -65,6 +66,30 @@ var _ = Describe("CLI validation", func() {
Expect(cli.ValidateConfig(config)).Error().ShouldNot(HaveOccurred())
Expect(cli.ValidateConfigFile(config)).Error().ShouldNot(HaveOccurred())
})

It("should pass validation when embeddings are set", func() {
config := cli.Config{
Mode: cli.ModeCheck,
Embeddings: []cli.EmbeddingConfig{baseEmbeddingConfig()},
}

Expect(cli.ValidateConfig(config)).Error().ShouldNot(HaveOccurred())
})

It("should store embedding fragments under a named subfolder", func() {
embedding := baseEmbeddingConfig()
embedding.FragmentsPath = "/tmp/fragments"
config := cli.Config{
Mode: cli.ModeCheck,
Embeddings: []cli.EmbeddingConfig{embedding},
}

embedConfigs := cli.BuildEmbedCodeConfiguration(config)

Expect(embedConfigs).To(HaveLen(1))
Expect(embedConfigs[0].Name).To(Equal("docs"))
Expect(embedConfigs[0].FragmentsDir).To(Equal(filepath.Join("/tmp/fragments", "docs")))
})
})

Context("with invalid config", func() {
Expand Down Expand Up @@ -107,7 +132,7 @@ var _ = Describe("CLI validation", func() {

Expect(cli.ValidateConfig(invalidConfig)).Error().Should(HaveOccurred())
Expect(cli.ValidateConfig(invalidConfig).Error()).Should(Equal(
"code-path and docs-path must both be set"))
"`code-path` and `docs-path` must both be set"))
})

It("should fail validation when config, code and docs paths are set at the same time", func() {
Expand All @@ -119,17 +144,79 @@ var _ = Describe("CLI validation", func() {
"config path cannot be set when code-path, docs-path or optional params are set"))
})

It("should correctly convert embed mappings to a few configs", func() {
It("should fail validation when embeddings and root paths are set at the same time", func() {
invalidConfig := baseCliConfig()
invalidConfig.Embeddings = []cli.EmbeddingConfig{baseEmbeddingConfig()}

Expect(cli.ValidateConfig(invalidConfig)).Error().Should(HaveOccurred())
Expect(cli.ValidateConfig(invalidConfig).Error()).Should(Equal(
"`code-path` and `docs-path` cannot be set when `embeddings` are set"))
})

It("should fail validation when embeddings and root optional params are set at the same time", func() {
invalidConfig := cli.Config{
Mode: cli.ModeCheck,
CodeIncludes: []string{"**/*.java"},
Embeddings: []cli.EmbeddingConfig{baseEmbeddingConfig()},
}

Expect(cli.ValidateConfig(invalidConfig)).Error().Should(HaveOccurred())
Expect(cli.ValidateConfig(invalidConfig).Error()).Should(Equal(
"root optional embedding options cannot be set when `embeddings` are set"))
})

It("should fail validation when embedding name is missed", func() {
invalidConfig := cli.Config{
Mode: cli.ModeCheck,
Embeddings: []cli.EmbeddingConfig{baseEmbeddingConfig()},
}
invalidConfig.Embeddings[0].Name = ""

Expect(cli.ValidateConfig(invalidConfig)).Error().Should(HaveOccurred())
Expect(cli.ValidateConfig(invalidConfig).Error()).Should(Equal(
"embedding #1: `name` must be set"))
})

It("should fail validation when embedding names are duplicated", func() {
embedding := baseEmbeddingConfig()
duplicateEmbedding := baseEmbeddingConfig()
invalidConfig := cli.Config{
Mode: cli.ModeCheck,
Embeddings: []cli.EmbeddingConfig{embedding, duplicateEmbedding},
}

Expect(cli.ValidateConfig(invalidConfig)).Error().Should(HaveOccurred())
Expect(cli.ValidateConfig(invalidConfig).Error()).Should(Equal(
"duplicate embedding names detected:\n- docs"))
})

It("should correctly convert embeddings to a few configs", func() {
config := cli.Config{
Mode: cli.ModeCheck,
ConfigPath: "../test/resources/config_files/embedded_mappings_config.yml",
ConfigPath: "../test/resources/config_files/embeddings_config.yml",
}

fileConfig, err := cli.FillArgsFromConfigFile(config)
embedConfigs := cli.BuildEmbedCodeConfiguration(fileConfig)

Expect(err).ToNot(HaveOccurred())
Expect(embedConfigs).To(HaveLen(3))
Expect(embedConfigs[0].Name).To(Equal("java"))
Expect(embedConfigs[0].CodeRoots[0].Path).To(Equal("test/resources/code/java"))
Expect(embedConfigs[0].DocumentationRoot).To(Equal("test/resources/docs"))
Expect(embedConfigs[0].FragmentsDir).To(Equal(
filepath.Join(configuration.DefaultFragmentsDir, "java")))
Expect(embedConfigs[1].Name).To(Equal("kotlin"))
Expect(embedConfigs[1].CodeRoots[0].Path).To(Equal("test/resources/code/kotlin"))
Expect(embedConfigs[1].DocumentationRoot).To(Equal("test/resources/docs/nested-dir-1"))
Expect(embedConfigs[1].FragmentsDir).To(Equal(
filepath.Join(configuration.DefaultFragmentsDir, "kotlin")))
Expect(embedConfigs[2].Name).To(Equal("nested-java"))
Expect(embedConfigs[2].DocumentationRoot).To(
Equal("test/resources/docs/nested-dir-1/nested-dir-3"))
Expect(embedConfigs[2].FragmentsDir).To(Equal(
filepath.Join(configuration.DefaultFragmentsDir, "nested-java")))
Expect(embedConfigs[2].Separator).To(Equal("---"))
})

})
Expand All @@ -150,6 +237,15 @@ func baseCliConfig() cli.Config {
}
}

func baseEmbeddingConfig() cli.EmbeddingConfig {
baseConfig := baseCliConfig()
return cli.EmbeddingConfig{
Name: "docs",
CodePaths: baseConfig.BaseCodePaths,
DocsPath: baseConfig.BaseDocsPath,
}
}

func configFilePath() string {
currentDir, err := os.Getwd()
if err != nil {
Expand Down
Loading
Loading