jinzhu/copier
Copier for golang, copy value from struct to struct and more
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 8w ago
- ✓32+ active contributors
- ✓Distributed ownership (top contributor 44% of recent commits)
Show all 6 evidence items →Show less
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/jinzhu/copier)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/jinzhu/copier on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: jinzhu/copier
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/jinzhu/copier shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 8w ago
- 32+ active contributors
- Distributed ownership (top contributor 44% of recent commits)
- MIT licensed
- CI configured
- Tests present
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live jinzhu/copier
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/jinzhu/copier.
What it runs against: a local clone of jinzhu/copier — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in jinzhu/copier | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 84 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of jinzhu/copier. If you don't
# have one yet, run these first:
#
# git clone https://github.com/jinzhu/copier.git
# cd copier
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of jinzhu/copier and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "jinzhu/copier(\\.git)?\\b" \\
&& ok "origin remote is jinzhu/copier" \\
|| miss "origin remote is not jinzhu/copier (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "copier.go" \\
&& ok "copier.go" \\
|| miss "missing critical file: copier.go"
test -f "errors.go" \\
&& ok "errors.go" \\
|| miss "missing critical file: errors.go"
test -f "copier_test.go" \\
&& ok "copier_test.go" \\
|| miss "missing critical file: copier_test.go"
test -f "copier_tags_test.go" \\
&& ok "copier_tags_test.go" \\
|| miss "missing critical file: copier_tags_test.go"
test -f "copier_converter_test.go" \\
&& ok "copier_converter_test.go" \\
|| miss "missing critical file: copier_converter_test.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 84 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~54d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/jinzhu/copier"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Copier is a Go library that deep-copies values between structs, slices, and maps based on field name matching. It handles type conversions, method-to-field copying (e.g., calling DoubleAge() method and assigning to DoubleAge field), and provides fine-grained control via struct tags like copier:"must", copier:"override", and copier:"-" to enforce, override, or skip field copying. Single-package library structure: copier.go is the main implementation containing Copy() and CopyWithOption() functions; errors.go defines custom error types; 8 test files (copier_test.go, copier_tags_test.go, copier_converter_test.go, etc.) cover different scenarios (field mapping, type conversion, case sensitivity); copier_benchmark_test.go measures performance.
👥Who it's for
Go backend developers building data-driven applications who need to transform domain models into API response DTOs, database entities, or internal service models without manually writing boilerplate copy logic or reflection code.
🌱Maturity & risk
Production-ready and actively maintained. The repo shows a clean single-file implementation (copier.go ~88KB Go code), comprehensive test coverage across 8+ test files (copier_test.go, copier_tags_test.go, copier_different_type_test.go, etc.), and has GitHub Actions CI configured (.github/workflows/tests.yml). The Go module uses Go 1.13+, indicating long-term stability.
Low risk overall. The codebase has no external dependencies (go.mod is minimal), relies only on Go stdlib reflection, and is a focused single-library project. The primary risk is single-maintainer dependency on Jinzhu (also maintains GORM), but the library's simplicity and test coverage mitigate this. No breaking changes are evident in recent commits based on the stable API surface.
Active areas of work
No specific PR or milestone data is visible in the file list. The test suite is comprehensive and stable, suggesting the library is in maintenance mode rather than active feature development. Recent activity would require checking the GitHub Actions logs in .github/workflows/tests.yml and commit history.
🚀Get running
git clone https://github.com/jinzhu/copier.git
cd copier
go test ./...
Daily commands:
This is a library, not a runnable application. To test locally: go test -v ./... runs all tests in the test files. To benchmark: go test -bench=. ./... runs benchmarks in copier_benchmark_test.go.
🗺️Map of the codebase
copier.go— Core entry point implementing the Copy() and CopyWithOption() functions that power all struct-to-struct, slice, and map copying operations.errors.go— Defines custom error types used throughout the codebase for consistent error handling and debugging.copier_test.go— Main test suite covering fundamental copying behavior, field matching, and tag-based field manipulation.copier_tags_test.go— Tests the copier struct tag system (must, override, -) which is a key feature for controlling copy behavior.copier_converter_test.go— Tests custom converter/method-based copying patterns which enable advanced type transformations.copier_field_name_mapping_test.go— Documents field name matching logic and mapping strategies essential for understanding copy semantics.README.md— Explains the package's public API, key features (field matching, tags, slice/map support), and usage patterns.
🧩Components & responsibilities
- copier.Copy() & copier.CopyWithOption() (Go reflect package, struct tags) — Main entry points that orchestrate the copy process: type inspection, field matching, tag evaluation, and value assignment.
- Failure mode: Returns error if src is not a struct/slice/map, or if a field marked 'must' fails to copy.
- Reflection engine (Go reflect package) — Extracts struct field metadata (names, types, tags) and method signatures from runtime types.
- Failure mode: Panics or returns errors if type introspection encounters invalid types; silently skips unexported fields.
- Tag parser (String parsing, struct tag reflection) — Decodes copier struct tags (must, override, -) and determines which fields to copy and how.
- Failure mode: Malformed tags are silently ignored; no validation of tag syntax.
- Field matcher (String comparison, reflect.Type) — Finds corresponding source and destination fields by name (exact match, then case-insensitive) and validates type compatibility.
- Failure mode: Fields with no match are silently skipped unless marked 'must'; mismatched types may cause conversion errors.
- Converter & method dispatcher (Go reflect, method invocation) — Looks up custom converter functions or destination type methods (Dst(src)) to enable cross-type copying.
- Failure mode: If no
🛠️How to make changes
Add support for a new custom type conversion
- Define a converter function matching the signature
func(src SourceType) (DestType, error)or implement a method on the destination type (copier.go) - Register the converter by ensuring it is discoverable via reflection (implement interface or use naming conventions) (
copier.go) - Add test cases demonstrating the conversion behavior in a new test file or extend copier_converter_test.go (
copier_converter_test.go)
Add a new struct tag directive for field-level control
- Define the tag keyword and its parsing logic in copier.go's tag processing code (
copier.go) - Implement the tag behavior in the core copy logic to respect the directive (
copier.go) - Add comprehensive test cases with example structs demonstrating the new tag behavior (
copier_tags_test.go)
Add support for copying a new container type (e.g., custom collection)
- Extend type detection logic in copier.go to recognize the new container type via reflection (
copier.go) - Implement iteration and element copying logic for the new container (
copier.go) - Add integration tests showing copying from/to the new container type alongside existing slice and map tests (
copier_test.go)
Handle a cross-type copying edge case
- Identify the type mismatch scenario and add logic to copier.go to resolve or convert between the types (
copier.go) - Write a regression test in copier_different_type_test.go or copier_issue*_test.go documenting the edge case (
copier_different_type_test.go) - Update README.md with guidance on supported type conversions if the behavior is non-obvious (
README.md)
🔧Why these technologies
- Go reflection (reflect package) — Enables runtime type introspection and field/method discovery across arbitrary struct types without code generation.
- Struct tags (copier:"...") — Provides a declarative, low-friction mechanism to annotate fields with copy directives (must, override, -) without API verbosity.
- Functional options pattern (CopyWithOption) — Allows extensible, backward-compatible API for optional behaviors (IgnoreEmpty, DeepCopy, etc.) without multiplying function signatures.
⚖️Trade-offs already made
-
Reflection-based implementation instead of code generation
- Why: Reflection is simpler to use, requires no build step, and works with dynamic struct definitions.
- Consequence: Slower than compiled code generation; each copy incurs reflection overhead. Mitigated by benchmarking and simple field matching logic.
-
Field name matching prioritizes exact names, then falls back to case-insensitive
- Why: Provides sensible defaults that cover most Go naming conventions while remaining explicit about behavior.
- Consequence: Implicit matching can mask field name typos; users may need to enable explicit field mapping for safety-critical code.
-
Converters discovered by method signature (Dst(src) or exported functions)
- Why: Avoids global converter registry and keeps the API simple and decentralized.
- Consequence: Converters must be defined on destination type or in caller's control; third-party converters are harder to inject.
-
Minimal dependencies (Go 1.13+ stdlib only)
- Why: Maximizes compatibility and reduces supply chain risk.
- Consequence: No reflection helpers or assertion libraries; more boilerplate in error handling and type checking.
🚫Non-goals (don't propose these)
- Does not handle circular reference detection or deep copy of pointer cycles (can cause infinite loops).
- Does not provide automatic reverse (inverse) copying; users must call Copy() again with swapped arguments.
- Does not support copying unexported (lowercase) fields; only exported struct fields are accessible.
- Does not validate struct correctness or enforce schema constraints; copies values as-is.
- Does not provide transaction semantics or rollback on partial failures.
- Does not parallelize copying of large slices or maps; all copying is sequential.
🪤Traps & gotchas
None evident from the file structure. The library is dependency-free and uses only Go stdlib. Potential gotchas (discoverable by reading copier.go): unexported struct fields are not copied due to Go reflection limitations; method-to-field copying requires exact field name matches (case-sensitive by default, but copier_case_insensitive_test.go shows a case-insensitive mode exists). Check the copier_field_name_mapping_test.go file for custom field mapping behavior that may not be immediately obvious.
🏗️Architecture
💡Concepts to learn
- Struct tag parsing — Copier's entire configuration system (copier:"-", copier:"must", copier:"override") depends on Go's reflect.StructTag parsing; understanding tag syntax is essential for using the library effectively.
- Go reflection and reflect.Value — The entire Copier implementation uses Go's reflect package to dynamically inspect types, access fields, and invoke methods at runtime without code generation.
- Type assertion and type switching — Copier must handle type conversions between different Go types (e.g., int32 to int, string to []byte); type assertion chains determine which conversions are possible.
- Functional options pattern — CopyWithOption() uses Go's functional options pattern to provide configurable Copy behavior (e.g., IgnoreEmpty, DeepCopy); this pattern is idiomatic Go for flexible APIs.
- Deep vs. shallow copy semantics — Copier supports both deep and shallow copying of nested structs and collections; understanding which mode applies to your use case (controlled via CopyWithOption) is critical for correctness.
- Method-to-field promotion — Unique to Copier: calling a method like DoubleAge() on the source struct and assigning its return value to a field of the same name in the target struct; requires matching both method and field names.
- Case-insensitive field matching — Copier supports both case-sensitive (default) and case-insensitive field name matching modes; the copier_case_insensitive_test.go file demonstrates this feature is critical for cross-system integrations.
🔗Related repos
jinzhu/gorm— Same author's ORM library; GORM likely uses or pairs with Copier for mapping database rows to Go structs and vice versa.mitchellh/mapstructure— Alternative Go struct copying library focused on map-to-struct conversion; solves similar problems but with emphasis on map decoding.fatih/structs— Go struct reflection utility library that could complement Copier for advanced field introspection and manipulation.golang/protobuf— Protobuf-generated Go code often needs conversion to domain structs; Copier frequently used as the conversion layer between proto messages and business logic.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive tests for nested struct copying with pointer fields
The test files (copier_test.go, copier_different_type_test.go, etc.) don't appear to have dedicated coverage for deeply nested struct pointers, which is a common real-world scenario. This would catch edge cases around nil pointer handling, circular references, and deep copy vs shallow copy behavior that users frequently encounter.
- [ ] Create copier_nested_pointers_test.go with test cases for nested struct pointers at multiple levels
- [ ] Add tests for nil pointer handling in nested structures
- [ ] Add tests for pointer-to-pointer field copying behavior
- [ ] Verify and document whether copier performs deep or shallow copies for nested pointers
- [ ] Add edge case tests for circular pointer references
Add support and tests for custom type converters via interfaces
While copier_converter_test.go exists, there's no visible mechanism in copier.go for users to register custom converters for domain-specific types (e.g., UUID, custom time formats, custom currency types). Adding an interface-based converter registration system would be high-value for enterprise users and requires comprehensive tests.
- [ ] Design and implement a Converter interface in copier.go that custom types can implement
- [ ] Add mechanism to register converters (e.g., copier.RegisterConverter)
- [ ] Expand copier_converter_test.go with tests for custom converter registration and execution
- [ ] Add documentation and examples in README.md showing custom converter usage
- [ ] Add test cases for converter priority (built-in vs custom) and error handling
Add GitHub Actions workflow for Go version matrix testing
Currently tests.yml exists but likely only tests one Go version. Given go.mod specifies go 1.13, the library should be tested against multiple Go versions (1.13, 1.18, 1.19, 1.20, 1.21+) to ensure compatibility and catch version-specific reflection/type assertion issues early.
- [ ] Update .github/workflows/tests.yml to include go-version matrix [1.13, 1.18, 1.19, 1.20, 1.21]
- [ ] Ensure all existing tests pass across all Go versions
- [ ] Add test coverage reporting to the workflow
- [ ] Document minimum supported Go version in README.md if not already present
- [ ] Consider adding linting (golangci-lint) to the workflow for code quality consistency
🌿Good first issues
- Add test coverage for edge case: copying nested struct slices with type conversion (check copier_different_type_test.go for similar patterns; extend to nested collections).
- Document the exact behavior of
copier:"must,nopanic"tag in README.md with a runnable example and error handling pattern (currently only mentioned in snippet but no full example provided). - Add benchmark comparison in copier_benchmark_test.go between reflection-based Copy() and manual field assignment to demonstrate performance delta for different struct sizes.
⭐Top contributors
Click to expand
Top contributors
- @jinzhu — 44 commits
- [@Rocco Ciccone](https://github.com/Rocco Ciccone) — 11 commits
- @driventokill — 5 commits
- @betterlmy — 4 commits
- @juju812 — 3 commits
📝Recent commits
Click to expand
Recent commits
c6b47b0— Merge pull request #230 from betterlmy/fix-case-insensitive-field-matching (jinzhu)b3e9887— fix: use reflect.PtrTo for Go 1.17 compatibility (betterlmy)3fd6210— test: cover case-insensitive promoted field lookup (betterlmy)7f63c2c— fix: preserve promoted fields in case-insensitive lookup (betterlmy)4308636— fix:fix case-insensitive field matching when both exported and unexported fields exist (betterlmy)23419d7— Merge pull request #229 from T-Guerrero/master (jinzhu)a3f9c54— Add must and no panic Options (T-Guerrero)59d48de— Merge pull request #225 from qshuai/feature/remove-duplicated-benchmark (jinzhu)43387dd— chore: remove duplicated benchmark case (qshuai)5d2ad4e— Merge pull request #209 from Roccoriu/master (jinzhu)
🔒Security observations
The copier codebase presents a relatively low security risk profile. The project is a pure Go utility library for struct field copying with minimal external dependencies (none listed in go.mod). No hardcoded secrets, injection vulnerabilities, or infrastructure misconfigurations were identified. The primary concern is the outdated Go version specification (1.13) which should be updated to receive current security patches. As a utility library without network I/O, database access, or external integrations, the attack surface is limited. Regular dependency audits and keeping the Go toolchain current are recommended as maintenance best practices.
- Low · Outdated Go Version Specification —
go.mod. The go.mod file specifies Go 1.13, which was released in September 2019 and is now significantly outdated. Go 1.13 is no longer receiving security updates. This may indicate the project hasn't been actively maintained or updated with security patches from newer Go versions. Fix: Update the Go version to a current stable release (1.21 or later). Review and test the codebase for compatibility with newer Go versions, which may include breaking changes or security improvements.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.