Documentation
¶
Overview ¶
Package go_dynamic_questionnaire provides a flexible, condition-based questionnaire system for Go applications.
This package allows you to create dynamic questionnaires where the questions shown to users depend on their previous answers. Questions can have conditional logic using expressions, and the system supports progress tracking and closing remarks.
Basic Usage ¶
See README.md for a quick start guide. For more examples and advanced usage, see the examples/ directory.
Thread Safety ¶
All operations are thread-safe. The same questionnaire instance can be used concurrently by multiple goroutines without any synchronization.
Error Handling ¶
The package provides rich error information through custom error types. Validation errors include context about what went wrong and suggestions for fixing the issue.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ClosingRemark ¶
type ClosingRemark struct {
Id string `json:"id"` // Unique identifier for the remark
Text string `json:"text"` // The message text to display
}
ClosingRemark represents a message shown to users when the questionnaire is completed. Multiple remarks can be shown based on the user's answers and conditional logic.
Example usage in JSON:
{
"id": "thank_you",
"text": "Thank you for your feedback!"
}
type Loader ¶
type Loader interface {
// Load parses the configuration data and populates the provided questionnaire struct.
// The data parameter can be either a file path (string) or raw content ([]byte).
// The q parameter is a pointer to the questionnaire struct to be populated.
//
// Parameters:
// data: Either a file path or raw configuration content
// q: Pointer to the questionnaire struct to populate
//
// Returns:
// error: Parsing errors, file reading errors, or validation errors
Load(data interface{}, q *questionnaire) error
}
Loader defines the interface for loading questionnaire configurations. Each loader implementation is responsible for parsing a specific format (YAML, JSON, etc.) and populating a given questionnaire struct.
The Loader interface allows the system to be easily extended to support additional configuration formats without modifying the core questionnaire logic.
type Progress ¶
type Progress struct {
Current int `json:"current"` // Number of questions answered so far
Total int `json:"total"` // Total number of questions that could be answered
}
Progress represents the user's progress through the questionnaire. It provides information about how many questions have been processed and how many remain, useful for displaying progress bars or indicators.
The progress calculation is based on available questions at each step, taking into account conditional logic. Questions that cannot be reached due to user answers are not counted in the total.
Note: Progress is nil when the questionnaire is completed.
type Question ¶
type Question struct {
Id string `json:"id"` // Unique identifier for the question
Text string `json:"text"` // The question text to display
Answers []string `json:"answers"` // List of answer choices (1-indexed when referenced)
}
Question represents a question that should be presented to the user. This is the external representation used in API responses.
type Questionnaire ¶
type Questionnaire interface {
// Next processes the provided answers and returns the next set of questions,
// progress information, and completion status.
//
// Parameters:
// answers: A map where keys are question IDs and values are 1-indexed answer choices.
// For example, if a question has answers ["Yes", "No", "Maybe"],
// a value of 1 means "Yes", 2 means "No", and 3 means "Maybe".
//
// Returns:
// *Response: Contains the next questions to show, completion status,
// progress information, and closing remarks (if completed).
// error: Returns validation errors for invalid question IDs, out-of-range answers,
// or condition evaluation errors.
//
// The method validates all provided answers before processing. If any answer
// is invalid, the entire operation fails and returns a validation error with
// details about what went wrong.
Next(answers map[string]int) (*Response, error)
}
Questionnaire represents a dynamic questionnaire that can process user answers and determine the next questions to show based on conditional logic.
All methods are thread-safe and can be called concurrently from multiple goroutines. The questionnaire instance is stateless - all state is passed through the answers parameter.
Example usage:
q, err := gdq.New("questionnaire.yaml")
if err != nil {
return err
}
answers := map[string]int{"q1": 2, "q2": 1}
response, err := q.Next(answers)
if err != nil {
return err
}
if response.Completed {
// Handle completion and closing remarks
} else {
// Present next questions to user
}
func New ¶
func New[T config](config T) (Questionnaire, error)
New creates a new Questionnaire instance from either a file path or content (YAML or JSON).
The function accepts two types of input:
- string: Path to a configuration file (.yaml, .yml, or .json)
- []byte: Raw configuration content (YAML or JSON)
Parameters:
config: Either a file path (string) or configuration content ([]byte).
The configuration must contain 'questions' and optionally 'closing_remarks' sections.
Supported formats: YAML (.yaml, .yml) and JSON (.json)
Returns:
Questionnaire: A fully configured questionnaire instance ready for use.
The instance is immutable and thread-safe.
error: Returns configuration errors, file reading errors, parsing errors,
or validation errors if the questionnaire structure is invalid.
Example usage with YAML file:
q, err := gdq.New("surveys/customer-feedback.yaml")
if err != nil {
log.Fatalf("Failed to load questionnaire: %v", err)
}
Example usage with YAML content:
yamlData := []byte(`
questions:
- id: "satisfaction"
text: "How satisfied are you with our service?"
answers: ["Very satisfied", "Satisfied", "Neutral", "Dissatisfied"]
- id: "recommend"
text: "Would you recommend us?"
answers: ["Yes", "No"]
condition: 'answers["satisfaction"] <= 2'
closing_remarks:
- id: "thanks"
text: "Thank you for your feedback!"
`)
q, err := gdq.New(yamlData)
if err != nil {
return fmt.Errorf("questionnaire creation failed: %w", err)
}
Example usage with JSON content:
jsonData := []byte(`{
"questions": [
{
"id": "satisfaction",
"text": "How satisfied are you with our service?",
"answers": ["Very satisfied", "Satisfied", "Neutral", "Dissatisfied"]
},
{
"id": "recommend",
"text": "Would you recommend us?",
"answers": ["Yes", "No"],
"condition": "answers[\"satisfaction\"] <= 2"
}
],
"closing_remarks": [
{
"id": "thanks",
"text": "Thank you for your feedback!"
}
]
}`)
q, err := gdq.New(jsonData)
if err != nil {
return fmt.Errorf("questionnaire creation failed: %w", err)
}
The function validates the questionnaire structure during creation, checking for:
- Duplicate question IDs
- Empty question IDs
- Questions without answer options
- Invalid configuration syntax
type Response ¶
type Response struct {
Questions []Question `json:"questions"` // Next questions to show (empty if completed)
ClosingRemarks []ClosingRemark `json:"closing_remarks,omitempty"` // Closing remarks (only when completed)
Completed bool `json:"completed"` // Whether the questionnaire is finished
Progress *Progress `json:"progress,omitempty"` // Progress information (nil when completed)
}
Response represents the complete response from processing a questionnaire step. It contains all information needed to either continue the questionnaire or handle completion.
The response structure allows clients to:
- Display the next questions to users (Questions field)
- Show progress information (Progress field)
- Handle questionnaire completion (Completed field)
- Display closing messages (ClosingRemarks field)
Example JSON output:
{
"questions": [{"id": "q1", "text": "...", "answers": ["Yes", "No"]}],
"completed": false,
"progress": {"current": 2, "total": 5}
}