Upgrade protobuf-codegen to 3.2.0

This project was upgraded with external_updater.
Usage: tools/external_updater/updater.sh update rust/crates/protobuf-codegen
For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md

Bug: 270895633
Test: TreeHugger
Change-Id: I7d1c61acd4a3c215219ef9f30647678480cb8a22
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 8ec5394..0cfc90b 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
 {
   "git": {
-    "sha1": "ec31ce829473039ac598ca6fdcb245cbd6fa82ba"
+    "sha1": "7155092f3df112159d55132081937e1fe5c30490"
   },
   "path_in_vcs": "protobuf-codegen"
 }
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index a698555..b0ae081 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,11 +24,17 @@
     name: "libprotobuf_codegen",
     crate_name: "protobuf_codegen",
     cargo_env_compat: true,
-    cargo_pkg_version: "2.27.1",
+    cargo_pkg_version: "3.2.0",
     srcs: ["src/lib.rs"],
-    edition: "2015",
+    edition: "2021",
     rustlibs: [
+        "libanyhow",
+        "libonce_cell",
         "libprotobuf",
+        "libprotobuf_parse",
+        "libregex",
+        "libtempfile",
+        "libthiserror",
     ],
     product_available: true,
     vendor_available: true,
@@ -38,16 +44,22 @@
     name: "protobuf-codegen_test_src_lib",
     crate_name: "protobuf_codegen",
     cargo_env_compat: true,
-    cargo_pkg_version: "2.27.1",
+    cargo_pkg_version: "3.2.0",
     srcs: ["src/lib.rs"],
     test_suites: ["general-tests"],
     auto_gen_config: true,
     test_options: {
         unit_test: true,
     },
-    edition: "2015",
+    edition: "2021",
     rustlibs: [
+        "libanyhow",
+        "libonce_cell",
         "libprotobuf",
+        "libprotobuf_parse",
+        "libregex",
+        "libtempfile",
+        "libthiserror",
     ],
 }
 
@@ -55,12 +67,18 @@
     name: "protoc-gen-rust",
     crate_name: "protoc_gen_rust",
     cargo_env_compat: true,
-    cargo_pkg_version: "2.27.1",
+    cargo_pkg_version: "3.2.0",
     srcs: ["src/bin/protoc-gen-rust.rs"],
-    edition: "2015",
+    edition: "2021",
     rustlibs: [
+        "libanyhow",
+        "libonce_cell",
         "libprotobuf",
         "libprotobuf_codegen",
+        "libprotobuf_parse",
+        "libregex",
+        "libtempfile",
+        "libthiserror",
     ],
     product_available: true,
     vendor_available: true,
diff --git a/Cargo.lock b/Cargo.lock
index e9b0703..c92bdea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,14 +3,287 @@
 version = 3
 
 [[package]]
-name = "protobuf"
-version = "2.27.1"
+name = "aho-corasick"
+version = "0.7.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "protobuf"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror",
+]
 
 [[package]]
 name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
 dependencies = [
+ "anyhow",
+ "once_cell",
  "protobuf",
+ "protobuf-parse",
+ "regex",
+ "tempfile",
+ "thiserror",
 ]
+
+[[package]]
+name = "protobuf-parse"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49"
+dependencies = [
+ "anyhow",
+ "indexmap",
+ "log",
+ "protobuf",
+ "protobuf-support",
+ "tempfile",
+ "thiserror",
+ "which",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "which"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index ca327e2..43e4376 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,17 +10,17 @@
 # See Cargo.toml.orig for the original contents.
 
 [package]
+edition = "2021"
 name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
 authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"]
 description = """
 Code generator for rust-protobuf.
 
-Includes a library and `protoc-gen-rust` binary.
-
-See `protoc-rust` and `protobuf-codegen-pure` crates.
+Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rust` binary.
 """
 homepage = "https://github.com/stepancheg/rust-protobuf/"
+readme = "README.md"
 license = "MIT"
 repository = "https://github.com/stepancheg/rust-protobuf/"
 
@@ -35,10 +35,23 @@
 path = "src/bin/protoc-gen-rust.rs"
 test = false
 
-[[bin]]
-name = "protobuf-bin-gen-rust-do-not-use"
-path = "src/bin/protobuf-bin-gen-rust-do-not-use.rs"
-test = false
+[dependencies.anyhow]
+version = "1.0.53"
+
+[dependencies.once_cell]
+version = "1.10.0"
 
 [dependencies.protobuf]
-version = "=2.27.1"
+version = "=3.2.0"
+
+[dependencies.protobuf-parse]
+version = "=3.2.0"
+
+[dependencies.regex]
+version = "1.5.5"
+
+[dependencies.tempfile]
+version = "3"
+
+[dependencies.thiserror]
+version = "1.0.30"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 3400e66..4cfd140 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,23 +1,29 @@
 [package]
 name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
 authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"]
+edition = "2021"
 license = "MIT"
 homepage = "https://github.com/stepancheg/rust-protobuf/"
 repository = "https://github.com/stepancheg/rust-protobuf/"
 description = """
 Code generator for rust-protobuf.
 
-Includes a library and `protoc-gen-rust` binary.
-
-See `protoc-rust` and `protobuf-codegen-pure` crates.
+Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rust` binary.
 """
 
 [lib]
 bench = false
 
 [dependencies]
-protobuf = { path = "../protobuf", version = "=2.27.1" }
+thiserror = "1.0.30"
+anyhow    = "1.0.53"
+regex     = "1.5.5"
+once_cell = "1.10.0"
+tempfile  = "3"
+
+protobuf = { path = "../protobuf", version = "=3.2.0" }
+protobuf-parse = { path = "../protobuf-parse", version = "=3.2.0" }
 
 [[bin]]
 
@@ -25,11 +31,5 @@
 path = "src/bin/protoc-gen-rust.rs"
 test = false
 
-[[bin]]
-
-name = "protobuf-bin-gen-rust-do-not-use"
-path = "src/bin/protobuf-bin-gen-rust-do-not-use.rs"
-test = false
-
 [package.metadata.docs.rs]
 all-features = true
diff --git a/METADATA b/METADATA
index 4593845..1aefc7a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/protobuf-codegen
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "protobuf-codegen"
 description: "Code generator for rust-protobuf.  Includes a library and `protoc-gen-rust` binary.  See `protoc-rust` and `protobuf-codegen-pure` crates."
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-2.27.1.crate"
+    value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-3.2.0.crate"
   }
-  version: "2.27.1"
+  version: "3.2.0"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2022
-    month: 3
-    day: 1
+    year: 2023
+    month: 4
+    day: 3
   }
 }
diff --git a/README.md b/README.md
index 2ab7e05..5f1e2d7 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,65 @@
 <!-- cargo-sync-readme start -->
 
-# Protobuf code generator
+# Protobuf code generator for `protobuf` crate
 
-This crate contains protobuf code generator implementation
-and a `protoc-gen-rust` `protoc` plugin.
+This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build.
 
-This crate:
-* provides `protoc-gen-rust` plugin for `protoc` command
-* implement protobuf codegen
+# How to generate code
 
-This crate is not meant to be used directly, in fact, it does not provide any public API
-(except for `protoc-gen-rust` binary).
+There are three main ways to generate `.rs` files from `.proto` files:
+* using `protoc` command line tool and `protoc-gen-rust` plugin
+* using this crate `Codegen` with pure rust parser
+* using this crate `Codegen` with `protoc` parser
 
-Code can be generated with either:
-* `protoc-gen-rust` plugin for `protoc` or
-* [`protoc-rust`](https://docs.rs/protoc) crate
-  (code generator which depends on `protoc` binary for parsing of `.proto` files)
-* [`protobuf-codegen-pure`](https://docs.rs/protobuf-codegen-pure) crate,
-  similar API to `protoc-rust`, but uses pure rust parser of `.proto` files.
+Which one should you use depends on your needs.
 
-# `protoc-gen-rust` plugin for `protoc`
+If you are using non-cargo build system (like Bazel), you might prefer
+using `protoc-gen-rust` plugin for `protoc`.
 
-When non-cargo build system is used, consider using standard protobuf code generation pattern:
-`protoc` command does all the work of handling paths and parsing `.proto` files.
-When `protoc` is invoked with `--rust_out=` option, it invokes `protoc-gen-rust` plugin.
-provided by this crate.
+If you build with `cargo`, you probably want to use `Codegen` from this crate.
 
-When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+# Protoc parser vs pure rust parser
 
-## How to use `protoc-gen-rust` if you have to
+There are two protobuf parsers which can be plugged into this crate:
+* `protoc`-based parser (`protoc` is a command like utility from Google protobuf)
+* pure rust parser (`protobuf-parse` crate)
+
+`protoc`-based parser is expected to parse `.proto` files very correctly:
+all Google's protobuf implementations rely on it.
+
+While there are no known bugs in `protobuf-parse`, it is not tested very well.
+Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser,
+mostly complex message options specified in `.proto` files.
+I never saw anyone using them, but you have been warned.
+
+Note `protoc` command can be obtained from
+[`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate.
+
+# Example
+
+```rust
+// Use this in build.rs
+protobuf_codegen::Codegen::new()
+    // Use `protoc` parser, optional.
+    .protoc()
+    // Use `protoc-bin-vendored` bundled protoc command, optional.
+    .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+    // All inputs and imports from the inputs must reside in `includes` directories.
+    .includes(&["src/protos"])
+    // Inputs must reside in some of include paths.
+    .input("src/protos/apple.proto")
+    .input("src/protos/banana.proto")
+    // Specify output directory relative to Cargo output directory.
+    .cargo_out_dir("protos")
+    .run_from_script();
+```
+
+## How to use `protoc-gen-rust`
+
+If you have to.
 
 (Note `protoc` can be invoked programmatically with
-[protoc crate](https://docs.rs/protoc))
+[protoc crate](https://docs.rs/protoc/%3E=3.0.0-alpha))
 
 0) Install protobuf for `protoc` binary.
 
@@ -48,11 +76,11 @@
 ```
 
 Protobuf is needed only for code generation, `rust-protobuf` runtime
-does not use `protobuf` library.
+does not use C++ protobuf library.
 
 1) Install `protoc-gen-rust` program (which is `protoc` plugin)
 
-It can be installed either from source or with `cargo install protobuf` command.
+It can be installed either from source or with `cargo install protobuf-codegen` command.
 
 2) Add `protoc-gen-rust` to $PATH
 
@@ -70,11 +98,32 @@
 
 This will generate .rs files in current directory.
 
-# Version 2
+# Customize generate code
 
-This is documentation for version 2 of the crate.
+Sometimes generated code need to be adjusted, e. g. to have custom derives.
 
-[Version 3 of the crate](https://docs.rs/protobuf-codegen/%3E=3.0.0-alpha)
-(currently in development) encapsulates both `protoc` and pure codegens in this crate.
+rust-protobuf provides two options to do that:
+* generated `.rs` files contain `@@protoc_insertion_point(...)` markers
+  (similar markers inserts Google's protobuf generator for C++ or Java).
+  Simple script `sed` one-liners can be used to replace these markers with custom annotations.
+* `Codegen::customize_callback` can be used to patch generated code
+  when invoked from `build.rs` script.
+
+# Serde
+
+rust-protobuf since version 3 no longer directly supports serde.
+
+Rust-protobuf 3 fully supports:
+* runtime reflection
+* JSON parsing and printing via
+ [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping)
+
+Which covers the most of serde use cases.
+
+If you still need serde, generic customization callback (see above) can be used
+to insert `#[serde(...)]` annotations.
+
+[Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)
+in the rust-protobuf repository demonstrates how to do it.
 
 <!-- cargo-sync-readme end -->
diff --git a/src/bin/protobuf-bin-gen-rust-do-not-use.rs b/src/bin/protobuf-bin-gen-rust-do-not-use.rs
deleted file mode 100644
index a6f96ca..0000000
--- a/src/bin/protobuf-bin-gen-rust-do-not-use.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-extern crate protobuf;
-extern crate protobuf_codegen;
-
-use std::fs::*;
-use std::io::Read;
-use std::path::Path;
-
-use protobuf::descriptor::*;
-use protobuf::Message;
-use protobuf_codegen::*;
-
-fn write_file(bin: &str) {
-    let mut is = File::open(&Path::new(bin)).unwrap();
-    let fds = FileDescriptorSet::parse_from_reader(&mut is as &mut dyn Read).unwrap();
-
-    let file_names: Vec<String> = fds
-        .get_file()
-        .iter()
-        .map(|f| f.get_name().to_string())
-        .collect();
-    gen_and_write(
-        fds.get_file(),
-        &file_names,
-        Path::new("."),
-        &Default::default(),
-    )
-    .expect("gen_and_write");
-}
-
-fn main() {
-    let args: Vec<String> = std::env::args().collect();
-    if args.len() != 2 {
-        panic!("must have exactly one argument");
-    }
-    let ref pb_bin = args[1];
-    write_file(&pb_bin);
-}
diff --git a/src/bin/protoc-gen-rust.rs b/src/bin/protoc-gen-rust.rs
index 97ac7d2..8c34e49 100644
--- a/src/bin/protoc-gen-rust.rs
+++ b/src/bin/protoc-gen-rust.rs
@@ -1,5 +1,3 @@
-extern crate protobuf_codegen;
-
 fn main() {
-    protobuf_codegen::protoc_gen_rust_main();
+    protobuf_codegen::protoc_gen_rust::protoc_gen_rust_main();
 }
diff --git a/src/code_writer.rs b/src/code_writer.rs
deleted file mode 100644
index 50eaeb8..0000000
--- a/src/code_writer.rs
+++ /dev/null
@@ -1,387 +0,0 @@
-// TODO: used by grpc-rust, should move it into separate crate.
-#![doc(hidden)]
-
-use std::io::Write;
-
-use inside::protobuf_crate_path;
-use Customize;
-
-/// Field visibility.
-pub enum Visibility {
-    Public,
-    Default,
-}
-
-pub struct CodeWriter<'a> {
-    writer: &'a mut (dyn Write + 'a),
-    indent: String,
-}
-
-impl<'a> CodeWriter<'a> {
-    pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
-        CodeWriter {
-            writer: writer,
-            indent: "".to_string(),
-        }
-    }
-
-    pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
-        (if line.as_ref().is_empty() {
-            self.writer.write_all("\n".as_bytes())
-        } else {
-            let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
-            self.writer.write_all(s.as_bytes())
-        })
-        .unwrap();
-    }
-
-    pub fn write_generated(&mut self) {
-        self.write_line("// This file is generated. Do not edit");
-        self.write_generated_common();
-    }
-
-    pub fn write_generated_by(&mut self, pkg: &str, version: &str) {
-        self.write_line(format!(
-            "// This file is generated by {pkg} {version}. Do not edit",
-            pkg = pkg,
-            version = version
-        ));
-        self.write_generated_common();
-    }
-
-    fn write_generated_common(&mut self) {
-        // https://secure.phabricator.com/T784
-        self.write_line("// @generated");
-
-        self.write_line("");
-        self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
-        self.write_line("#![allow(unknown_lints)]");
-        self.write_line("#![allow(clippy::all)]");
-        self.write_line("");
-        self.write_line("#![allow(unused_attributes)]");
-        self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
-        self.write_line("");
-        self.write_line("#![allow(box_pointers)]");
-        self.write_line("#![allow(dead_code)]");
-        self.write_line("#![allow(missing_docs)]");
-        self.write_line("#![allow(non_camel_case_types)]");
-        self.write_line("#![allow(non_snake_case)]");
-        self.write_line("#![allow(non_upper_case_globals)]");
-        self.write_line("#![allow(trivial_casts)]");
-        self.write_line("#![allow(unused_imports)]");
-        self.write_line("#![allow(unused_results)]");
-    }
-
-    pub fn todo(&mut self, message: &str) {
-        self.write_line(format!("panic!(\"TODO: {}\");", message));
-    }
-
-    pub fn unimplemented(&mut self) {
-        self.write_line(format!("unimplemented!();"));
-    }
-
-    pub fn indented<F>(&mut self, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        cb(&mut CodeWriter {
-            writer: self.writer,
-            indent: format!("{}    ", self.indent),
-        });
-    }
-
-    #[allow(dead_code)]
-    pub fn commented<F>(&mut self, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        cb(&mut CodeWriter {
-            writer: self.writer,
-            indent: format!("// {}", self.indent),
-        });
-    }
-
-    pub fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
-        self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
-    }
-
-    pub fn lazy_static(&mut self, name: &str, ty: &str, customize: &Customize) {
-        self.write_line(&format!(
-            "static {}: {}::rt::LazyV2<{}> = {}::rt::LazyV2::INIT;",
-            name,
-            protobuf_crate_path(customize),
-            ty,
-            protobuf_crate_path(customize),
-        ));
-    }
-
-    pub fn lazy_static_decl_get<F>(&mut self, name: &str, ty: &str, customize: &Customize, init: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.lazy_static(name, ty, customize);
-        self.write_line(&format!("{}.get(|| {{", name));
-        self.indented(|w| init(w));
-        self.write_line(&format!("}})"));
-    }
-
-    pub fn lazy_static_decl_get_simple(
-        &mut self,
-        name: &str,
-        ty: &str,
-        init: &str,
-        customize: &Customize,
-    ) {
-        self.lazy_static(name, ty, customize);
-        self.write_line(&format!("{}.get({})", name, init));
-    }
-
-    pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.write_line(first_line);
-        self.indented(cb);
-        self.write_line(last_line);
-    }
-
-    pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.block(&format!("{} {{", prefix), "}", cb);
-    }
-
-    pub fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
-    }
-
-    pub fn unsafe_expr<F>(&mut self, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block("unsafe", cb);
-    }
-
-    pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("impl {}", name.as_ref()), cb);
-    }
-
-    pub fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, tr: S1, ty: S2, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
-    }
-
-    pub fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        let args_str = if args.is_empty() {
-            "".to_owned()
-        } else {
-            format!("<{}>", args.join(", "))
-        };
-        self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
-    }
-
-    pub fn unsafe_impl(&mut self, what: &str, for_what: &str) {
-        self.write_line(&format!("unsafe impl {} for {} {{}}", what, for_what));
-    }
-
-    pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
-    }
-
-    pub fn def_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("struct {}", name.as_ref()), cb);
-    }
-
-    pub fn pub_enum<F>(&mut self, name: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("pub enum {}", name), cb);
-    }
-
-    pub fn pub_trait<F>(&mut self, name: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("pub trait {}", name), cb);
-    }
-
-    pub fn pub_trait_extend<F>(&mut self, name: &str, extend: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("pub trait {} : {}", name, extend), cb);
-    }
-
-    pub fn field_entry(&mut self, name: &str, value: &str) {
-        self.write_line(&format!("{}: {},", name, value));
-    }
-
-    pub fn field_decl(&mut self, name: &str, field_type: &str) {
-        self.write_line(&format!("{}: {},", name, field_type));
-    }
-
-    pub fn pub_field_decl(&mut self, name: &str, field_type: &str) {
-        self.write_line(&format!("pub {}: {},", name, field_type));
-    }
-
-    pub fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
-        match vis {
-            Visibility::Public => self.pub_field_decl(name, field_type),
-            Visibility::Default => self.field_decl(name, field_type),
-        }
-    }
-
-    pub fn derive(&mut self, derive: &[&str]) {
-        let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
-        self.write_line(&format!("#[derive({})]", v.join(",")));
-    }
-
-    pub fn allow(&mut self, what: &[&str]) {
-        let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
-        self.write_line(&format!("#[allow({})]", v.join(",")));
-    }
-
-    pub fn comment(&mut self, comment: &str) {
-        if comment.is_empty() {
-            self.write_line("//");
-        } else {
-            self.write_line(&format!("// {}", comment));
-        }
-    }
-
-    pub fn fn_def(&mut self, sig: &str) {
-        self.write_line(&format!("fn {};", sig));
-    }
-
-    pub fn fn_block<F>(&mut self, public: bool, sig: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        if public {
-            self.expr_block(&format!("pub fn {}", sig), cb);
-        } else {
-            self.expr_block(&format!("fn {}", sig), cb);
-        }
-    }
-
-    pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.fn_block(true, sig, cb);
-    }
-
-    pub fn def_fn<F>(&mut self, sig: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.fn_block(false, sig, cb);
-    }
-
-    pub fn def_mod<F>(&mut self, name: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("mod {}", name), cb)
-    }
-
-    pub fn pub_mod<F>(&mut self, name: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("pub mod {}", name), cb)
-    }
-
-    pub fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("while {}", cond.as_ref()), cb);
-    }
-
-    // if ... { ... }
-    pub fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("if {}", cond.as_ref()), cb);
-    }
-
-    // if ... {} else { ... }
-    pub fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.write_line(&format!("if {} {{", cond.as_ref()));
-        self.write_line("} else {");
-        self.indented(cb);
-        self.write_line("}");
-    }
-
-    // if let ... = ... { ... }
-    pub fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.if_stmt(&format!("let {} = {}", decl, expr), cb);
-    }
-
-    // if let ... = ... { } else { ... }
-    pub fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
-    }
-
-    pub fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
-    }
-
-    pub fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.stmt_block(&format!("match {}", value.as_ref()), cb);
-    }
-
-    pub fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.expr_block(&format!("match {}", value.as_ref()), cb);
-    }
-
-    pub fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
-    }
-
-    pub fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
-        self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
-    }
-}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
new file mode 100644
index 0000000..3b8f629
--- /dev/null
+++ b/src/codegen/mod.rs
@@ -0,0 +1,272 @@
+use std::env;
+use std::ffi::OsString;
+use std::fs;
+use std::path::Component;
+use std::path::Path;
+use std::path::PathBuf;
+use std::process;
+
+use anyhow::Context;
+use protobuf_parse::Parser;
+
+use crate::customize::CustomizeCallback;
+use crate::customize::CustomizeCallbackHolder;
+use crate::gen_and_write::gen_and_write;
+use crate::Customize;
+
+#[derive(Debug)]
+enum WhichParser {
+    Pure,
+    Protoc,
+}
+
+impl Default for WhichParser {
+    fn default() -> WhichParser {
+        WhichParser::Pure
+    }
+}
+
+#[derive(Debug, thiserror::Error)]
+enum CodegenError {
+    #[error("out_dir is not specified")]
+    OutDirNotSpecified,
+}
+
+/// Entry point for `.proto` to `.rs` code generation.
+///
+/// This is similar to `protoc --rust_out...`.
+#[derive(Debug, Default)]
+pub struct Codegen {
+    /// What parser to use to parse `.proto` files.
+    which_parser: Option<WhichParser>,
+    /// Create out directory.
+    create_out_dir: bool,
+    /// --lang_out= param
+    out_dir: Option<PathBuf>,
+    /// -I args
+    includes: Vec<PathBuf>,
+    /// List of .proto files to compile
+    inputs: Vec<PathBuf>,
+    /// Customize code generation
+    customize: Customize,
+    /// Customize code generation
+    customize_callback: CustomizeCallbackHolder,
+    /// Protoc command path
+    protoc: Option<PathBuf>,
+    /// Extra `protoc` args
+    protoc_extra_args: Vec<OsString>,
+    /// Capture stderr when running `protoc`.
+    capture_stderr: bool,
+}
+
+impl Codegen {
+    /// Create new codegen object.
+    ///
+    /// Uses `protoc` from `$PATH` by default.
+    ///
+    /// Can be switched to pure rust parser using [`pure`](Self::pure) function.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Switch to pure Rust parser of `.proto` files.
+    pub fn pure(&mut self) -> &mut Self {
+        self.which_parser = Some(WhichParser::Pure);
+        self
+    }
+
+    /// Switch to `protoc` parser of `.proto` files.
+    pub fn protoc(&mut self) -> &mut Self {
+        self.which_parser = Some(WhichParser::Protoc);
+        self
+    }
+
+    /// Output directory for generated code.
+    ///
+    /// When invoking from `build.rs`, consider using
+    /// [`cargo_out_dir`](Self::cargo_out_dir) instead.
+    pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
+        self.out_dir = Some(out_dir.as_ref().to_owned());
+        self
+    }
+
+    /// Set output directory relative to Cargo output dir.
+    ///
+    /// With this option, output directory is erased and recreated during invocation.
+    pub fn cargo_out_dir(&mut self, rel: &str) -> &mut Self {
+        let rel = Path::new(rel);
+        let mut not_empty = false;
+        for comp in rel.components() {
+            match comp {
+                Component::ParentDir => {
+                    panic!("parent path in components of rel path: `{}`", rel.display());
+                }
+                Component::CurDir => {
+                    continue;
+                }
+                Component::Normal(..) => {}
+                Component::RootDir | Component::Prefix(..) => {
+                    panic!("root dir in components of rel path: `{}`", rel.display());
+                }
+            }
+            not_empty = true;
+        }
+
+        if !not_empty {
+            panic!("empty rel path: `{}`", rel.display());
+        }
+
+        let cargo_out_dir = env::var("OUT_DIR").expect("OUT_DIR env var not set");
+        let mut path = PathBuf::from(cargo_out_dir);
+        path.push(rel);
+        self.create_out_dir = true;
+        self.out_dir(path)
+    }
+
+    /// Add an include directory.
+    pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
+        self.includes.push(include.as_ref().to_owned());
+        self
+    }
+
+    /// Add include directories.
+    pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
+        for include in includes {
+            self.include(include);
+        }
+        self
+    }
+
+    /// Append a `.proto` file path to compile
+    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
+        self.inputs.push(input.as_ref().to_owned());
+        self
+    }
+
+    /// Append multiple `.proto` file paths to compile
+    pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
+        for input in inputs {
+            self.input(input);
+        }
+        self
+    }
+
+    /// Specify `protoc` command path to be used when invoking code generation.
+    ///
+    /// # Examples
+    ///
+    /// ```no_run
+    /// # mod protoc_bin_vendored {
+    /// #   pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
+    /// #       unimplemented!()
+    /// #   }
+    /// # }
+    ///
+    /// use protobuf_codegen::Codegen;
+    ///
+    /// Codegen::new()
+    ///     .protoc()
+    ///     .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+    ///     // ...
+    ///     .run()
+    ///     .unwrap();
+    /// ```
+    ///
+    /// This option is ignored when pure Rust parser is used.
+    pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self {
+        self.protoc = Some(protoc.to_owned());
+        self
+    }
+
+    /// Capture stderr to error when running `protoc`.
+    pub fn capture_stderr(&mut self) -> &mut Self {
+        self.capture_stderr = true;
+        self
+    }
+
+    /// Extra command line flags for `protoc` invocation.
+    ///
+    /// For example, `--experimental_allow_proto3_optional` option.
+    ///
+    /// This option is ignored when pure Rust parser is used.
+    pub fn protoc_extra_arg(&mut self, arg: impl Into<OsString>) -> &mut Self {
+        self.protoc_extra_args.push(arg.into());
+        self
+    }
+
+    /// Set options to customize code generation
+    pub fn customize(&mut self, customize: Customize) -> &mut Self {
+        self.customize.update_with(&customize);
+        self
+    }
+
+    /// Callback for dynamic per-element customization.
+    pub fn customize_callback(&mut self, callback: impl CustomizeCallback) -> &mut Self {
+        self.customize_callback = CustomizeCallbackHolder::new(callback);
+        self
+    }
+
+    /// Invoke the code generation.
+    ///
+    /// This is roughly equivalent to `protoc --rust_out=...` but
+    /// without requiring `protoc-gen-rust` command in `$PATH`.
+    ///
+    /// This function uses pure Rust parser or `protoc` parser depending on
+    /// how this object was configured.
+    pub fn run(&self) -> anyhow::Result<()> {
+        let out_dir = match &self.out_dir {
+            Some(out_dir) => out_dir,
+            None => return Err(CodegenError::OutDirNotSpecified.into()),
+        };
+
+        if self.create_out_dir {
+            if out_dir.exists() {
+                fs::remove_dir_all(&out_dir)?;
+            }
+            fs::create_dir(&out_dir)?;
+        }
+
+        let mut parser = Parser::new();
+        parser.protoc();
+        if let Some(protoc) = &self.protoc {
+            parser.protoc_path(protoc);
+        }
+        match &self.which_parser {
+            Some(WhichParser::Protoc) => {
+                parser.protoc();
+            }
+            Some(WhichParser::Pure) => {
+                parser.pure();
+            }
+            None => {}
+        }
+
+        parser.inputs(&self.inputs);
+        parser.includes(&self.includes);
+
+        if self.capture_stderr {
+            parser.capture_stderr();
+        }
+
+        let parsed_and_typechecked = parser
+            .parse_and_typecheck()
+            .context("parse and typecheck")?;
+
+        gen_and_write(
+            &parsed_and_typechecked.file_descriptors,
+            &parsed_and_typechecked.parser,
+            &parsed_and_typechecked.relative_paths,
+            &out_dir,
+            &self.customize,
+            &*self.customize_callback,
+        )
+    }
+
+    /// Similar to `run`, but prints the message to stderr and exits the process on error.
+    pub fn run_from_script(&self) {
+        if let Err(e) = self.run() {
+            eprintln!("codegen failed: {:?}", e);
+            process::exit(1);
+        }
+    }
+}
diff --git a/src/compiler_plugin.rs b/src/compiler_plugin.rs
new file mode 100644
index 0000000..b7ff6ea
--- /dev/null
+++ b/src/compiler_plugin.rs
@@ -0,0 +1,48 @@
+use std::io::stdin;
+use std::io::stdout;
+use std::str;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::plugin::*;
+use protobuf::Message;
+use protobuf_parse::ProtoPathBuf;
+
+pub struct GenRequest<'a> {
+    pub file_descriptors: &'a [FileDescriptorProto],
+    pub files_to_generate: &'a [ProtoPathBuf],
+    pub parameter: &'a str,
+}
+
+pub struct GenResult {
+    pub name: String,
+    pub content: Vec<u8>,
+}
+
+pub fn plugin_main<F>(gen: F) -> anyhow::Result<()>
+where
+    F: Fn(&GenRequest) -> anyhow::Result<Vec<GenResult>>,
+{
+    let req = CodeGeneratorRequest::parse_from_reader(&mut stdin()).unwrap();
+    let result = gen(&GenRequest {
+        file_descriptors: &req.proto_file,
+        files_to_generate: &req
+            .file_to_generate
+            .iter()
+            .map(|n| ProtoPathBuf::new(n.to_owned()))
+            .collect::<anyhow::Result<Vec<_>>>()?,
+        parameter: req.parameter(),
+    })?;
+    let mut resp = CodeGeneratorResponse::new();
+    resp.set_supported_features(code_generator_response::Feature::FEATURE_PROTO3_OPTIONAL as u64);
+    resp.file = result
+        .iter()
+        .map(|file| {
+            let mut r = code_generator_response::File::new();
+            r.set_name(file.name.to_string());
+            r.set_content(str::from_utf8(file.content.as_ref()).unwrap().to_string());
+            r
+        })
+        .collect();
+    resp.write_to_writer(&mut stdout()).unwrap();
+    Ok(())
+}
diff --git a/src/customize.rs b/src/customize.rs
deleted file mode 100644
index b3415ab..0000000
--- a/src/customize.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use protobuf::descriptor::EnumOptions;
-use protobuf::descriptor::FieldOptions;
-use protobuf::descriptor::FileOptions;
-use protobuf::descriptor::MessageOptions;
-use protobuf::rustproto;
-
-/// Specifies style of generated code.
-#[derive(Default, Debug, Clone)]
-pub struct Customize {
-    /// Make oneof enum public.
-    pub expose_oneof: Option<bool>,
-    /// When true all fields are public, and accessors are not generated
-    pub expose_fields: Option<bool>,
-    /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
-    pub generate_accessors: Option<bool>,
-    /// Use `bytes::Bytes` for `bytes` fields
-    pub carllerche_bytes_for_bytes: Option<bool>,
-    /// Use `bytes::Bytes` for `string` fields
-    pub carllerche_bytes_for_string: Option<bool>,
-    /// Implement serde_derive for messages
-    pub serde_derive: Option<bool>,
-    /// When `serde_derive` is set, serde annotations will be guarded with `#[cfg(cfg, ...)]`.
-    pub serde_derive_cfg: Option<String>,
-    /// When `serde_derive` is set, use attribute rename_all
-    pub serde_rename_all: Option<String>,
-    /// Enable lite runtime
-    pub lite_runtime: Option<bool>,
-    /// Generate `mod.rs` in the output directory.
-    ///
-    /// This option allows inclusion of generated files from cargo output directory.
-    ///
-    /// This option will likely be on by default in rust-protobuf version 3.
-    pub gen_mod_rs: Option<bool>,
-    /// Used internally to generate protos bundled in protobuf crate
-    /// like `descriptor.proto`
-    pub inside_protobuf: Option<bool>,
-
-    // When adding more options please keep in sync with `parse_from_parameter` below.
-    /// Make sure `Customize` is always used with `..Default::default()`
-    /// for future compatibility.
-    pub _future_options: (),
-}
-
-#[derive(Debug)]
-pub enum CustomizeParseParameterError {
-    EqNotFound,
-    CannotParseBool,
-    UnknownOptionName(String),
-}
-
-pub type CustomizeParseParameterResult<T> = Result<T, CustomizeParseParameterError>;
-
-impl Customize {
-    /// Update fields of self with fields defined in other customize
-    pub fn update_with(&mut self, that: &Customize) {
-        if let Some(v) = that.expose_oneof {
-            self.expose_oneof = Some(v);
-        }
-        if let Some(v) = that.expose_fields {
-            self.expose_fields = Some(v);
-        }
-        if let Some(v) = that.generate_accessors {
-            self.generate_accessors = Some(v);
-        }
-        if let Some(v) = that.carllerche_bytes_for_bytes {
-            self.carllerche_bytes_for_bytes = Some(v);
-        }
-        if let Some(v) = that.carllerche_bytes_for_string {
-            self.carllerche_bytes_for_string = Some(v);
-        }
-        if let Some(v) = that.serde_derive {
-            self.serde_derive = Some(v);
-        }
-        if let Some(ref v) = that.serde_derive_cfg {
-            self.serde_derive_cfg = Some(v.clone());
-        }
-        if let Some(ref v) = that.serde_rename_all {
-            self.serde_rename_all = Some(v.clone());
-        }
-        if let Some(v) = that.lite_runtime {
-            self.lite_runtime = Some(v);
-        }
-        if let Some(v) = that.gen_mod_rs {
-            self.gen_mod_rs = Some(v);
-        }
-        if let Some(v) = that.inside_protobuf {
-            self.inside_protobuf = Some(v);
-        }
-    }
-
-    /// Update unset fields of self with fields from other customize
-    pub fn set_defaults_from(&mut self, other: &Customize) {
-        let mut tmp = other.clone();
-        tmp.update_with(self);
-        *self = tmp;
-    }
-
-    /// Parse customize options from a string passed via protoc flag.
-    pub fn parse_from_parameter(parameter: &str) -> CustomizeParseParameterResult<Customize> {
-        fn parse_bool(v: &str) -> CustomizeParseParameterResult<bool> {
-            v.parse()
-                .map_err(|_| CustomizeParseParameterError::CannotParseBool)
-        }
-
-        let mut r = Customize::default();
-        for nv in parameter.split_whitespace() {
-            let eq = match nv.find('=') {
-                Some(eq) => eq,
-                None => return Err(CustomizeParseParameterError::EqNotFound),
-            };
-
-            let n = &nv[..eq];
-            let v = &nv[eq + 1..];
-
-            if n == "expose_oneof" {
-                r.expose_oneof = Some(parse_bool(v)?);
-            } else if n == "expose_fields" {
-                r.expose_fields = Some(parse_bool(v)?);
-            } else if n == "generate_accessors" {
-                r.generate_accessors = Some(parse_bool(v)?);
-            } else if n == "carllerche_bytes_for_bytes" {
-                r.carllerche_bytes_for_bytes = Some(parse_bool(v)?);
-            } else if n == "carllerche_bytes_for_string" {
-                r.carllerche_bytes_for_string = Some(parse_bool(v)?);
-            } else if n == "serde_derive" {
-                r.serde_derive = Some(parse_bool(v)?);
-            } else if n == "serde_derive_cfg" {
-                r.serde_derive_cfg = Some(v.to_owned());
-            } else if n == "serde_rename_all" {
-                r.serde_rename_all = Some(v.to_owned());
-            } else if n == "lite_runtime" {
-                r.lite_runtime = Some(parse_bool(v)?);
-            } else if n == "gen_mod_rs" {
-                r.gen_mod_rs = Some(parse_bool(v)?);
-            } else if n == "inside_protobuf" {
-                r.inside_protobuf = Some(parse_bool(v)?);
-            } else {
-                return Err(CustomizeParseParameterError::UnknownOptionName(
-                    n.to_owned(),
-                ));
-            }
-        }
-        Ok(r)
-    }
-}
-
-pub fn customize_from_rustproto_for_message(source: &MessageOptions) -> Customize {
-    let expose_oneof = rustproto::exts::expose_oneof.get(source);
-    let expose_fields = rustproto::exts::expose_fields.get(source);
-    let generate_accessors = rustproto::exts::generate_accessors.get(source);
-    let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes.get(source);
-    let carllerche_bytes_for_string = rustproto::exts::carllerche_bytes_for_string.get(source);
-    let serde_derive = rustproto::exts::serde_derive.get(source);
-    let serde_derive_cfg = rustproto::exts::serde_derive_cfg.get(source);
-    let lite_runtime = None;
-    let gen_mod_rs = None;
-    let inside_protobuf = None;
-    let serde_rename_all = None;
-    Customize {
-        expose_oneof,
-        expose_fields,
-        generate_accessors,
-        carllerche_bytes_for_bytes,
-        carllerche_bytes_for_string,
-        serde_derive,
-        serde_derive_cfg,
-        serde_rename_all,
-        lite_runtime,
-        gen_mod_rs,
-        inside_protobuf,
-        _future_options: (),
-    }
-}
-
-pub fn customize_from_rustproto_for_enum(source: &EnumOptions) -> Customize {
-    let serde_rename_all = rustproto::exts::serde_rename_all.get(source);
-    let mut r = Customize::default();
-    r.serde_rename_all = serde_rename_all;
-    return r;
-}
-
-pub fn customize_from_rustproto_for_field(source: &FieldOptions) -> Customize {
-    let expose_oneof = None;
-    let expose_fields = rustproto::exts::expose_fields_field.get(source);
-    let generate_accessors = rustproto::exts::generate_accessors_field.get(source);
-    let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes_field.get(source);
-    let carllerche_bytes_for_string =
-        rustproto::exts::carllerche_bytes_for_string_field.get(source);
-    let serde_rename_all = None;
-    let serde_derive = None;
-    let serde_derive_cfg = None;
-    let lite_runtime = None;
-    let gen_mod_rs = None;
-    let inside_protobuf = None;
-    Customize {
-        expose_oneof,
-        expose_fields,
-        generate_accessors,
-        carllerche_bytes_for_bytes,
-        carllerche_bytes_for_string,
-        serde_derive,
-        serde_derive_cfg,
-        serde_rename_all,
-        lite_runtime,
-        gen_mod_rs,
-        inside_protobuf,
-        _future_options: (),
-    }
-}
-
-pub fn customize_from_rustproto_for_file(source: &FileOptions) -> Customize {
-    let expose_oneof = rustproto::exts::expose_oneof_all.get(source);
-    let expose_fields = rustproto::exts::expose_fields_all.get(source);
-    let generate_accessors = rustproto::exts::generate_accessors_all.get(source);
-    let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes_all.get(source);
-    let carllerche_bytes_for_string = rustproto::exts::carllerche_bytes_for_string_all.get(source);
-    let serde_derive = rustproto::exts::serde_derive_all.get(source);
-    let serde_derive_cfg = rustproto::exts::serde_derive_cfg_all.get(source);
-    let lite_runtime = rustproto::exts::lite_runtime_all.get(source);
-    let gen_mod_rs = None;
-    let inside_protobuf = None;
-    let serde_rename_all = None;
-    Customize {
-        expose_oneof,
-        expose_fields,
-        generate_accessors,
-        carllerche_bytes_for_bytes,
-        carllerche_bytes_for_string,
-        serde_derive,
-        serde_derive_cfg,
-        serde_rename_all,
-        lite_runtime,
-        inside_protobuf,
-        gen_mod_rs,
-        _future_options: (),
-    }
-}
diff --git a/src/customize/ctx.rs b/src/customize/ctx.rs
new file mode 100644
index 0000000..185fc31
--- /dev/null
+++ b/src/customize/ctx.rs
@@ -0,0 +1,92 @@
+use std::fmt;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+use crate::customize::CustomizeCallback;
+use crate::Customize;
+
+#[derive(Clone)]
+pub(crate) struct CustomizeElemCtx<'a> {
+    pub(crate) for_elem: Customize,
+    pub(crate) for_children: Customize,
+    pub(crate) callback: &'a dyn CustomizeCallback,
+}
+
+impl<'a> fmt::Debug for CustomizeElemCtx<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("CustomizeElemCtx")
+            .field("for_elem", &self.for_elem)
+            .field("for_children", &self.for_children)
+            .finish_non_exhaustive()
+    }
+}
+
+impl<'a> CustomizeElemCtx<'a> {
+    pub(crate) fn child(
+        &self,
+        elem_from_rustproto: &Customize,
+        elem_descriptor: &impl DescriptorForCustomize,
+    ) -> CustomizeElemCtx<'a> {
+        let mut for_elem = self.for_children.clone();
+        for_elem.update_with(elem_from_rustproto);
+
+        let for_children = for_elem.clone();
+
+        for_elem.update_with(&elem_descriptor.customize(self.callback));
+
+        CustomizeElemCtx {
+            for_elem,
+            for_children,
+            callback: self.callback,
+        }
+    }
+}
+
+pub(crate) trait DescriptorForCustomize {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize;
+}
+
+impl DescriptorForCustomize for MessageDescriptor {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.message(self)
+    }
+}
+
+impl DescriptorForCustomize for FieldDescriptor {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.field(self)
+    }
+}
+
+impl DescriptorForCustomize for EnumDescriptor {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.enumeration(self)
+    }
+}
+
+impl DescriptorForCustomize for OneofDescriptor {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.oneof(self)
+    }
+}
+
+impl DescriptorForCustomize for FileDescriptor {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.file(self)
+    }
+}
+
+pub(crate) struct SpecialFieldPseudoDescriptor<'a> {
+    pub(crate) message: &'a MessageDescriptor,
+    pub(crate) field: &'a str,
+}
+
+impl<'a> DescriptorForCustomize for SpecialFieldPseudoDescriptor<'a> {
+    fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+        callback.special_field(self.message, self.field)
+    }
+}
diff --git a/src/customize/mod.rs b/src/customize/mod.rs
new file mode 100644
index 0000000..826bd3f
--- /dev/null
+++ b/src/customize/mod.rs
@@ -0,0 +1,251 @@
+pub(crate) mod ctx;
+pub(crate) mod rustproto_proto;
+
+use std::fmt;
+use std::ops::Deref;
+use std::rc::Rc;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+/// Dynamic callback to customize code generation.
+pub trait CustomizeCallback: 'static {
+    fn file(&self, file: &FileDescriptor) -> Customize {
+        let _ = file;
+        Customize::default()
+    }
+
+    fn message(&self, message: &MessageDescriptor) -> Customize {
+        let _ = message;
+        Customize::default()
+    }
+
+    fn field(&self, field: &FieldDescriptor) -> Customize {
+        let _ = field;
+        Customize::default()
+    }
+
+    fn special_field(&self, message: &MessageDescriptor, field: &str) -> Customize {
+        let _ = (message, field);
+        Customize::default()
+    }
+
+    fn enumeration(&self, enum_type: &EnumDescriptor) -> Customize {
+        let _ = enum_type;
+        Customize::default()
+    }
+
+    fn oneof(&self, oneof: &OneofDescriptor) -> Customize {
+        let _ = oneof;
+        Customize::default()
+    }
+}
+
+pub(crate) struct CustomizeCallbackDefault;
+impl CustomizeCallback for CustomizeCallbackDefault {}
+
+#[derive(Clone)]
+pub(crate) struct CustomizeCallbackHolder(pub(crate) Rc<dyn CustomizeCallback>);
+
+impl CustomizeCallbackHolder {
+    pub(crate) fn new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder {
+        CustomizeCallbackHolder(Rc::new(callback))
+    }
+}
+
+impl Deref for CustomizeCallbackHolder {
+    type Target = dyn CustomizeCallback;
+
+    fn deref(&self) -> &Self::Target {
+        &*self.0
+    }
+}
+
+impl Default for CustomizeCallbackHolder {
+    fn default() -> Self {
+        CustomizeCallbackHolder(Rc::new(CustomizeCallbackDefault))
+    }
+}
+
+impl PartialEq for CustomizeCallbackHolder {
+    fn eq(&self, other: &Self) -> bool {
+        Rc::ptr_eq(&self.0, &other.0)
+    }
+}
+
+impl fmt::Debug for CustomizeCallbackHolder {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.debug_struct("CustomizeCallbackWrapper")
+            .finish_non_exhaustive()
+    }
+}
+
+/// Specifies style of generated code.
+/// Generated files can be customized using this proto
+/// or using `rustproto.proto` options.
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct Customize {
+    /// Code to insert before the element in the generated file.
+    pub(crate) before: Option<String>,
+    /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
+    pub(crate) generate_accessors: Option<bool>,
+    /// When false, `get_` is not generated even if `syntax = "proto2"`
+    pub(crate) generate_getter: Option<bool>,
+    /// Use `bytes::Bytes` for `bytes` fields
+    pub(crate) tokio_bytes: Option<bool>,
+    /// Use `bytes::Bytes` for `string` fields
+    pub(crate) tokio_bytes_for_string: Option<bool>,
+    /// Enable lite runtime.
+    pub(crate) lite_runtime: Option<bool>,
+    /// Generate `mod.rs` in the output directory.
+    ///
+    /// This option allows inclusion of generated files from cargo output directory.
+    ///
+    /// This option will likely be on by default in rust-protobuf version 3.
+    pub(crate) gen_mod_rs: Option<bool>,
+    /// Used internally to generate protos bundled in protobuf crate
+    /// like `descriptor.proto`
+    pub(crate) inside_protobuf: Option<bool>,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) enum CustomizeParseParameterError {
+    #[error("Cannot parse bool option value: {:?}", .0)]
+    CannotParseBool(String),
+    #[error("Unknown option name: {:?}", .0)]
+    UnknownOptionName(String),
+}
+
+impl Customize {
+    /// Insert code before the element in the generated file
+    /// (e. g. serde annotations, see
+    /// [example here](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)).
+    pub fn before(mut self, before: &str) -> Self {
+        self.before = Some(before.to_owned());
+        self
+    }
+
+    pub fn generate_accessors(mut self, generate_accessors: bool) -> Self {
+        self.generate_accessors = Some(generate_accessors);
+        self
+    }
+
+    pub fn generate_getter(mut self, generate_getter: bool) -> Self {
+        self.generate_getter = Some(generate_getter);
+        self
+    }
+
+    pub fn tokio_bytes(mut self, tokio_bytes: bool) -> Self {
+        self.tokio_bytes = Some(tokio_bytes);
+        self
+    }
+
+    pub fn tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self {
+        self.tokio_bytes_for_string = Some(tokio_bytes_for_string);
+        self
+    }
+
+    /// Generate code for "lite runtime". Generated code contains no code for reflection.
+    /// So the generated code (and more importantly, generated binary size) is smaller,
+    /// but reflection, text format, JSON serialization won't work.
+    ///
+    /// Note when using `protoc` plugin `protoc-gen-rust`, the option name is just `lite`.
+    pub fn lite_runtime(mut self, lite_runtime: bool) -> Self {
+        self.lite_runtime = Some(lite_runtime);
+        self
+    }
+
+    /// Generate `mod.rs` with all the generated modules.
+    /// This option is on by default in rust-protobuf version 3.
+    pub fn gen_mod_rs(mut self, gen_mod_rs: bool) -> Self {
+        self.gen_mod_rs = Some(gen_mod_rs);
+        self
+    }
+
+    /// Generate code bundled in protobuf crate. Regular users don't need this option.
+    pub fn inside_protobuf(mut self, inside_protobuf: bool) -> Self {
+        self.inside_protobuf = Some(inside_protobuf);
+        self
+    }
+
+    /// Update fields of self with fields defined in other customize
+    pub fn update_with(&mut self, that: &Customize) {
+        if let Some(v) = &that.before {
+            self.before = Some(v.clone());
+        }
+        if let Some(v) = that.generate_accessors {
+            self.generate_accessors = Some(v);
+        }
+        if let Some(v) = that.generate_getter {
+            self.generate_getter = Some(v);
+        }
+        if let Some(v) = that.tokio_bytes {
+            self.tokio_bytes = Some(v);
+        }
+        if let Some(v) = that.tokio_bytes_for_string {
+            self.tokio_bytes_for_string = Some(v);
+        }
+        if let Some(v) = that.lite_runtime {
+            self.lite_runtime = Some(v);
+        }
+        if let Some(v) = that.gen_mod_rs {
+            self.gen_mod_rs = Some(v);
+        }
+        if let Some(v) = that.inside_protobuf {
+            self.inside_protobuf = Some(v);
+        }
+    }
+
+    /// Update unset fields of self with fields from other customize
+    pub fn set_defaults_from(&mut self, other: &Customize) {
+        let mut tmp = other.clone();
+        tmp.update_with(self);
+        *self = tmp;
+    }
+
+    /// Parse customize options from a string passed via protoc flag.
+    pub fn parse_from_parameter(parameter: &str) -> anyhow::Result<Customize> {
+        fn parse_bool(v: &str) -> anyhow::Result<bool> {
+            v.parse()
+                .map_err(|_| CustomizeParseParameterError::CannotParseBool(v.to_owned()).into())
+        }
+
+        let mut r = Customize::default();
+        for nv in parameter.split_whitespace() {
+            let (n, v) = match nv.find('=') {
+                Some(eq) => {
+                    let n = &nv[..eq];
+                    let v = &nv[eq + 1..];
+                    (n, v)
+                }
+                None => (nv, "true"),
+            };
+
+            if n == "generate_accessors" {
+                r.generate_accessors = Some(parse_bool(v)?);
+            } else if n == "generate_getter" {
+                r.generate_getter = Some(parse_bool(v)?);
+            } else if n == "tokio_bytes" {
+                r.tokio_bytes = Some(parse_bool(v)?);
+            } else if n == "tokio_bytes_for_string" {
+                r.tokio_bytes_for_string = Some(parse_bool(v)?);
+            } else if n == "lite_runtime" {
+                r.lite_runtime = Some(parse_bool(v)?);
+            } else if n == "gen_mod_rs" {
+                r.gen_mod_rs = Some(parse_bool(v)?);
+            } else if n == "inside_protobuf" {
+                r.inside_protobuf = Some(parse_bool(v)?);
+            } else if n == "lite" {
+                // Support Java and C++ protoc plugin syntax:
+                // https://github.com/protocolbuffers/protobuf/issues/6489
+                r.lite_runtime = Some(parse_bool(v)?);
+            } else {
+                return Err(CustomizeParseParameterError::UnknownOptionName(n.to_owned()).into());
+            }
+        }
+        Ok(r)
+    }
+}
diff --git a/src/customize/rustproto_proto.rs b/src/customize/rustproto_proto.rs
new file mode 100644
index 0000000..3d9a77b
--- /dev/null
+++ b/src/customize/rustproto_proto.rs
@@ -0,0 +1,74 @@
+use protobuf::descriptor::EnumOptions;
+use protobuf::descriptor::FieldOptions;
+use protobuf::descriptor::FileOptions;
+use protobuf::descriptor::MessageOptions;
+use protobuf::rustproto;
+
+use crate::Customize;
+
+pub(crate) fn customize_from_rustproto_for_message(source: &MessageOptions) -> Customize {
+    let before = None;
+    let generate_accessors = rustproto::exts::generate_accessors.get(source);
+    let generate_getter = rustproto::exts::generate_getter.get(source);
+    let tokio_bytes = rustproto::exts::tokio_bytes.get(source);
+    let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string.get(source);
+    let lite_runtime = None;
+    let gen_mod_rs = None;
+    let inside_protobuf = None;
+    Customize {
+        before,
+        generate_accessors,
+        generate_getter,
+        tokio_bytes,
+        tokio_bytes_for_string,
+        lite_runtime,
+        gen_mod_rs,
+        inside_protobuf,
+    }
+}
+
+pub(crate) fn customize_from_rustproto_for_enum(_source: &EnumOptions) -> Customize {
+    Customize::default()
+}
+
+pub(crate) fn customize_from_rustproto_for_field(source: &FieldOptions) -> Customize {
+    let before = None;
+    let generate_accessors = rustproto::exts::generate_accessors_field.get(source);
+    let generate_getter = rustproto::exts::generate_getter_field.get(source);
+    let tokio_bytes = rustproto::exts::tokio_bytes_field.get(source);
+    let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_field.get(source);
+    let lite_runtime = None;
+    let gen_mod_rs = None;
+    let inside_protobuf = None;
+    Customize {
+        before,
+        generate_accessors,
+        generate_getter,
+        tokio_bytes,
+        tokio_bytes_for_string,
+        lite_runtime,
+        gen_mod_rs,
+        inside_protobuf,
+    }
+}
+
+pub(crate) fn customize_from_rustproto_for_file(source: &FileOptions) -> Customize {
+    let before = None;
+    let generate_accessors = rustproto::exts::generate_accessors_all.get(source);
+    let generate_getter = rustproto::exts::generate_getter_all.get(source);
+    let tokio_bytes = rustproto::exts::tokio_bytes_all.get(source);
+    let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_all.get(source);
+    let lite_runtime = rustproto::exts::lite_runtime_all.get(source);
+    let gen_mod_rs = None;
+    let inside_protobuf = None;
+    Customize {
+        before,
+        generate_accessors,
+        generate_getter,
+        tokio_bytes,
+        tokio_bytes_for_string,
+        lite_runtime,
+        inside_protobuf,
+        gen_mod_rs,
+    }
+}
diff --git a/src/enums.rs b/src/enums.rs
deleted file mode 100644
index ec3444e..0000000
--- a/src/enums.rs
+++ /dev/null
@@ -1,377 +0,0 @@
-use std::collections::HashSet;
-
-use file_descriptor::file_descriptor_proto_expr;
-use inside::protobuf_crate_path;
-use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_types_values::type_name_to_rust_relative;
-use scope::EnumWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-use CodeWriter;
-
-use crate::customize::customize_from_rustproto_for_enum;
-use crate::Customize;
-
-#[derive(Clone)]
-pub struct EnumValueGen {
-    proto: EnumValueDescriptorProto,
-    enum_rust_name: String,
-    variant_rust_name: String,
-}
-
-impl EnumValueGen {
-    fn parse(
-        proto: &EnumValueDescriptorProto,
-        enum_rust_name: &str,
-        variant_rust_name: &str,
-    ) -> EnumValueGen {
-        EnumValueGen {
-            proto: proto.clone(),
-            enum_rust_name: enum_rust_name.to_string(),
-            variant_rust_name: variant_rust_name.to_string(),
-        }
-    }
-
-    // enum value
-    fn number(&self) -> i32 {
-        self.proto.get_number()
-    }
-
-    // name of enum variant in generated rust code
-    fn rust_name_inner(&self) -> String {
-        self.variant_rust_name.clone()
-    }
-
-    pub fn rust_name_outer(&self) -> String {
-        let mut r = String::new();
-        r.push_str(&self.enum_rust_name);
-        r.push_str("::");
-        r.push_str(&self.rust_name_inner());
-        r
-    }
-}
-
-pub(crate) struct EnumGen<'a> {
-    enum_with_scope: &'a EnumWithScope<'a>,
-    type_name: String,
-    lite_runtime: bool,
-    customize: Customize,
-}
-
-impl<'a> EnumGen<'a> {
-    pub fn new(
-        enum_with_scope: &'a EnumWithScope<'a>,
-        current_file: &FileDescriptorProto,
-        customize: &Customize,
-        root_scope: &RootScope,
-    ) -> EnumGen<'a> {
-        let rust_name = if enum_with_scope.get_scope().get_file_descriptor().get_name()
-            == current_file.get_name()
-        {
-            // field type is a message or enum declared in the same file
-            enum_with_scope.rust_name().to_string()
-        } else {
-            type_name_to_rust_relative(
-                &ProtobufAbsolutePath::from(enum_with_scope.name_absolute()),
-                current_file,
-                false,
-                root_scope,
-                customize,
-            )
-            .to_string()
-        };
-        let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
-            enum_with_scope
-                .get_scope()
-                .get_file_descriptor()
-                .get_options()
-                .get_optimize_for()
-                == FileOptions_OptimizeMode::LITE_RUNTIME
-        });
-
-        let mut customize = customize.clone();
-        customize.update_with(&customize_from_rustproto_for_enum(
-            enum_with_scope.en.options.as_ref().unwrap_or_default(),
-        ));
-
-        EnumGen {
-            enum_with_scope,
-            type_name: rust_name,
-            lite_runtime,
-            customize,
-        }
-    }
-
-    fn allow_alias(&self) -> bool {
-        self.enum_with_scope.en.get_options().get_allow_alias()
-    }
-
-    fn values_all(&self) -> Vec<EnumValueGen> {
-        let mut r = Vec::new();
-        for p in self.enum_with_scope.values() {
-            r.push(EnumValueGen::parse(
-                &p.proto,
-                &self.type_name,
-                p.rust_name().get(),
-            ));
-        }
-        r
-    }
-
-    pub fn values_unique(&self) -> Vec<EnumValueGen> {
-        let mut used = HashSet::new();
-        let mut r = Vec::new();
-        for p in self.enum_with_scope.values() {
-            // skipping non-unique enums
-            // TODO: should support it
-            if !used.insert(p.proto.get_number()) {
-                continue;
-            }
-            r.push(EnumValueGen::parse(
-                p.proto,
-                &self.type_name,
-                p.rust_name().get(),
-            ));
-        }
-        r
-    }
-
-    // find enum value by name
-    pub fn value_by_name(&'a self, name: &str) -> EnumValueGen {
-        let v = self.enum_with_scope.value_by_name(name);
-        EnumValueGen::parse(v.proto, &self.type_name, v.rust_name().get())
-    }
-
-    pub fn write(&self, w: &mut CodeWriter) {
-        self.write_struct(w);
-        if self.allow_alias() {
-            w.write_line("");
-            self.write_impl_eq(w);
-            w.write_line("");
-            self.write_impl_hash(w);
-        }
-        w.write_line("");
-        self.write_impl_enum(w);
-        w.write_line("");
-        self.write_impl_copy(w);
-        w.write_line("");
-        self.write_impl_default(w);
-        w.write_line("");
-        self.write_impl_value(w);
-    }
-
-    fn write_struct(&self, w: &mut CodeWriter) {
-        let mut derive = Vec::new();
-        derive.push("Clone");
-        if !self.allow_alias() {
-            derive.push("PartialEq");
-        }
-        derive.push("Eq");
-        derive.push("Debug");
-        if !self.allow_alias() {
-            derive.push("Hash");
-        } else {
-            w.comment("Note: you cannot use pattern matching for enums with allow_alias option");
-        }
-        w.derive(&derive);
-        serde::write_serde_attr(
-            w,
-            &self.customize,
-            "derive(::serde::Serialize, ::serde::Deserialize)",
-        );
-        if let Some(ref ren) = self.customize.serde_rename_all {
-            let attr = format!("serde(rename_all = \"{}\")", ren);
-            serde::write_serde_attr(w, &self.customize, &attr);
-        }
-        let ref type_name = self.type_name;
-        w.expr_block(&format!("pub enum {}", type_name), |w| {
-            for value in self.values_all() {
-                if self.allow_alias() {
-                    w.write_line(&format!(
-                        "{}, // {}",
-                        value.rust_name_inner(),
-                        value.number()
-                    ));
-                } else {
-                    w.write_line(&format!(
-                        "{} = {},",
-                        value.rust_name_inner(),
-                        value.number()
-                    ));
-                }
-            }
-        });
-    }
-
-    fn write_fn_value(&self, w: &mut CodeWriter) {
-        w.def_fn("value(&self) -> i32", |w| {
-            if self.allow_alias() {
-                w.match_expr("*self", |w| {
-                    for value in self.values_all() {
-                        w.case_expr(value.rust_name_outer(), format!("{}", value.number()));
-                    }
-                });
-            } else {
-                w.write_line("*self as i32")
-            }
-        });
-    }
-
-    fn write_impl_enum(&self, w: &mut CodeWriter) {
-        let ref type_name = self.type_name;
-        w.impl_for_block(
-            &format!("{}::ProtobufEnum", protobuf_crate_path(&self.customize)),
-            &format!("{}", type_name),
-            |w| {
-                self.write_fn_value(w);
-
-                w.write_line("");
-                let ref type_name = self.type_name;
-                w.def_fn(
-                    &format!(
-                        "from_i32(value: i32) -> ::std::option::Option<{}>",
-                        type_name
-                    ),
-                    |w| {
-                        w.match_expr("value", |w| {
-                            let values = self.values_unique();
-                            for value in values {
-                                w.write_line(&format!(
-                                    "{} => ::std::option::Option::Some({}),",
-                                    value.number(),
-                                    value.rust_name_outer()
-                                ));
-                            }
-                            w.write_line(&format!("_ => ::std::option::Option::None"));
-                        });
-                    },
-                );
-
-                w.write_line("");
-                w.def_fn(&format!("values() -> &'static [Self]"), |w| {
-                    w.write_line(&format!("static values: &'static [{}] = &[", type_name));
-                    w.indented(|w| {
-                        for value in self.values_all() {
-                            w.write_line(&format!("{},", value.rust_name_outer()));
-                        }
-                    });
-                    w.write_line("];");
-                    w.write_line("values");
-                });
-
-                if !self.lite_runtime {
-                    w.write_line("");
-                    w.def_fn(
-                        &format!(
-                            "enum_descriptor_static() -> &'static {}::reflect::EnumDescriptor",
-                            protobuf_crate_path(&self.customize)
-                        ),
-                        |w| {
-                            w.lazy_static_decl_get(
-                                "descriptor",
-                                &format!(
-                                    "{}::reflect::EnumDescriptor",
-                                    protobuf_crate_path(&self.customize)
-                                ),
-                                &self.customize,
-                                |w| {
-                                    let ref type_name = self.type_name;
-                                    w.write_line(&format!(
-                                    "{}::reflect::EnumDescriptor::new_pb_name::<{}>(\"{}\", {})",
-                                    protobuf_crate_path(&self.customize),
-                                    type_name,
-                                    self.enum_with_scope.name_to_package(),
-                                    file_descriptor_proto_expr(&self.enum_with_scope.scope)
-                                ));
-                                },
-                            );
-                        },
-                    );
-                }
-            },
-        );
-    }
-
-    fn write_impl_value(&self, w: &mut CodeWriter) {
-        w.impl_for_block(
-            &format!(
-                "{}::reflect::ProtobufValue",
-                protobuf_crate_path(&self.customize)
-            ),
-            &format!("{}", self.type_name),
-            |w| {
-                w.def_fn(
-                    &format!(
-                        "as_ref(&self) -> {}::reflect::ReflectValueRef",
-                        protobuf_crate_path(&self.customize)
-                    ),
-                    |w| {
-                        w.write_line(&format!(
-                        "{}::reflect::ReflectValueRef::Enum({}::ProtobufEnum::descriptor(self))",
-                        protobuf_crate_path(&self.customize),
-                        protobuf_crate_path(&self.customize)
-                    ))
-                    },
-                )
-            },
-        )
-    }
-
-    fn write_impl_copy(&self, w: &mut CodeWriter) {
-        w.impl_for_block("::std::marker::Copy", &self.type_name, |_w| {});
-    }
-
-    fn write_impl_eq(&self, w: &mut CodeWriter) {
-        assert!(self.allow_alias());
-        w.impl_for_block(
-            "::std::cmp::PartialEq",
-            &format!("{}", self.type_name),
-            |w| {
-                w.def_fn("eq(&self, other: &Self) -> bool", |w| {
-                    w.write_line(&format!(
-                        "{}::ProtobufEnum::value(self) == {}::ProtobufEnum::value(other)",
-                        protobuf_crate_path(&self.customize),
-                        protobuf_crate_path(&self.customize)
-                    ));
-                });
-            },
-        );
-    }
-
-    fn write_impl_hash(&self, w: &mut CodeWriter) {
-        assert!(self.allow_alias());
-        w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
-            w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
-                w.write_line(&format!(
-                    "state.write_i32({}::ProtobufEnum::value(self))",
-                    protobuf_crate_path(&self.customize)
-                ));
-            });
-        });
-    }
-
-    fn write_impl_default(&self, w: &mut CodeWriter) {
-        let first_value = &self.enum_with_scope.values()[0];
-        if first_value.proto.get_number() != 0 {
-            // This warning is emitted only for proto2
-            // (because in proto3 first enum variant number is always 0).
-            // `Default` implemented unconditionally to simplify certain
-            // generic operations, e. g. reading a map.
-            // Also, note that even in proto2 some operations fallback to
-            // first enum value, e. g. `get_xxx` for unset field,
-            // so this implementation is not completely unreasonable.
-            w.comment("Note, `Default` is implemented although default value is not 0");
-        }
-        w.impl_for_block("::std::default::Default", &self.type_name, |w| {
-            w.def_fn("default() -> Self", |w| {
-                w.write_line(&format!(
-                    "{}::{}",
-                    &self.type_name,
-                    &first_value.rust_name()
-                ))
-            });
-        });
-    }
-}
diff --git a/src/extensions.rs b/src/extensions.rs
deleted file mode 100644
index def5948..0000000
--- a/src/extensions.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use field::rust_field_name_for_protobuf_field_name;
-use inside::protobuf_crate_path;
-use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use scope::RootScope;
-use Customize;
-
-use super::code_writer::CodeWriter;
-use super::rust_types_values::*;
-
-struct ExtGen<'a> {
-    file: &'a FileDescriptorProto,
-    root_scope: &'a RootScope<'a>,
-    field: &'a FieldDescriptorProto,
-    customize: Customize,
-}
-
-impl<'a> ExtGen<'a> {
-    fn extendee_rust_name(&self) -> String {
-        type_name_to_rust_relative(
-            &ProtobufAbsolutePath::from(self.field.get_extendee()),
-            self.file,
-            true,
-            self.root_scope,
-            &self.customize,
-        )
-    }
-
-    fn repeated(&self) -> bool {
-        match self.field.get_label() {
-            FieldDescriptorProto_Label::LABEL_REPEATED => true,
-            FieldDescriptorProto_Label::LABEL_OPTIONAL => false,
-            FieldDescriptorProto_Label::LABEL_REQUIRED => {
-                panic!("required ext field: {}", self.field.get_name())
-            }
-        }
-    }
-
-    fn return_type_gen(&self) -> ProtobufTypeGen {
-        if self.field.has_type_name() {
-            let rust_name_relative = type_name_to_rust_relative(
-                &ProtobufAbsolutePath::from(self.field.get_type_name()),
-                self.file,
-                true,
-                self.root_scope,
-                &self.customize,
-            );
-            match self.field.get_field_type() {
-                FieldDescriptorProto_Type::TYPE_MESSAGE => {
-                    ProtobufTypeGen::Message(rust_name_relative)
-                }
-                FieldDescriptorProto_Type::TYPE_ENUM => ProtobufTypeGen::Enum(rust_name_relative),
-                t => panic!("unknown type: {:?}", t),
-            }
-        } else {
-            ProtobufTypeGen::Primitive(self.field.get_field_type(), PrimitiveTypeVariant::Default)
-        }
-    }
-
-    fn write(&self, w: &mut CodeWriter) {
-        let suffix = if self.repeated() {
-            "Repeated"
-        } else {
-            "Optional"
-        };
-        let field_type = format!(
-            "{}::ext::ExtField{}",
-            protobuf_crate_path(&self.customize),
-            suffix
-        );
-        w.pub_const(
-            rust_field_name_for_protobuf_field_name(self.field.get_name()).get(),
-            &format!(
-                "{}<{}, {}>",
-                field_type,
-                self.extendee_rust_name(),
-                self.return_type_gen().rust_type(&self.customize),
-            ),
-            &format!(
-                "{} {{ field_number: {}, phantom: ::std::marker::PhantomData }}",
-                field_type,
-                self.field.get_number()
-            ),
-        );
-    }
-}
-
-pub(crate) fn write_extensions(
-    file: &FileDescriptorProto,
-    root_scope: &RootScope,
-    w: &mut CodeWriter,
-    customize: &Customize,
-) {
-    if file.get_extension().is_empty() {
-        return;
-    }
-
-    w.write_line("");
-    w.write_line("/// Extension fields");
-    w.pub_mod("exts", |w| {
-        for field in file.get_extension() {
-            if field.get_field_type() == FieldDescriptorProto_Type::TYPE_GROUP {
-                continue;
-            }
-
-            w.write_line("");
-            ExtGen {
-                file: file,
-                root_scope: root_scope,
-                field: field,
-                customize: customize.clone(),
-            }
-            .write(w);
-        }
-    });
-}
diff --git a/src/field/mod.rs b/src/field/mod.rs
deleted file mode 100644
index e4be944..0000000
--- a/src/field/mod.rs
+++ /dev/null
@@ -1,2031 +0,0 @@
-use std::marker;
-
-use float;
-use inside::protobuf_crate_path;
-use message::RustTypeMessage;
-use oneof::OneofField;
-use protobuf::descriptor::*;
-use protobuf::rt;
-use protobuf::rust;
-use protobuf::text_format;
-use protobuf::wire_format;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use rust_name::RustIdentWithPath;
-use scope::FieldWithContext;
-use scope::MessageOrEnumWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use syntax::Syntax;
-
-use super::code_writer::CodeWriter;
-use super::customize::customize_from_rustproto_for_field;
-use super::customize::Customize;
-use super::enums::*;
-use super::rust_types_values::*;
-
-fn type_is_copy(field_type: FieldDescriptorProto_Type) -> bool {
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_MESSAGE
-        | FieldDescriptorProto_Type::TYPE_STRING
-        | FieldDescriptorProto_Type::TYPE_BYTES => false,
-        _ => true,
-    }
-}
-
-trait FieldDescriptorProtoTypeExt {
-    fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String;
-    fn is_s_varint(&self) -> bool;
-}
-
-impl FieldDescriptorProtoTypeExt for FieldDescriptorProto_Type {
-    fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String {
-        match primitive_type_variant {
-            PrimitiveTypeVariant::Default => format!("{}.read_{}()", is, protobuf_name(*self)),
-            PrimitiveTypeVariant::Carllerche => {
-                let protobuf_name = match self {
-                    &FieldDescriptorProto_Type::TYPE_STRING => "chars",
-                    _ => protobuf_name(*self),
-                };
-                format!("{}.read_carllerche_{}()", is, protobuf_name)
-            }
-        }
-    }
-
-    /// True if self is signed integer with zigzag encoding
-    fn is_s_varint(&self) -> bool {
-        match *self {
-            FieldDescriptorProto_Type::TYPE_SINT32 | FieldDescriptorProto_Type::TYPE_SINT64 => true,
-            _ => false,
-        }
-    }
-}
-
-fn field_type_wire_type(field_type: FieldDescriptorProto_Type) -> wire_format::WireType {
-    use protobuf::wire_format::*;
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_INT32 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_INT64 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_UINT32 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_UINT64 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_SINT32 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_SINT64 => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_BOOL => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_ENUM => WireTypeVarint,
-        FieldDescriptorProto_Type::TYPE_FIXED32 => WireTypeFixed32,
-        FieldDescriptorProto_Type::TYPE_FIXED64 => WireTypeFixed64,
-        FieldDescriptorProto_Type::TYPE_SFIXED32 => WireTypeFixed32,
-        FieldDescriptorProto_Type::TYPE_SFIXED64 => WireTypeFixed64,
-        FieldDescriptorProto_Type::TYPE_FLOAT => WireTypeFixed32,
-        FieldDescriptorProto_Type::TYPE_DOUBLE => WireTypeFixed64,
-        FieldDescriptorProto_Type::TYPE_STRING => WireTypeLengthDelimited,
-        FieldDescriptorProto_Type::TYPE_BYTES => WireTypeLengthDelimited,
-        FieldDescriptorProto_Type::TYPE_MESSAGE => WireTypeLengthDelimited,
-        FieldDescriptorProto_Type::TYPE_GROUP => WireTypeLengthDelimited, // not true
-    }
-}
-
-fn type_protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str {
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_INT32 => "int32",
-        FieldDescriptorProto_Type::TYPE_INT64 => "int64",
-        FieldDescriptorProto_Type::TYPE_UINT32 => "uint32",
-        FieldDescriptorProto_Type::TYPE_UINT64 => "uint64",
-        FieldDescriptorProto_Type::TYPE_SINT32 => "sint32",
-        FieldDescriptorProto_Type::TYPE_SINT64 => "sint64",
-        FieldDescriptorProto_Type::TYPE_BOOL => "bool",
-        FieldDescriptorProto_Type::TYPE_FIXED32 => "fixed32",
-        FieldDescriptorProto_Type::TYPE_FIXED64 => "fixed64",
-        FieldDescriptorProto_Type::TYPE_SFIXED32 => "sfixed32",
-        FieldDescriptorProto_Type::TYPE_SFIXED64 => "sfixed64",
-        FieldDescriptorProto_Type::TYPE_FLOAT => "float",
-        FieldDescriptorProto_Type::TYPE_DOUBLE => "double",
-        FieldDescriptorProto_Type::TYPE_STRING => "string",
-        FieldDescriptorProto_Type::TYPE_BYTES => "bytes",
-        FieldDescriptorProto_Type::TYPE_ENUM
-        | FieldDescriptorProto_Type::TYPE_MESSAGE
-        | FieldDescriptorProto_Type::TYPE_GROUP => panic!(),
-    }
-}
-
-fn field_type_protobuf_name<'a>(field: &'a FieldDescriptorProto) -> &'a str {
-    if field.has_type_name() {
-        field.get_type_name()
-    } else {
-        type_protobuf_name(field.get_field_type())
-    }
-}
-
-// size of value for type, None if variable
-fn field_type_size(field_type: FieldDescriptorProto_Type) -> Option<u32> {
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_BOOL => Some(1),
-        t if field_type_wire_type(t) == wire_format::WireTypeFixed32 => Some(4),
-        t if field_type_wire_type(t) == wire_format::WireTypeFixed64 => Some(8),
-        _ => None,
-    }
-}
-
-#[derive(Clone, PartialEq, Eq)]
-pub enum SingularFieldFlag {
-    // proto2 or proto3 message
-    WithFlag { required: bool },
-    // proto3
-    WithoutFlag,
-}
-
-impl SingularFieldFlag {
-    pub fn is_required(&self) -> bool {
-        match *self {
-            SingularFieldFlag::WithFlag { required, .. } => required,
-            SingularFieldFlag::WithoutFlag => false,
-        }
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct SingularField<'a> {
-    pub flag: SingularFieldFlag,
-    pub elem: FieldElem<'a>,
-}
-
-impl<'a> SingularField<'a> {
-    fn rust_storage_type(&self) -> RustType {
-        match self.flag {
-            SingularFieldFlag::WithFlag { .. } => match self.elem.proto_type() {
-                FieldDescriptorProto_Type::TYPE_MESSAGE => {
-                    RustType::SingularPtrField(Box::new(self.elem.rust_storage_type()))
-                }
-                FieldDescriptorProto_Type::TYPE_STRING | FieldDescriptorProto_Type::TYPE_BYTES
-                    if self.elem.primitive_type_variant() == PrimitiveTypeVariant::Default =>
-                {
-                    RustType::SingularField(Box::new(self.elem.rust_storage_type()))
-                }
-                _ => RustType::Option(Box::new(self.elem.rust_storage_type())),
-            },
-            SingularFieldFlag::WithoutFlag => self.elem.rust_storage_type(),
-        }
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct RepeatedField<'a> {
-    pub elem: FieldElem<'a>,
-    pub packed: bool,
-}
-
-impl<'a> RepeatedField<'a> {
-    fn rust_type(&self) -> RustType {
-        if !self.elem.is_copy()
-            && self.elem.primitive_type_variant() != PrimitiveTypeVariant::Carllerche
-        {
-            RustType::RepeatedField(Box::new(self.elem.rust_storage_type()))
-        } else {
-            RustType::Vec(Box::new(self.elem.rust_storage_type()))
-        }
-    }
-}
-
-#[derive(Clone)]
-pub struct MapField<'a> {
-    _name: String,
-    key: FieldElem<'a>,
-    value: FieldElem<'a>,
-}
-
-#[derive(Clone)]
-pub(crate) enum FieldKind<'a> {
-    // optional or required
-    Singular(SingularField<'a>),
-    // repeated except map
-    Repeated(RepeatedField<'a>),
-    // map
-    Map(MapField<'a>),
-    // part of oneof
-    Oneof(OneofField<'a>),
-}
-
-impl<'a> FieldKind<'a> {
-    fn elem(&self) -> &FieldElem {
-        match self {
-            &FieldKind::Singular(ref s) => &s.elem,
-            &FieldKind::Repeated(ref r) => &r.elem,
-            &FieldKind::Oneof(ref o) => &o.elem,
-            &FieldKind::Map(..) => {
-                panic!("no single elem type for map field");
-            }
-        }
-    }
-
-    fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
-        self.elem().primitive_type_variant()
-    }
-}
-
-// Representation of map entry: key type and value type
-#[derive(Clone, Debug)]
-pub struct EntryKeyValue<'a>(FieldElem<'a>, FieldElem<'a>);
-
-#[derive(Clone, Debug)]
-pub(crate) enum FieldElem<'a> {
-    Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant),
-    // name, file name, entry
-    Message(
-        String,
-        String,
-        Option<Box<EntryKeyValue<'a>>>,
-        marker::PhantomData<&'a ()>,
-    ),
-    // name, file name, default value
-    Enum(String, String, RustIdent),
-    Group,
-}
-
-impl<'a> FieldElem<'a> {
-    fn proto_type(&self) -> FieldDescriptorProto_Type {
-        match *self {
-            FieldElem::Primitive(t, ..) => t,
-            FieldElem::Group => FieldDescriptorProto_Type::TYPE_GROUP,
-            FieldElem::Message(..) => FieldDescriptorProto_Type::TYPE_MESSAGE,
-            FieldElem::Enum(..) => FieldDescriptorProto_Type::TYPE_ENUM,
-        }
-    }
-
-    fn is_copy(&self) -> bool {
-        type_is_copy(self.proto_type())
-    }
-
-    pub fn rust_storage_type(&self) -> RustType {
-        match *self {
-            FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => rust_name(t),
-            FieldElem::Primitive(
-                FieldDescriptorProto_Type::TYPE_STRING,
-                PrimitiveTypeVariant::Carllerche,
-            ) => RustType::Chars,
-            FieldElem::Primitive(
-                FieldDescriptorProto_Type::TYPE_BYTES,
-                PrimitiveTypeVariant::Carllerche,
-            ) => RustType::Bytes,
-            FieldElem::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(),
-            FieldElem::Group => RustType::Group,
-            FieldElem::Message(ref name, ..) => {
-                RustType::Message(RustTypeMessage(RustIdentWithPath::new(name.clone())))
-            }
-            FieldElem::Enum(ref name, _, ref default_value) => {
-                RustType::Enum(name.clone(), default_value.clone())
-            }
-        }
-    }
-
-    fn protobuf_type_gen(&self) -> ProtobufTypeGen {
-        match *self {
-            FieldElem::Primitive(t, v) => ProtobufTypeGen::Primitive(t, v),
-            FieldElem::Message(ref name, ..) => ProtobufTypeGen::Message(name.clone()),
-            FieldElem::Enum(ref name, ..) => ProtobufTypeGen::Enum(name.clone()),
-            FieldElem::Group => unreachable!(),
-        }
-    }
-
-    /// implementation of ProtobufType trait
-    fn lib_protobuf_type(&self, customize: &Customize) -> String {
-        self.protobuf_type_gen().rust_type(customize)
-    }
-
-    fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
-        match self {
-            &FieldElem::Primitive(_, v) => v,
-            _ => PrimitiveTypeVariant::Default,
-        }
-    }
-}
-
-fn field_elem<'a>(
-    field: &FieldWithContext,
-    root_scope: &'a RootScope<'a>,
-    parse_map: bool,
-    customize: &Customize,
-) -> (FieldElem<'a>, Option<EnumValueGen>) {
-    if field.field.get_field_type() == FieldDescriptorProto_Type::TYPE_GROUP {
-        (FieldElem::Group, None)
-    } else if field.field.has_type_name() {
-        let message_or_enum = root_scope
-            .find_message_or_enum(&ProtobufAbsolutePath::from(field.field.get_type_name()));
-        let file_name = message_or_enum
-            .get_scope()
-            .file_scope
-            .file_descriptor
-            .get_name()
-            .to_owned();
-        let rust_relative_name = type_name_to_rust_relative(
-            &ProtobufAbsolutePath::from(field.field.get_type_name()),
-            field.message.get_scope().file_scope.file_descriptor,
-            false,
-            root_scope,
-            customize,
-        );
-        match (field.field.get_field_type(), message_or_enum) {
-            (
-                FieldDescriptorProto_Type::TYPE_MESSAGE,
-                MessageOrEnumWithScope::Message(message_with_scope),
-            ) => {
-                let entry_key_value = if let (true, Some((key, value))) =
-                    (parse_map, message_with_scope.map_entry())
-                {
-                    Some(Box::new(EntryKeyValue(
-                        field_elem(&key, root_scope, false, customize).0,
-                        field_elem(&value, root_scope, false, customize).0,
-                    )))
-                } else {
-                    None
-                };
-                (
-                    FieldElem::Message(
-                        rust_relative_name,
-                        file_name,
-                        entry_key_value,
-                        marker::PhantomData,
-                    ),
-                    None,
-                )
-            }
-            (
-                FieldDescriptorProto_Type::TYPE_ENUM,
-                MessageOrEnumWithScope::Enum(enum_with_scope),
-            ) => {
-                let e = EnumGen::new(
-                    &enum_with_scope,
-                    field.message.get_scope().get_file_descriptor(),
-                    customize,
-                    root_scope,
-                );
-                let ev = if field.field.has_default_value() {
-                    e.value_by_name(field.field.get_default_value()).clone()
-                } else {
-                    e.values_unique().into_iter().next().unwrap()
-                };
-                (
-                    FieldElem::Enum(
-                        rust_relative_name,
-                        file_name,
-                        RustIdent::from(enum_with_scope.values()[0].rust_name().to_owned()),
-                    ),
-                    Some(ev),
-                )
-            }
-            _ => panic!("unknown named type: {:?}", field.field.get_field_type()),
-        }
-    } else if field.field.has_field_type() {
-        let carllerche_for_bytes = customize.carllerche_bytes_for_bytes.unwrap_or(false);
-        let carllerche_for_string = customize.carllerche_bytes_for_string.unwrap_or(false);
-
-        let elem = match field.field.get_field_type() {
-            FieldDescriptorProto_Type::TYPE_STRING if carllerche_for_string => {
-                FieldElem::Primitive(
-                    FieldDescriptorProto_Type::TYPE_STRING,
-                    PrimitiveTypeVariant::Carllerche,
-                )
-            }
-            FieldDescriptorProto_Type::TYPE_BYTES if carllerche_for_bytes => FieldElem::Primitive(
-                FieldDescriptorProto_Type::TYPE_BYTES,
-                PrimitiveTypeVariant::Carllerche,
-            ),
-            t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default),
-        };
-
-        (elem, None)
-    } else {
-        panic!(
-            "neither type_name, nor field_type specified for field: {}",
-            field.field.get_name()
-        );
-    }
-}
-
-pub enum AccessorStyle {
-    Lambda,
-    HasGet,
-}
-
-pub struct AccessorFn {
-    name: String,
-    type_params: Vec<String>,
-    pub style: AccessorStyle,
-}
-
-impl AccessorFn {
-    pub fn sig(&self) -> String {
-        let mut s = self.name.clone();
-        s.push_str("::<_");
-        for p in &self.type_params {
-            s.push_str(", ");
-            s.push_str(&p);
-        }
-        s.push_str(">");
-        s
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct FieldGen<'a> {
-    _root_scope: &'a RootScope<'a>,
-    syntax: Syntax,
-    pub proto_field: FieldWithContext<'a>,
-    // field name in generated code
-    pub rust_name: RustIdent,
-    pub proto_type: FieldDescriptorProto_Type,
-    wire_type: wire_format::WireType,
-    enum_default_value: Option<EnumValueGen>,
-    pub kind: FieldKind<'a>,
-    pub expose_field: bool,
-    pub generate_accessors: bool,
-    pub(crate) customize: Customize,
-}
-
-impl<'a> FieldGen<'a> {
-    pub fn parse(
-        field: FieldWithContext<'a>,
-        root_scope: &'a RootScope<'a>,
-        customize: &Customize,
-    ) -> FieldGen<'a> {
-        let mut customize = customize.clone();
-        customize.update_with(&customize_from_rustproto_for_field(
-            &field.field.get_options(),
-        ));
-
-        let (elem, enum_default_value) = field_elem(&field, root_scope, true, &customize);
-
-        let generate_accessors = customize.generate_accessors.unwrap_or(true);
-
-        let syntax = field.message.scope.file_scope.syntax();
-
-        let field_may_have_custom_default_value = syntax == Syntax::PROTO2
-            && field.field.get_label() != FieldDescriptorProto_Label::LABEL_REPEATED
-            && field.field.get_field_type() != FieldDescriptorProto_Type::TYPE_MESSAGE;
-
-        let default_expose_field = !field_may_have_custom_default_value;
-
-        let expose_field = customize.expose_fields.unwrap_or(default_expose_field);
-
-        let kind = if field.field.get_label() == FieldDescriptorProto_Label::LABEL_REPEATED {
-            match (elem, true) {
-                // map field
-                (FieldElem::Message(name, _, Some(key_value), _), true) => {
-                    FieldKind::Map(MapField {
-                        _name: name,
-                        key: key_value.0.clone(),
-                        value: key_value.1.clone(),
-                    })
-                }
-                // regular repeated field
-                (elem, _) => FieldKind::Repeated(RepeatedField {
-                    elem,
-                    packed: field.field.get_options().get_packed(),
-                }),
-            }
-        } else if let Some(oneof) = field.oneof() {
-            FieldKind::Oneof(OneofField::parse(&oneof, &field, elem, root_scope))
-        } else {
-            let flag = if field.message.scope.file_scope.syntax() == Syntax::PROTO3
-                && field.field.get_field_type() != FieldDescriptorProto_Type::TYPE_MESSAGE
-            {
-                SingularFieldFlag::WithoutFlag
-            } else {
-                SingularFieldFlag::WithFlag {
-                    required: field.field.get_label() == FieldDescriptorProto_Label::LABEL_REQUIRED,
-                }
-            };
-            FieldKind::Singular(SingularField { elem, flag })
-        };
-
-        FieldGen {
-            _root_scope: root_scope,
-            syntax: field.message.get_scope().file_scope.syntax(),
-            rust_name: field.rust_name(),
-            proto_type: field.field.get_field_type(),
-            wire_type: field_type_wire_type(field.field.get_field_type()),
-            enum_default_value,
-            proto_field: field.clone(),
-            kind,
-            expose_field,
-            generate_accessors,
-            customize,
-        }
-    }
-
-    fn tag_size(&self) -> u32 {
-        rt::tag_size(self.proto_field.number())
-    }
-
-    pub fn is_oneof(&self) -> bool {
-        match self.kind {
-            FieldKind::Oneof(..) => true,
-            _ => false,
-        }
-    }
-
-    pub fn oneof(&self) -> &OneofField {
-        match self.kind {
-            FieldKind::Oneof(ref oneof) => &oneof,
-            _ => panic!("not a oneof field: {}", self.reconstruct_def()),
-        }
-    }
-
-    fn is_singular(&self) -> bool {
-        match self.kind {
-            FieldKind::Singular(..) => true,
-            _ => false,
-        }
-    }
-
-    fn is_repeated_not_map(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(..) => true,
-            _ => false,
-        }
-    }
-
-    fn is_repeated_or_map(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => true,
-            _ => false,
-        }
-    }
-
-    fn is_repeated_packed(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(RepeatedField { packed: true, .. }) => true,
-            _ => false,
-        }
-    }
-
-    #[allow(dead_code)]
-    fn repeated(&self) -> &RepeatedField {
-        match self.kind {
-            FieldKind::Repeated(ref repeated) => &repeated,
-            _ => panic!("not a repeated field: {}", self.reconstruct_def()),
-        }
-    }
-
-    fn singular(&self) -> &SingularField {
-        match self.kind {
-            FieldKind::Singular(ref singular) => &singular,
-            _ => panic!("not a singular field: {}", self.reconstruct_def()),
-        }
-    }
-
-    fn map(&self) -> &MapField {
-        match self.kind {
-            FieldKind::Map(ref map) => &map,
-            _ => panic!("not a map field: {}", self.reconstruct_def()),
-        }
-    }
-
-    fn variant_path(&self) -> String {
-        // TODO: should reuse code from OneofVariantGen
-        format!(
-            "{}::{}",
-            self.oneof().oneof_type_name.to_code(&self.customize),
-            self.rust_name
-        )
-    }
-
-    // TODO: drop it
-    pub fn elem(&self) -> &FieldElem {
-        match self.kind {
-            FieldKind::Singular(SingularField { ref elem, .. }) => &elem,
-            FieldKind::Repeated(RepeatedField { ref elem, .. }) => &elem,
-            FieldKind::Oneof(OneofField { ref elem, .. }) => &elem,
-            FieldKind::Map(..) => unreachable!(),
-        }
-    }
-
-    // type of field in struct
-    pub fn full_storage_type(&self) -> RustType {
-        match self.kind {
-            FieldKind::Repeated(ref repeated) => repeated.rust_type(),
-            FieldKind::Map(MapField {
-                ref key, ref value, ..
-            }) => RustType::HashMap(
-                Box::new(key.rust_storage_type()),
-                Box::new(value.rust_storage_type()),
-            ),
-            FieldKind::Singular(ref singular) => singular.rust_storage_type(),
-            FieldKind::Oneof(..) => unreachable!(),
-        }
-    }
-
-    // type of `v` in `for v in field`
-    fn full_storage_iter_elem_type(&self) -> RustType {
-        if let FieldKind::Oneof(ref oneof) = self.kind {
-            oneof.elem.rust_storage_type()
-        } else {
-            self.full_storage_type().iter_elem_type()
-        }
-    }
-
-    // suffix `xxx` as in `os.write_xxx_no_tag(..)`
-    fn os_write_fn_suffix(&self) -> &str {
-        protobuf_name(self.proto_type)
-    }
-
-    // type of `v` in `os.write_xxx_no_tag(v)`
-    fn os_write_fn_param_type(&self) -> RustType {
-        match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
-            FieldDescriptorProto_Type::TYPE_BYTES => {
-                RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::Int(false, 8)))))
-            }
-            FieldDescriptorProto_Type::TYPE_ENUM => RustType::Int(true, 32),
-            t => rust_name(t),
-        }
-    }
-
-    // for field `foo`, type of param of `fn set_foo(..)`
-    fn set_xxx_param_type(&self) -> RustType {
-        match self.kind {
-            FieldKind::Singular(SingularField { ref elem, .. })
-            | FieldKind::Oneof(OneofField { ref elem, .. }) => elem.rust_storage_type(),
-            FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(),
-        }
-    }
-
-    // for field `foo`, return type if `fn take_foo(..)`
-    fn take_xxx_return_type(&self) -> RustType {
-        self.set_xxx_param_type()
-    }
-
-    // for field `foo`, return type of `fn mut_foo(..)`
-    fn mut_xxx_return_type(&self) -> RustType {
-        RustType::Ref(Box::new(match self.kind {
-            FieldKind::Singular(SingularField { ref elem, .. })
-            | FieldKind::Oneof(OneofField { ref elem, .. }) => elem.rust_storage_type(),
-            FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(),
-        }))
-    }
-
-    // for field `foo`, return type of `fn get_foo(..)`
-    fn get_xxx_return_type(&self) -> RustType {
-        match self.kind {
-            FieldKind::Singular(SingularField { ref elem, .. })
-            | FieldKind::Oneof(OneofField { ref elem, .. }) => match elem.is_copy() {
-                true => elem.rust_storage_type(),
-                false => elem.rust_storage_type().ref_type(),
-            },
-            FieldKind::Repeated(RepeatedField { ref elem, .. }) => RustType::Ref(Box::new(
-                RustType::Slice(Box::new(elem.rust_storage_type())),
-            )),
-            FieldKind::Map(..) => RustType::Ref(Box::new(self.full_storage_type())),
-        }
-    }
-
-    // fixed size type?
-    fn is_fixed(&self) -> bool {
-        field_type_size(self.proto_type).is_some()
-    }
-
-    // must use zigzag encoding?
-    fn is_zigzag(&self) -> bool {
-        match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_SINT32 | FieldDescriptorProto_Type::TYPE_SINT64 => true,
-            _ => false,
-        }
-    }
-
-    // data is enum
-    fn is_enum(&self) -> bool {
-        match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_ENUM => true,
-            _ => false,
-        }
-    }
-
-    // elem data is not stored in heap
-    pub fn elem_type_is_copy(&self) -> bool {
-        type_is_copy(self.proto_type)
-    }
-
-    fn defaut_value_from_proto_float(&self) -> String {
-        assert!(self.proto_field.field.has_default_value());
-
-        let type_name = match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_FLOAT => "f32",
-            FieldDescriptorProto_Type::TYPE_DOUBLE => "f64",
-            _ => unreachable!(),
-        };
-        let proto_default = self.proto_field.field.get_default_value();
-
-        let f = float::parse_protobuf_float(proto_default)
-            .expect(&format!("failed to parse float: {:?}", proto_default));
-
-        if f.is_nan() {
-            format!("::std::{}::NAN", type_name)
-        } else if f.is_infinite() {
-            if f > 0.0 {
-                format!("::std::{}::INFINITY", type_name)
-            } else {
-                format!("::std::{}::NEG_INFINITY", type_name)
-            }
-        } else {
-            format!("{:?}{}", f, type_name)
-        }
-    }
-
-    fn default_value_from_proto(&self) -> Option<String> {
-        assert!(self.is_singular() || self.is_oneof());
-        if self.enum_default_value.is_some() {
-            Some(self.enum_default_value.as_ref().unwrap().rust_name_outer())
-        } else if self.proto_field.field.has_default_value() {
-            let proto_default = self.proto_field.field.get_default_value();
-            Some(match self.proto_type {
-                // For numeric types, contains the original text representation of the value
-                FieldDescriptorProto_Type::TYPE_DOUBLE | FieldDescriptorProto_Type::TYPE_FLOAT => {
-                    self.defaut_value_from_proto_float()
-                }
-                FieldDescriptorProto_Type::TYPE_INT32
-                | FieldDescriptorProto_Type::TYPE_SINT32
-                | FieldDescriptorProto_Type::TYPE_SFIXED32 => format!("{}i32", proto_default),
-                FieldDescriptorProto_Type::TYPE_UINT32
-                | FieldDescriptorProto_Type::TYPE_FIXED32 => format!("{}u32", proto_default),
-                FieldDescriptorProto_Type::TYPE_INT64
-                | FieldDescriptorProto_Type::TYPE_SINT64
-                | FieldDescriptorProto_Type::TYPE_SFIXED64 => format!("{}i64", proto_default),
-                FieldDescriptorProto_Type::TYPE_UINT64
-                | FieldDescriptorProto_Type::TYPE_FIXED64 => format!("{}u64", proto_default),
-
-                // For booleans, "true" or "false"
-                FieldDescriptorProto_Type::TYPE_BOOL => format!("{}", proto_default),
-                // For strings, contains the default text contents (not escaped in any way)
-                FieldDescriptorProto_Type::TYPE_STRING => rust::quote_escape_str(proto_default),
-                // For bytes, contains the C escaped value.  All bytes >= 128 are escaped
-                FieldDescriptorProto_Type::TYPE_BYTES => {
-                    rust::quote_escape_bytes(&text_format::unescape_string(proto_default))
-                }
-                // TODO: resolve outer message prefix
-                FieldDescriptorProto_Type::TYPE_GROUP | FieldDescriptorProto_Type::TYPE_ENUM => {
-                    unreachable!()
-                }
-                FieldDescriptorProto_Type::TYPE_MESSAGE => panic!(
-                    "default value is not implemented for type: {:?}",
-                    self.proto_type
-                ),
-            })
-        } else {
-            None
-        }
-    }
-
-    fn default_value_from_proto_typed(&self) -> Option<RustValueTyped> {
-        self.default_value_from_proto().map(|v| {
-            let default_value_type = match self.proto_type {
-                FieldDescriptorProto_Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
-                FieldDescriptorProto_Type::TYPE_BYTES => {
-                    RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::u8()))))
-                }
-                _ => self.full_storage_iter_elem_type(),
-            };
-
-            RustValueTyped {
-                value: v,
-                rust_type: default_value_type,
-            }
-        })
-    }
-
-    // default value to be returned from fn get_xxx
-    fn get_xxx_default_value_rust(&self) -> String {
-        assert!(self.is_singular() || self.is_oneof());
-        self.default_value_from_proto()
-            .unwrap_or_else(|| self.get_xxx_return_type().default_value(&self.customize))
-    }
-
-    // default to be assigned to field
-    fn element_default_value_rust(&self) -> RustValueTyped {
-        assert!(
-            self.is_singular() || self.is_oneof(),
-            "field is not singular: {}",
-            self.reconstruct_def()
-        );
-        self.default_value_from_proto_typed().unwrap_or_else(|| {
-            self.elem()
-                .rust_storage_type()
-                .default_value_typed(&self.customize)
-        })
-    }
-
-    pub fn reconstruct_def(&self) -> String {
-        let prefix = match (self.proto_field.field.get_label(), self.syntax) {
-            (FieldDescriptorProto_Label::LABEL_REPEATED, _) => "repeated ",
-            (_, Syntax::PROTO3) => "",
-            (FieldDescriptorProto_Label::LABEL_OPTIONAL, _) => "optional ",
-            (FieldDescriptorProto_Label::LABEL_REQUIRED, _) => "required ",
-        };
-        format!(
-            "{}{} {} = {}",
-            prefix,
-            field_type_protobuf_name(&self.proto_field.field),
-            self.proto_field.name(),
-            self.proto_field.number()
-        )
-    }
-
-    pub fn accessor_fn(&self) -> AccessorFn {
-        match self.kind {
-            FieldKind::Repeated(RepeatedField { ref elem, .. }) => {
-                let coll = match self.full_storage_type() {
-                    RustType::Vec(..) => "vec",
-                    RustType::RepeatedField(..) => "repeated_field",
-                    _ => unreachable!(),
-                };
-                let name = format!("make_{}_accessor", coll);
-                AccessorFn {
-                    name: name,
-                    type_params: vec![elem.lib_protobuf_type(&self.customize)],
-                    style: AccessorStyle::Lambda,
-                }
-            }
-            FieldKind::Map(MapField {
-                ref key, ref value, ..
-            }) => AccessorFn {
-                name: "make_map_accessor".to_owned(),
-                type_params: vec![
-                    key.lib_protobuf_type(&self.customize),
-                    value.lib_protobuf_type(&self.customize),
-                ],
-                style: AccessorStyle::Lambda,
-            },
-            FieldKind::Singular(SingularField {
-                ref elem,
-                flag: SingularFieldFlag::WithoutFlag,
-            }) => {
-                if let &FieldElem::Message(ref name, ..) = elem {
-                    // TODO: old style, needed because of default instance
-
-                    AccessorFn {
-                        name: "make_singular_message_accessor".to_owned(),
-                        type_params: vec![name.clone()],
-                        style: AccessorStyle::HasGet,
-                    }
-                } else {
-                    AccessorFn {
-                        name: "make_simple_field_accessor".to_owned(),
-                        type_params: vec![elem.lib_protobuf_type(&self.customize)],
-                        style: AccessorStyle::Lambda,
-                    }
-                }
-            }
-            FieldKind::Singular(SingularField {
-                ref elem,
-                flag: SingularFieldFlag::WithFlag { .. },
-            }) => {
-                let coll = match self.full_storage_type() {
-                    RustType::Option(..) => "option",
-                    RustType::SingularField(..) => "singular_field",
-                    RustType::SingularPtrField(..) => "singular_ptr_field",
-                    _ => unreachable!(),
-                };
-                let name = format!("make_{}_accessor", coll);
-                AccessorFn {
-                    name: name,
-                    type_params: vec![elem.lib_protobuf_type(&self.customize)],
-                    style: AccessorStyle::Lambda,
-                }
-            }
-            FieldKind::Oneof(OneofField { ref elem, .. }) => {
-                // TODO: uses old style
-
-                let suffix = match &self.elem().rust_storage_type() {
-                    t if t.is_primitive() => t.to_code(&self.customize),
-                    &RustType::String | &RustType::Chars => "string".to_string(),
-                    &RustType::Vec(ref t) if t.is_u8() => "bytes".to_string(),
-                    &RustType::Bytes => "bytes".to_string(),
-                    &RustType::Enum(..) => "enum".to_string(),
-                    &RustType::Message(..) => "message".to_string(),
-                    t => panic!("unexpected field type: {:?}", t),
-                };
-
-                let name = format!("make_singular_{}_accessor", suffix);
-
-                let mut type_params = Vec::new();
-                match elem {
-                    &FieldElem::Message(ref name, ..) | &FieldElem::Enum(ref name, ..) => {
-                        type_params.push(name.to_owned());
-                    }
-                    _ => (),
-                }
-
-                AccessorFn {
-                    name: name,
-                    type_params: type_params,
-                    style: AccessorStyle::HasGet,
-                }
-            }
-        }
-    }
-
-    pub fn write_clear(&self, w: &mut CodeWriter) {
-        if self.is_oneof() {
-            w.write_line(&format!(
-                "self.{} = ::std::option::Option::None;",
-                self.oneof().oneof_rust_field_name
-            ));
-        } else {
-            let clear_expr = self
-                .full_storage_type()
-                .clear(&self.self_field(), &self.customize);
-            w.write_line(&format!("{};", clear_expr));
-        }
-    }
-
-    // expression that returns size of data is variable
-    fn element_size(&self, var: &str, var_type: &RustType) -> String {
-        assert!(!self.is_repeated_packed());
-
-        match field_type_size(self.proto_type) {
-            Some(data_size) => format!("{}", data_size + self.tag_size()),
-            None => match self.proto_type {
-                FieldDescriptorProto_Type::TYPE_MESSAGE => panic!("not a single-liner"),
-                FieldDescriptorProto_Type::TYPE_BYTES => format!(
-                    "{}::rt::bytes_size({}, &{})",
-                    protobuf_crate_path(&self.customize),
-                    self.proto_field.number(),
-                    var
-                ),
-                FieldDescriptorProto_Type::TYPE_STRING => format!(
-                    "{}::rt::string_size({}, &{})",
-                    protobuf_crate_path(&self.customize),
-                    self.proto_field.number(),
-                    var
-                ),
-                FieldDescriptorProto_Type::TYPE_ENUM => {
-                    let param_type = match var_type {
-                        &RustType::Ref(ref t) => (**t).clone(),
-                        t => t.clone(),
-                    };
-                    format!(
-                        "{}::rt::enum_size({}, {})",
-                        protobuf_crate_path(&self.customize),
-                        self.proto_field.number(),
-                        var_type.into_target(&param_type, var, &self.customize)
-                    )
-                }
-                _ => {
-                    let param_type = match var_type {
-                        &RustType::Ref(ref t) => (**t).clone(),
-                        t => t.clone(),
-                    };
-                    if self.proto_type.is_s_varint() {
-                        format!(
-                            "{}::rt::value_varint_zigzag_size({}, {})",
-                            protobuf_crate_path(&self.customize),
-                            self.proto_field.number(),
-                            var_type.into_target(&param_type, var, &self.customize)
-                        )
-                    } else {
-                        format!(
-                            "{}::rt::value_size({}, {}, {}::wire_format::{:?})",
-                            protobuf_crate_path(&self.customize),
-                            self.proto_field.number(),
-                            var_type.into_target(&param_type, var, &self.customize),
-                            protobuf_crate_path(&self.customize),
-                            self.wire_type,
-                        )
-                    }
-                }
-            },
-        }
-    }
-
-    // output code that writes single element to stream
-    pub fn write_write_element(&self, w: &mut CodeWriter, os: &str, var: &str, ty: &RustType) {
-        if let FieldKind::Repeated(RepeatedField { packed: true, .. }) = self.kind {
-            unreachable!();
-        };
-
-        match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_MESSAGE => {
-                w.write_line(&format!(
-                    "{}.write_tag({}, {}::wire_format::{:?})?;",
-                    os,
-                    self.proto_field.number(),
-                    protobuf_crate_path(&self.customize),
-                    wire_format::WireTypeLengthDelimited
-                ));
-                w.write_line(&format!(
-                    "{}.write_raw_varint32({}.get_cached_size())?;",
-                    os, var
-                ));
-                w.write_line(&format!("{}.write_to_with_cached_sizes({})?;", var, os));
-            }
-            _ => {
-                let param_type = self.os_write_fn_param_type();
-                let os_write_fn_suffix = self.os_write_fn_suffix();
-                let number = self.proto_field.number();
-                w.write_line(&format!(
-                    "{}.write_{}({}, {})?;",
-                    os,
-                    os_write_fn_suffix,
-                    number,
-                    ty.into_target(&param_type, var, &self.customize)
-                ));
-            }
-        }
-    }
-
-    fn self_field(&self) -> String {
-        format!("self.{}", self.rust_name)
-    }
-
-    fn self_field_is_some(&self) -> String {
-        assert!(self.is_singular());
-        format!("{}.is_some()", self.self_field())
-    }
-
-    fn self_field_is_not_empty(&self) -> String {
-        assert!(self.is_repeated_or_map());
-        format!("!{}.is_empty()", self.self_field())
-    }
-
-    fn self_field_is_none(&self) -> String {
-        assert!(self.is_singular());
-        format!("{}.is_none()", self.self_field())
-    }
-
-    // type of expression returned by `as_option()`
-    fn as_option_type(&self) -> RustType {
-        assert!(self.is_singular());
-        match self.full_storage_type() {
-            RustType::Option(ref e) if e.is_copy() => RustType::Option(e.clone()),
-            RustType::Option(e) => RustType::Option(Box::new(e.ref_type())),
-            RustType::SingularField(ty) | RustType::SingularPtrField(ty) => {
-                RustType::Option(Box::new(RustType::Ref(ty)))
-            }
-            x => panic!("cannot convert {:?} to option", x),
-        }
-    }
-
-    // field data viewed as Option
-    fn self_field_as_option(&self) -> RustValueTyped {
-        assert!(self.is_singular());
-
-        let suffix = match self.full_storage_type() {
-            RustType::Option(ref e) if e.is_copy() => "",
-            _ => ".as_ref()",
-        };
-
-        self.as_option_type()
-            .value(format!("{}{}", self.self_field(), suffix))
-    }
-
-    fn write_if_let_self_field_is_some<F>(&self, w: &mut CodeWriter, cb: F)
-    where
-        F: Fn(&str, &RustType, &mut CodeWriter),
-    {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => panic!("field is not singular"),
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithFlag { .. },
-                ref elem,
-            }) => {
-                let var = "v";
-                let ref_prefix = match elem.rust_storage_type().is_copy() {
-                    true => "",
-                    false => "ref ",
-                };
-                let as_option = self.self_field_as_option();
-                w.if_let_stmt(
-                    &format!("Some({}{})", ref_prefix, var),
-                    &as_option.value,
-                    |w| {
-                        let v_type = as_option.rust_type.elem_type();
-                        cb(var, &v_type, w);
-                    },
-                );
-            }
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithoutFlag,
-                ref elem,
-            }) => match *elem {
-                FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
-                | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
-                    w.if_stmt(format!("!{}.is_empty()", self.self_field()), |w| {
-                        cb(&self.self_field(), &self.full_storage_type(), w);
-                    });
-                }
-                _ => {
-                    w.if_stmt(
-                        format!(
-                            "{} != {}",
-                            self.self_field(),
-                            self.full_storage_type().default_value(&self.customize)
-                        ),
-                        |w| {
-                            cb(&self.self_field(), &self.full_storage_type(), w);
-                        },
-                    );
-                }
-            },
-            FieldKind::Oneof(..) => unreachable!(),
-        }
-    }
-
-    fn write_if_self_field_is_not_empty<F>(&self, w: &mut CodeWriter, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        assert!(self.is_repeated_or_map());
-        let self_field_is_not_empty = self.self_field_is_not_empty();
-        w.if_stmt(self_field_is_not_empty, cb);
-    }
-
-    pub fn write_if_self_field_is_none<F>(&self, w: &mut CodeWriter, cb: F)
-    where
-        F: Fn(&mut CodeWriter),
-    {
-        let self_field_is_none = self.self_field_is_none();
-        w.if_stmt(self_field_is_none, cb)
-    }
-
-    // repeated or singular
-    pub fn write_for_self_field<F>(&self, w: &mut CodeWriter, varn: &str, cb: F)
-    where
-        F: Fn(&mut CodeWriter, &RustType),
-    {
-        match self.kind {
-            FieldKind::Oneof(OneofField {
-                ref elem,
-                ref oneof_type_name,
-                ..
-            }) => {
-                let cond = format!(
-                    "Some({}::{}(ref {}))",
-                    oneof_type_name.to_code(&self.customize),
-                    self.rust_name,
-                    varn
-                );
-                w.if_let_stmt(&cond, &self.self_field_oneof(), |w| {
-                    cb(w, &elem.rust_storage_type())
-                })
-            }
-            _ => {
-                let v_type = self.full_storage_iter_elem_type();
-                let self_field = self.self_field();
-                w.for_stmt(&format!("&{}", self_field), varn, |w| cb(w, &v_type));
-            }
-        }
-    }
-
-    fn write_self_field_assign(&self, w: &mut CodeWriter, value: &str) {
-        let self_field = self.self_field();
-        w.write_line(&format!("{} = {};", self_field, value));
-    }
-
-    fn write_self_field_assign_some(&self, w: &mut CodeWriter, value: &str) {
-        let full_storage_type = self.full_storage_type();
-        match self.singular() {
-            &SingularField {
-                flag: SingularFieldFlag::WithFlag { .. },
-                ..
-            } => {
-                self.write_self_field_assign(
-                    w,
-                    &full_storage_type.wrap_value(value, &self.customize),
-                );
-            }
-            &SingularField {
-                flag: SingularFieldFlag::WithoutFlag,
-                ..
-            } => {
-                self.write_self_field_assign(w, value);
-            }
-        }
-    }
-
-    fn write_self_field_assign_value(&self, w: &mut CodeWriter, value: &str, ty: &RustType) {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => {
-                let converted = ty.into_target(&self.full_storage_type(), value, &self.customize);
-                self.write_self_field_assign(w, &converted);
-            }
-            FieldKind::Singular(SingularField { ref elem, ref flag }) => {
-                let converted = ty.into_target(&elem.rust_storage_type(), value, &self.customize);
-                let wrapped = if *flag == SingularFieldFlag::WithoutFlag {
-                    converted
-                } else {
-                    self.full_storage_type()
-                        .wrap_value(&converted, &self.customize)
-                };
-                self.write_self_field_assign(w, &wrapped);
-            }
-            FieldKind::Oneof(..) => unreachable!(),
-        }
-    }
-
-    fn write_self_field_assign_default(&self, w: &mut CodeWriter) {
-        assert!(self.is_singular());
-        if self.is_oneof() {
-            let self_field_oneof = self.self_field_oneof();
-            w.write_line(format!(
-                "{} = ::std::option::Option::Some({}({}))",
-                self_field_oneof,
-                self.variant_path(),
-                // TODO: default from .proto is not needed here (?)
-                self.element_default_value_rust()
-                    .into_type(self.full_storage_iter_elem_type(), &self.customize)
-                    .value
-            ));
-        } else {
-            // Note it is different from C++ protobuf, where field is initialized
-            // with default value
-            match self.full_storage_type() {
-                RustType::SingularField(..) | RustType::SingularPtrField(..) => {
-                    let self_field = self.self_field();
-                    w.write_line(&format!("{}.set_default();", self_field));
-                }
-                _ => {
-                    self.write_self_field_assign_some(
-                        w,
-                        &self
-                            .elem()
-                            .rust_storage_type()
-                            .default_value_typed(&self.customize)
-                            .into_type(self.elem().rust_storage_type(), &self.customize)
-                            .value,
-                    );
-                }
-            }
-        }
-    }
-
-    fn self_field_vec_packed_fixed_data_size(&self) -> String {
-        assert!(self.is_fixed());
-        format!(
-            "({}.len() * {}) as u32",
-            self.self_field(),
-            field_type_size(self.proto_type).unwrap()
-        )
-    }
-
-    fn self_field_vec_packed_varint_data_size(&self) -> String {
-        assert!(!self.is_fixed());
-        let fn_name = if self.is_enum() {
-            "vec_packed_enum_data_size".to_string()
-        } else {
-            let zigzag_suffix = if self.is_zigzag() { "_zigzag" } else { "" };
-            format!("vec_packed_varint{}_data_size", zigzag_suffix)
-        };
-        format!(
-            "{}::rt::{}(&{})",
-            protobuf_crate_path(&self.customize),
-            fn_name,
-            self.self_field()
-        )
-    }
-
-    fn self_field_vec_packed_data_size(&self) -> String {
-        assert!(self.is_repeated_not_map());
-        if self.is_fixed() {
-            self.self_field_vec_packed_fixed_data_size()
-        } else {
-            self.self_field_vec_packed_varint_data_size()
-        }
-    }
-
-    fn self_field_vec_packed_fixed_size(&self) -> String {
-        // zero is filtered outside
-        format!(
-            "{} + {}::rt::compute_raw_varint32_size({}) + {}",
-            self.tag_size(),
-            protobuf_crate_path(&self.customize),
-            self.self_field_vec_packed_fixed_data_size(),
-            self.self_field_vec_packed_fixed_data_size()
-        )
-    }
-
-    fn self_field_vec_packed_varint_size(&self) -> String {
-        // zero is filtered outside
-        assert!(!self.is_fixed());
-        let fn_name = if self.is_enum() {
-            "vec_packed_enum_size".to_string()
-        } else {
-            let zigzag_suffix = if self.is_zigzag() { "_zigzag" } else { "" };
-            format!("vec_packed_varint{}_size", zigzag_suffix)
-        };
-        format!(
-            "{}::rt::{}({}, &{})",
-            protobuf_crate_path(&self.customize),
-            fn_name,
-            self.proto_field.number(),
-            self.self_field()
-        )
-    }
-
-    fn self_field_oneof(&self) -> String {
-        format!("self.{}", self.oneof().oneof_rust_field_name)
-    }
-
-    pub fn clear_field_func(&self) -> String {
-        format!("clear_{}", self.rust_name)
-    }
-
-    // Write `merge_from` part for this singular or repeated field
-    // of type message, string or bytes
-    fn write_merge_from_field_message_string_bytes(&self, w: &mut CodeWriter) {
-        let singular_or_repeated = match self.kind {
-            FieldKind::Repeated(..) => "repeated",
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithFlag { .. },
-                ..
-            }) => "singular",
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithoutFlag,
-                ..
-            }) => "singular_proto3",
-            FieldKind::Map(..) | FieldKind::Oneof(..) => unreachable!(),
-        };
-        let carllerche = match self.kind.primitive_type_variant() {
-            PrimitiveTypeVariant::Carllerche => "carllerche_",
-            PrimitiveTypeVariant::Default => "",
-        };
-        let type_name_for_fn = protobuf_name(self.proto_type);
-        w.write_line(&format!(
-            "{}::rt::read_{}_{}{}_into(wire_type, is, &mut self.{})?;",
-            protobuf_crate_path(&self.customize),
-            singular_or_repeated,
-            carllerche,
-            type_name_for_fn,
-            self.rust_name
-        ));
-    }
-
-    fn write_error_unexpected_wire_type(&self, wire_type_var: &str, w: &mut CodeWriter) {
-        w.write_line(&format!(
-            "return ::std::result::Result::Err({}::rt::unexpected_wire_type({}));",
-            protobuf_crate_path(&self.customize),
-            wire_type_var
-        ));
-    }
-
-    fn write_assert_wire_type(&self, wire_type_var: &str, w: &mut CodeWriter) {
-        w.if_stmt(
-            &format!(
-                "{} != {}::wire_format::{:?}",
-                wire_type_var,
-                protobuf_crate_path(&self.customize),
-                self.wire_type
-            ),
-            |w| {
-                self.write_error_unexpected_wire_type(wire_type_var, w);
-            },
-        );
-    }
-
-    // Write `merge_from` part for this oneof field
-    fn write_merge_from_oneof(&self, f: &OneofField, wire_type_var: &str, w: &mut CodeWriter) {
-        self.write_assert_wire_type(wire_type_var, w);
-
-        let typed = RustValueTyped {
-            value: format!(
-                "{}?",
-                self.proto_type.read("is", f.elem.primitive_type_variant())
-            ),
-            rust_type: self.full_storage_iter_elem_type(),
-        };
-
-        let maybe_boxed = if f.boxed {
-            typed.boxed(&self.customize)
-        } else {
-            typed
-        };
-
-        w.write_line(&format!(
-            "self.{} = ::std::option::Option::Some({}({}));",
-            self.oneof().oneof_rust_field_name,
-            self.variant_path(),
-            maybe_boxed.value
-        )); // TODO: into_type
-    }
-
-    // Write `merge_from` part for this map field
-    fn write_merge_from_map(&self, w: &mut CodeWriter) {
-        let &MapField {
-            ref key, ref value, ..
-        } = self.map();
-        w.write_line(&format!(
-            "{}::rt::read_map_into::<{}, {}>(wire_type, is, &mut {})?;",
-            protobuf_crate_path(&self.customize),
-            key.lib_protobuf_type(&self.customize),
-            value.lib_protobuf_type(&self.customize),
-            self.self_field()
-        ));
-    }
-
-    // Write `merge_from` part for this singular field
-    fn write_merge_from_singular(&self, wire_type_var: &str, w: &mut CodeWriter) {
-        let field = match self.kind {
-            FieldKind::Singular(ref field) => field,
-            _ => panic!(),
-        };
-
-        match field.elem {
-            FieldElem::Message(..)
-            | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
-            | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
-                self.write_merge_from_field_message_string_bytes(w);
-            }
-            FieldElem::Enum(..) => {
-                let version = match field.flag {
-                    SingularFieldFlag::WithFlag { .. } => "proto2",
-                    SingularFieldFlag::WithoutFlag => "proto3",
-                };
-                w.write_line(&format!(
-                    "{}::rt::read_{}_enum_with_unknown_fields_into({}, is, &mut self.{}, {}, &mut self.unknown_fields)?",
-                    protobuf_crate_path(&self.customize),
-                    version,
-                    wire_type_var,
-                    self.rust_name,
-                    self.proto_field.number()
-                ));
-            }
-            _ => {
-                let read_proc = format!(
-                    "{}?",
-                    self.proto_type.read("is", PrimitiveTypeVariant::Default)
-                );
-
-                self.write_assert_wire_type(wire_type_var, w);
-                w.write_line(&format!("let tmp = {};", read_proc));
-                self.write_self_field_assign_some(w, "tmp");
-            }
-        }
-    }
-
-    // Write `merge_from` part for this repeated field
-    fn write_merge_from_repeated(&self, wire_type_var: &str, w: &mut CodeWriter) {
-        let field = match self.kind {
-            FieldKind::Repeated(ref field) => field,
-            _ => panic!(),
-        };
-
-        match field.elem {
-            FieldElem::Message(..)
-            | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
-            | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
-                self.write_merge_from_field_message_string_bytes(w);
-            }
-            FieldElem::Enum(..) => {
-                w.write_line(&format!(
-                    "{}::rt::read_repeated_enum_with_unknown_fields_into({}, is, &mut self.{}, {}, &mut self.unknown_fields)?",
-                    protobuf_crate_path(&self.customize),
-                    wire_type_var,
-                    self.rust_name,
-                    self.proto_field.number()
-                ));
-            }
-            _ => {
-                w.write_line(&format!(
-                    "{}::rt::read_repeated_{}_into({}, is, &mut self.{})?;",
-                    protobuf_crate_path(&self.customize),
-                    protobuf_name(self.proto_type),
-                    wire_type_var,
-                    self.rust_name
-                ));
-            }
-        }
-    }
-
-    // Write `merge_from` part for this field
-    pub fn write_merge_from_field(&self, wire_type_var: &str, w: &mut CodeWriter) {
-        match self.kind {
-            FieldKind::Oneof(ref f) => self.write_merge_from_oneof(&f, wire_type_var, w),
-            FieldKind::Map(..) => self.write_merge_from_map(w),
-            FieldKind::Singular(..) => self.write_merge_from_singular(wire_type_var, w),
-            FieldKind::Repeated(..) => self.write_merge_from_repeated(wire_type_var, w),
-        }
-    }
-
-    fn self_field_vec_packed_size(&self) -> String {
-        match self.kind {
-            FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
-                // zero is filtered outside
-                if self.is_fixed() {
-                    self.self_field_vec_packed_fixed_size()
-                } else {
-                    self.self_field_vec_packed_varint_size()
-                }
-            }
-            _ => {
-                panic!("not packed");
-            }
-        }
-    }
-
-    pub fn write_element_size(
-        &self,
-        w: &mut CodeWriter,
-        item_var: &str,
-        item_var_type: &RustType,
-        sum_var: &str,
-    ) {
-        assert!(!self.is_repeated_packed());
-
-        match self.proto_type {
-            FieldDescriptorProto_Type::TYPE_MESSAGE => {
-                w.write_line(&format!("let len = {}.compute_size();", item_var));
-                let tag_size = self.tag_size();
-                w.write_line(&format!(
-                    "{} += {} + {}::rt::compute_raw_varint32_size(len) + len;",
-                    sum_var,
-                    tag_size,
-                    protobuf_crate_path(&self.customize)
-                ));
-            }
-            _ => {
-                w.write_line(&format!(
-                    "{} += {};",
-                    sum_var,
-                    self.element_size(item_var, item_var_type)
-                ));
-            }
-        }
-    }
-
-    pub fn write_message_write_field(&self, w: &mut CodeWriter) {
-        match self.kind {
-            FieldKind::Singular(..) => {
-                self.write_if_let_self_field_is_some(w, |v, v_type, w| {
-                    self.write_write_element(w, "os", v, v_type);
-                });
-            }
-            FieldKind::Repeated(RepeatedField { packed: false, .. }) => {
-                self.write_for_self_field(w, "v", |w, v_type| {
-                    self.write_write_element(w, "os", "v", v_type);
-                });
-            }
-            FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
-                self.write_if_self_field_is_not_empty(w, |w| {
-                    let number = self.proto_field.number();
-                    w.write_line(&format!(
-                        "os.write_tag({}, {}::wire_format::{:?})?;",
-                        number,
-                        protobuf_crate_path(&self.customize),
-                        wire_format::WireTypeLengthDelimited
-                    ));
-                    w.comment("TODO: Data size is computed again, it should be cached");
-                    let data_size_expr = self.self_field_vec_packed_data_size();
-                    w.write_line(&format!("os.write_raw_varint32({})?;", data_size_expr));
-                    self.write_for_self_field(w, "v", |w, v_type| {
-                        let param_type = self.os_write_fn_param_type();
-                        let os_write_fn_suffix = self.os_write_fn_suffix();
-                        w.write_line(&format!(
-                            "os.write_{}_no_tag({})?;",
-                            os_write_fn_suffix,
-                            v_type.into_target(&param_type, "v", &self.customize)
-                        ));
-                    });
-                });
-            }
-            FieldKind::Map(MapField {
-                ref key, ref value, ..
-            }) => {
-                w.write_line(&format!(
-                    "{}::rt::write_map_with_cached_sizes::<{}, {}>({}, &{}, os)?;",
-                    protobuf_crate_path(&self.customize),
-                    key.lib_protobuf_type(&self.customize),
-                    value.lib_protobuf_type(&self.customize),
-                    self.proto_field.number(),
-                    self.self_field()
-                ));
-            }
-            FieldKind::Oneof(..) => unreachable!(),
-        };
-    }
-
-    pub fn write_message_compute_field_size(&self, sum_var: &str, w: &mut CodeWriter) {
-        match self.kind {
-            FieldKind::Singular(..) => {
-                self.write_if_let_self_field_is_some(w, |v, v_type, w| {
-                    match field_type_size(self.proto_type) {
-                        Some(s) => {
-                            let tag_size = self.tag_size();
-                            w.write_line(&format!("{} += {};", sum_var, (s + tag_size) as isize));
-                        }
-                        None => {
-                            self.write_element_size(w, v, v_type, sum_var);
-                        }
-                    };
-                });
-            }
-            FieldKind::Repeated(RepeatedField { packed: false, .. }) => {
-                match field_type_size(self.proto_type) {
-                    Some(s) => {
-                        let tag_size = self.tag_size();
-                        let self_field = self.self_field();
-                        w.write_line(&format!(
-                            "{} += {} * {}.len() as u32;",
-                            sum_var,
-                            (s + tag_size) as isize,
-                            self_field
-                        ));
-                    }
-                    None => {
-                        self.write_for_self_field(w, "value", |w, value_type| {
-                            self.write_element_size(w, "value", value_type, sum_var);
-                        });
-                    }
-                };
-            }
-            FieldKind::Map(MapField {
-                ref key, ref value, ..
-            }) => {
-                w.write_line(&format!(
-                    "{} += {}::rt::compute_map_size::<{}, {}>({}, &{});",
-                    sum_var,
-                    protobuf_crate_path(&self.customize),
-                    key.lib_protobuf_type(&self.customize),
-                    value.lib_protobuf_type(&self.customize),
-                    self.proto_field.number(),
-                    self.self_field()
-                ));
-            }
-            FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
-                self.write_if_self_field_is_not_empty(w, |w| {
-                    let size_expr = self.self_field_vec_packed_size();
-                    w.write_line(&format!("{} += {};", sum_var, size_expr));
-                });
-            }
-            FieldKind::Oneof(..) => unreachable!(),
-        }
-    }
-
-    fn write_message_field_get_singular(&self, w: &mut CodeWriter) {
-        let get_xxx_return_type = self.get_xxx_return_type();
-
-        if self.proto_type == FieldDescriptorProto_Type::TYPE_MESSAGE {
-            let self_field = self.self_field();
-            let ref rust_type_message = match self.elem().rust_storage_type() {
-                RustType::Message(m) => m,
-                _ => unreachable!(),
-            };
-            w.write_line(&format!(
-                "{}.as_ref().unwrap_or_else(|| {})",
-                self_field,
-                rust_type_message.default_instance(&self.customize)
-            ));
-        } else {
-            let get_xxx_default_value_rust = self.get_xxx_default_value_rust();
-            let self_field = self.self_field();
-            match self.singular() {
-                &SingularField {
-                    flag: SingularFieldFlag::WithFlag { .. },
-                    ..
-                } => {
-                    if get_xxx_return_type.is_ref() {
-                        let as_option = self.self_field_as_option();
-                        w.match_expr(&as_option.value, |w| {
-                            let v_type = as_option.rust_type.elem_type();
-                            let r_type = self.get_xxx_return_type();
-                            w.case_expr(
-                                "Some(v)",
-                                v_type.into_target(&r_type, "v", &self.customize),
-                            );
-                            let get_xxx_default_value_rust = self.get_xxx_default_value_rust();
-                            w.case_expr("None", get_xxx_default_value_rust);
-                        });
-                    } else {
-                        w.write_line(&format!(
-                            "{}.unwrap_or({})",
-                            self_field, get_xxx_default_value_rust
-                        ));
-                    }
-                }
-                &SingularField {
-                    flag: SingularFieldFlag::WithoutFlag,
-                    ..
-                } => {
-                    w.write_line(self.full_storage_type().into_target(
-                        &get_xxx_return_type,
-                        &self_field,
-                        &self.customize,
-                    ));
-                }
-            }
-        }
-    }
-
-    fn write_message_field_get(&self, w: &mut CodeWriter) {
-        let get_xxx_return_type = self.get_xxx_return_type();
-        let fn_def = format!(
-            "get_{}(&self) -> {}",
-            self.rust_name,
-            get_xxx_return_type.to_code(&self.customize)
-        );
-
-        w.pub_fn(&fn_def, |w| match self.kind {
-            FieldKind::Oneof(OneofField { ref elem, .. }) => {
-                let self_field_oneof = self.self_field_oneof();
-                w.match_expr(self_field_oneof, |w| {
-                    let (refv, vtype) = if !self.elem_type_is_copy() {
-                        ("ref v", elem.rust_storage_type().ref_type())
-                    } else {
-                        ("v", elem.rust_storage_type())
-                    };
-                    w.case_expr(
-                        format!(
-                            "::std::option::Option::Some({}({}))",
-                            self.variant_path(),
-                            refv
-                        ),
-                        vtype.into_target(&get_xxx_return_type, "v", &self.customize),
-                    );
-                    w.case_expr("_", self.get_xxx_default_value_rust());
-                })
-            }
-            FieldKind::Singular(..) => {
-                self.write_message_field_get_singular(w);
-            }
-            FieldKind::Repeated(..) | FieldKind::Map(..) => {
-                let self_field = self.self_field();
-                w.write_line(&format!("&{}", self_field));
-            }
-        });
-    }
-
-    fn has_has(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => false,
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithFlag { .. },
-                ..
-            }) => true,
-            FieldKind::Singular(SingularField {
-                flag: SingularFieldFlag::WithoutFlag,
-                ..
-            }) => false,
-            FieldKind::Oneof(..) => true,
-        }
-    }
-
-    fn has_mut(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => true,
-            // TODO: string should be public, and mut is not needed
-            FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
-        }
-    }
-
-    fn has_take(&self) -> bool {
-        match self.kind {
-            FieldKind::Repeated(..) | FieldKind::Map(..) => true,
-            // TODO: string should be public, and mut is not needed
-            FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
-        }
-    }
-
-    fn has_name(&self) -> String {
-        format!("has_{}", self.rust_name)
-    }
-
-    fn write_message_field_has(&self, w: &mut CodeWriter) {
-        w.pub_fn(&format!("{}(&self) -> bool", self.has_name()), |w| {
-            if !self.is_oneof() {
-                let self_field_is_some = self.self_field_is_some();
-                w.write_line(self_field_is_some);
-            } else {
-                let self_field_oneof = self.self_field_oneof();
-                w.match_expr(self_field_oneof, |w| {
-                    w.case_expr(
-                        format!("::std::option::Option::Some({}(..))", self.variant_path()),
-                        "true",
-                    );
-                    w.case_expr("_", "false");
-                });
-            }
-        });
-    }
-
-    fn write_message_field_set(&self, w: &mut CodeWriter) {
-        let set_xxx_param_type = self.set_xxx_param_type();
-        w.comment("Param is passed by value, moved");
-        let ref name = self.rust_name;
-        w.pub_fn(
-            &format!(
-                "set_{}(&mut self, v: {})",
-                name,
-                set_xxx_param_type.to_code(&self.customize)
-            ),
-            |w| {
-                if !self.is_oneof() {
-                    self.write_self_field_assign_value(w, "v", &set_xxx_param_type);
-                } else {
-                    let self_field_oneof = self.self_field_oneof();
-                    let v = set_xxx_param_type.into_target(
-                        &self.oneof().rust_type(),
-                        "v",
-                        &self.customize,
-                    );
-                    w.write_line(&format!(
-                        "{} = ::std::option::Option::Some({}({}))",
-                        self_field_oneof,
-                        self.variant_path(),
-                        v
-                    ));
-                }
-            },
-        );
-    }
-
-    fn write_message_field_mut(&self, w: &mut CodeWriter) {
-        let mut_xxx_return_type = self.mut_xxx_return_type();
-        w.comment("Mutable pointer to the field.");
-        if self.is_singular() {
-            w.comment("If field is not initialized, it is initialized with default value first.");
-        }
-        let fn_def = match mut_xxx_return_type {
-            RustType::Ref(ref param) => format!(
-                "mut_{}(&mut self) -> &mut {}",
-                self.rust_name,
-                param.to_code(&self.customize)
-            ),
-            _ => panic!(
-                "not a ref: {}",
-                mut_xxx_return_type.to_code(&self.customize)
-            ),
-        };
-        w.pub_fn(&fn_def, |w| {
-            match self.kind {
-                FieldKind::Repeated(..) | FieldKind::Map(..) => {
-                    let self_field = self.self_field();
-                    w.write_line(&format!("&mut {}", self_field));
-                }
-                FieldKind::Singular(SingularField {
-                    flag: SingularFieldFlag::WithFlag { .. },
-                    ..
-                }) => {
-                    self.write_if_self_field_is_none(w, |w| {
-                        self.write_self_field_assign_default(w);
-                    });
-                    let self_field = self.self_field();
-                    w.write_line(&format!("{}.as_mut().unwrap()", self_field));
-                }
-                FieldKind::Singular(SingularField {
-                    flag: SingularFieldFlag::WithoutFlag,
-                    ..
-                }) => w.write_line(&format!("&mut {}", self.self_field())),
-                FieldKind::Oneof(..) => {
-                    let self_field_oneof = self.self_field_oneof();
-
-                    // if oneof does not contain current field
-                    w.if_let_else_stmt(
-                        &format!("::std::option::Option::Some({}(_))", self.variant_path())[..],
-                        &self_field_oneof[..],
-                        |w| {
-                            // initialize it with default value
-                            w.write_line(&format!(
-                                "{} = ::std::option::Option::Some({}({}));",
-                                self_field_oneof,
-                                self.variant_path(),
-                                self.element_default_value_rust()
-                                    .into_type(self.oneof().rust_type(), &self.customize)
-                                    .value
-                            ));
-                        },
-                    );
-
-                    // extract field
-                    w.match_expr(self_field_oneof, |w| {
-                        w.case_expr(
-                            format!(
-                                "::std::option::Option::Some({}(ref mut v))",
-                                self.variant_path()
-                            ),
-                            "v",
-                        );
-                        w.case_expr("_", "panic!()");
-                    });
-                }
-            }
-        });
-    }
-
-    fn write_message_field_take_oneof(&self, w: &mut CodeWriter) {
-        let take_xxx_return_type = self.take_xxx_return_type();
-
-        // TODO: replace with if let
-        w.write_line(&format!("if self.{}() {{", self.has_name()));
-        w.indented(|w| {
-            let self_field_oneof = self.self_field_oneof();
-            w.match_expr(format!("{}.take()", self_field_oneof), |w| {
-                let value_in_some = self.oneof().rust_type().value("v".to_owned());
-                let converted =
-                    value_in_some.into_type(self.take_xxx_return_type(), &self.customize);
-                w.case_expr(
-                    format!("::std::option::Option::Some({}(v))", self.variant_path()),
-                    &converted.value,
-                );
-                w.case_expr("_", "panic!()");
-            });
-        });
-        w.write_line("} else {");
-        w.indented(|w| {
-            w.write_line(
-                self.elem()
-                    .rust_storage_type()
-                    .default_value_typed(&self.customize)
-                    .into_type(take_xxx_return_type.clone(), &self.customize)
-                    .value,
-            );
-        });
-        w.write_line("}");
-    }
-
-    fn write_message_field_take(&self, w: &mut CodeWriter) {
-        let take_xxx_return_type = self.take_xxx_return_type();
-        w.comment("Take field");
-        w.pub_fn(
-            &format!(
-                "take_{}(&mut self) -> {}",
-                self.rust_name,
-                take_xxx_return_type.to_code(&self.customize)
-            ),
-            |w| match self.kind {
-                FieldKind::Oneof(..) => {
-                    self.write_message_field_take_oneof(w);
-                }
-                FieldKind::Repeated(..) | FieldKind::Map(..) => {
-                    w.write_line(&format!(
-                        "::std::mem::replace(&mut self.{}, {})",
-                        self.rust_name,
-                        take_xxx_return_type.default_value(&self.customize)
-                    ));
-                }
-                FieldKind::Singular(SingularField {
-                    ref elem,
-                    flag: SingularFieldFlag::WithFlag { .. },
-                }) => {
-                    if !elem.is_copy() {
-                        w.write_line(&format!(
-                            "{}.take().unwrap_or_else(|| {})",
-                            self.self_field(),
-                            elem.rust_storage_type().default_value(&self.customize)
-                        ));
-                    } else {
-                        w.write_line(&format!(
-                            "{}.take().unwrap_or({})",
-                            self.self_field(),
-                            self.element_default_value_rust().value
-                        ));
-                    }
-                }
-                FieldKind::Singular(SingularField {
-                    flag: SingularFieldFlag::WithoutFlag,
-                    ..
-                }) => w.write_line(&format!(
-                    "::std::mem::replace(&mut {}, {})",
-                    self.self_field(),
-                    self.full_storage_type().default_value(&self.customize)
-                )),
-            },
-        );
-    }
-
-    pub fn write_message_single_field_accessors(&self, w: &mut CodeWriter) {
-        // TODO: do not generate `get` when !proto2 and !generate_accessors`
-        w.write_line("");
-        self.write_message_field_get(w);
-
-        if !self.generate_accessors {
-            return;
-        }
-
-        let clear_field_func = self.clear_field_func();
-        w.pub_fn(&format!("{}(&mut self)", clear_field_func), |w| {
-            self.write_clear(w);
-        });
-
-        if self.has_has() {
-            w.write_line("");
-            self.write_message_field_has(w);
-        }
-
-        w.write_line("");
-        self.write_message_field_set(w);
-
-        if self.has_mut() {
-            w.write_line("");
-            self.write_message_field_mut(w);
-        }
-
-        if self.has_take() {
-            w.write_line("");
-            self.write_message_field_take(w);
-        }
-    }
-}
-
-pub(crate) fn rust_field_name_for_protobuf_field_name(name: &str) -> RustIdent {
-    if rust::is_rust_keyword(name) {
-        RustIdent::new(&format!("field_{}", name))
-    } else {
-        RustIdent::new(name)
-    }
-}
diff --git a/src/file.rs b/src/file.rs
deleted file mode 100644
index 413e1bc..0000000
--- a/src/file.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use crate::rust;
-use crate::rust_name::RustIdent;
-use crate::strx;
-
-// Copy-pasted from libsyntax.
-fn ident_start(c: char) -> bool {
-    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
-}
-
-// Copy-pasted from libsyntax.
-fn ident_continue(c: char) -> bool {
-    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'
-}
-
-pub(crate) fn proto_path_to_rust_mod(path: &str) -> RustIdent {
-    let without_dir = strx::remove_to(path, std::path::is_separator);
-    let without_suffix = strx::remove_suffix(without_dir, ".proto");
-
-    let name = without_suffix
-        .chars()
-        .enumerate()
-        .map(|(i, c)| {
-            let valid = if i == 0 {
-                ident_start(c)
-            } else {
-                ident_continue(c)
-            };
-            if valid {
-                c
-            } else {
-                '_'
-            }
-        })
-        .collect::<String>();
-
-    let name = if rust::is_rust_keyword(&name) {
-        format!("{}_pb", name)
-    } else {
-        name
-    };
-    RustIdent::from(name)
-}
-
-#[cfg(test)]
-mod test {
-
-    use super::proto_path_to_rust_mod;
-    use crate::rust_name::RustIdent;
-
-    #[test]
-    fn test_mod_path_proto_ext() {
-        assert_eq!(
-            RustIdent::from("proto"),
-            proto_path_to_rust_mod("proto.proto")
-        );
-    }
-
-    #[test]
-    fn test_mod_path_unknown_ext() {
-        assert_eq!(
-            RustIdent::from("proto_proto3"),
-            proto_path_to_rust_mod("proto.proto3")
-        );
-    }
-
-    #[test]
-    fn test_mod_path_empty_ext() {
-        assert_eq!(RustIdent::from("proto"), proto_path_to_rust_mod("proto"));
-    }
-
-    #[test]
-    fn test_mod_path_dir() {
-        assert_eq!(
-            RustIdent::from("baz"),
-            proto_path_to_rust_mod("foo/bar/baz.proto"),
-        )
-    }
-
-    #[cfg(target_os = "windows")]
-    #[test]
-    fn test_mod_path_dir_backslashes() {
-        assert_eq!(
-            RustIdent::from("baz"),
-            proto_path_to_rust_mod("foo\\bar\\baz.proto"),
-        )
-    }
-}
diff --git a/src/file_descriptor.rs b/src/file_descriptor.rs
deleted file mode 100644
index dde8603..0000000
--- a/src/file_descriptor.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use crate::scope::Scope;
-
-pub(crate) fn file_descriptor_proto_expr(_scope: &Scope) -> String {
-    format!("file_descriptor_proto()")
-}
diff --git a/src/float.rs b/src/float.rs
deleted file mode 100644
index 78ca622..0000000
--- a/src/float.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use std::f64;
-
-#[derive(Debug)]
-pub enum ProtobufFloatParseError {
-    EmptyString,
-    CannotParseFloat,
-}
-
-pub type ProtobufFloatParseResult<T> = Result<T, ProtobufFloatParseError>;
-
-pub const PROTOBUF_NAN: &str = "nan";
-pub const PROTOBUF_INF: &str = "inf";
-
-/// Format float as in protobuf `.proto` files
-pub fn format_protobuf_float(f: f64) -> String {
-    if f.is_nan() {
-        PROTOBUF_NAN.to_owned()
-    } else if f.is_infinite() {
-        if f > 0.0 {
-            format!("{}", PROTOBUF_INF)
-        } else {
-            format!("-{}", PROTOBUF_INF)
-        }
-    } else {
-        let i = f as i64;
-        if i as f64 == f {
-            // Older rust versions did print float without `.0` suffix
-            format!("{:?}.0", i)
-        } else {
-            // TODO: make sure doesn't lose precision
-            format!("{:?}", f)
-        }
-    }
-}
-
-/// Parse float from `.proto` format
-pub fn parse_protobuf_float(s: &str) -> ProtobufFloatParseResult<f64> {
-    if s.is_empty() {
-        return Err(ProtobufFloatParseError::EmptyString);
-    }
-    if s == PROTOBUF_NAN {
-        return Ok(f64::NAN);
-    }
-    if s == PROTOBUF_INF || s == format!("+{}", PROTOBUF_INF) {
-        return Ok(f64::INFINITY);
-    }
-    if s == format!("-{}", PROTOBUF_INF) {
-        return Ok(f64::NEG_INFINITY);
-    }
-    match s.parse() {
-        Ok(f) => Ok(f),
-        Err(_) => Err(ProtobufFloatParseError::CannotParseFloat),
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_format_protobuf_float() {
-        assert_eq!("10.0", format_protobuf_float(10.0));
-        assert_eq!("-10.0", format_protobuf_float(-10.0));
-        assert_eq!("10.5", format_protobuf_float(10.5));
-        assert_eq!("-10.5", format_protobuf_float(-10.5));
-    }
-}
diff --git a/src/gen/all.rs b/src/gen/all.rs
new file mode 100644
index 0000000..312b8db
--- /dev/null
+++ b/src/gen/all.rs
@@ -0,0 +1,64 @@
+use std::collections::HashMap;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtoPath;
+use protobuf_parse::ProtoPathBuf;
+
+use crate::compiler_plugin;
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::CustomizeCallback;
+use crate::gen::file::gen_file;
+use crate::gen::mod_rs::gen_mod_rs;
+use crate::gen::scope::RootScope;
+use crate::gen::well_known_types::gen_well_known_types_mod;
+use crate::Customize;
+
+pub(crate) fn gen_all(
+    file_descriptors: &[FileDescriptorProto],
+    parser: &str,
+    files_to_generate: &[ProtoPathBuf],
+    customize: &Customize,
+    customize_callback: &dyn CustomizeCallback,
+) -> anyhow::Result<Vec<compiler_plugin::GenResult>> {
+    let file_descriptors = FileDescriptor::new_dynamic_fds(file_descriptors.to_vec(), &[])?;
+
+    let root_scope = RootScope {
+        file_descriptors: &file_descriptors,
+    };
+
+    let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
+    let files_map: HashMap<&ProtoPath, &FileDescriptor> = file_descriptors
+        .iter()
+        .map(|f| Ok((ProtoPath::new(f.proto().name())?, f)))
+        .collect::<Result<_, anyhow::Error>>()?;
+
+    let mut mods = Vec::new();
+
+    let customize = CustomizeElemCtx {
+        for_elem: customize.clone(),
+        for_children: customize.clone(),
+        callback: customize_callback,
+    };
+
+    for file_name in files_to_generate {
+        let file = files_map.get(file_name.as_path()).expect(&format!(
+            "file not found in file descriptors: {:?}, files: {:?}",
+            file_name,
+            files_map.keys()
+        ));
+        let gen_file_result = gen_file(file, &files_map, &root_scope, &customize, parser)?;
+        results.push(gen_file_result.compiler_plugin_result);
+        mods.push(gen_file_result.mod_name);
+    }
+
+    if customize.for_elem.inside_protobuf.unwrap_or(false) {
+        results.push(gen_well_known_types_mod());
+    }
+
+    if customize.for_elem.gen_mod_rs.unwrap_or(true) {
+        results.push(gen_mod_rs(&mods));
+    }
+
+    Ok(results)
+}
diff --git a/src/gen/code_writer.rs b/src/gen/code_writer.rs
new file mode 100644
index 0000000..28b3817
--- /dev/null
+++ b/src/gen/code_writer.rs
@@ -0,0 +1,455 @@
+use std::convert::Infallible;
+
+use crate::gen::rust::rel_path::RustRelativePath;
+
+/// Field visibility.
+pub(crate) enum Visibility {
+    Public,
+    Default,
+    Path(RustRelativePath),
+}
+
+pub(crate) struct CodeWriter<'a> {
+    writer: &'a mut String,
+    indent: String,
+}
+
+impl<'a> CodeWriter<'a> {
+    pub(crate) fn new(writer: &'a mut String) -> CodeWriter<'a> {
+        CodeWriter {
+            writer,
+            indent: "".to_string(),
+        }
+    }
+
+    pub(crate) fn with_no_error(f: impl FnOnce(&mut CodeWriter)) -> String {
+        Self::with_impl::<Infallible, _>(|w| Ok(f(w))).unwrap_or_else(|e| match e {})
+    }
+
+    pub(crate) fn with<F>(f: F) -> anyhow::Result<String>
+    where
+        F: FnOnce(&mut CodeWriter) -> anyhow::Result<()>,
+    {
+        Self::with_impl(f)
+    }
+
+    fn with_impl<E, F>(f: F) -> Result<String, E>
+    where
+        F: FnOnce(&mut CodeWriter) -> Result<(), E>,
+    {
+        let mut writer = String::new();
+        {
+            let mut cw = CodeWriter::new(&mut writer);
+            f(&mut cw)?;
+        }
+        Ok(writer)
+    }
+
+    pub(crate) fn write_line<S: AsRef<str>>(&mut self, line: S) {
+        if line.as_ref().is_empty() {
+            self.writer.push_str("\n");
+        } else {
+            let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
+            self.writer.push_str(&s);
+        }
+    }
+
+    pub(crate) fn _write_text(&mut self, text: &str) {
+        for line in text.lines() {
+            self.write_line(line);
+        }
+    }
+
+    pub(crate) fn write_generated_by(&mut self, pkg: &str, version: &str, parser: &str) {
+        self.write_line(format!(
+            "// This file is generated by {pkg} {version}. Do not edit",
+            pkg = pkg,
+            version = version
+        ));
+        self.write_line(format!(
+            "// .proto file is parsed by {parser}",
+            parser = parser
+        ));
+        self.write_generated_common();
+    }
+
+    fn write_generated_common(&mut self) {
+        // https://secure.phabricator.com/T784
+        self.write_line(&format!("// {}generated", "@"));
+
+        self.write_line("");
+        self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
+        self.write_line("#![allow(unknown_lints)]");
+        self.write_line("#![allow(clippy::all)]");
+        self.write_line("");
+        self.write_line("#![allow(unused_attributes)]");
+        self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
+        self.write_line("");
+        self.write_line("#![allow(box_pointers)]");
+        self.write_line("#![allow(dead_code)]");
+        self.write_line("#![allow(missing_docs)]");
+        self.write_line("#![allow(non_camel_case_types)]");
+        self.write_line("#![allow(non_snake_case)]");
+        self.write_line("#![allow(non_upper_case_globals)]");
+        self.write_line("#![allow(trivial_casts)]");
+        self.write_line("#![allow(unused_results)]");
+        self.write_line("#![allow(unused_mut)]");
+    }
+
+    pub(crate) fn unimplemented(&mut self) {
+        self.write_line(format!("unimplemented!();"));
+    }
+
+    pub(crate) fn indented<F>(&mut self, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        cb(&mut CodeWriter {
+            writer: self.writer,
+            indent: format!("{}    ", self.indent),
+        });
+    }
+
+    #[allow(dead_code)]
+    pub(crate) fn commented<F>(&mut self, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        cb(&mut CodeWriter {
+            writer: self.writer,
+            indent: format!("// {}", self.indent),
+        });
+    }
+
+    pub(crate) fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
+        self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
+    }
+
+    pub(crate) fn lazy_static(&mut self, name: &str, ty: &str, protobuf_crate_path: &str) {
+        self.write_line(&format!(
+            "static {}: {}::rt::Lazy<{}> = {}::rt::Lazy::new();",
+            name, protobuf_crate_path, ty, protobuf_crate_path,
+        ));
+    }
+
+    pub(crate) fn lazy_static_decl_get_simple(
+        &mut self,
+        name: &str,
+        ty: &str,
+        init: &str,
+        protobuf_crate_path: &str,
+    ) {
+        self.lazy_static(name, ty, protobuf_crate_path);
+        self.write_line(&format!("{}.get({})", name, init));
+    }
+
+    pub(crate) fn lazy_static_decl_get(
+        &mut self,
+        name: &str,
+        ty: &str,
+        protobuf_crate_path: &str,
+        init: impl FnOnce(&mut CodeWriter),
+    ) {
+        self.lazy_static(name, ty, protobuf_crate_path);
+        self.block(&format!("{}.get(|| {{", name), "})", init);
+    }
+
+    pub(crate) fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.write_line(first_line);
+        self.indented(cb);
+        self.write_line(last_line);
+    }
+
+    pub(crate) fn expr_block<F>(&mut self, prefix: &str, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.block(&format!("{} {{", prefix), "}", cb);
+    }
+
+    pub(crate) fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
+    }
+
+    pub(crate) fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("impl {}", name.as_ref()), cb);
+    }
+
+    pub(crate) fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(
+        &mut self,
+        tr: S1,
+        ty: S2,
+        cb: F,
+    ) where
+        F: Fn(&mut CodeWriter),
+    {
+        self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
+    }
+
+    pub(crate) fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        let args_str = if args.is_empty() {
+            "".to_owned()
+        } else {
+            format!("<{}>", args.join(", "))
+        };
+        self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
+    }
+
+    pub(crate) fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
+    }
+
+    pub(crate) fn pub_enum<F>(&mut self, name: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("pub enum {}", name), cb);
+    }
+
+    pub(crate) fn field_entry(&mut self, name: &str, value: &str) {
+        self.write_line(&format!("{}: {},", name, value));
+    }
+
+    pub(crate) fn field_decl(&mut self, name: &str, field_type: &str) {
+        self.write_line(&format!("{}: {},", name, field_type));
+    }
+
+    pub(crate) fn pub_field_decl(&mut self, name: &str, field_type: &str) {
+        self.write_line(&format!("pub {}: {},", name, field_type));
+    }
+
+    pub(crate) fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
+        match vis {
+            Visibility::Public => self.pub_field_decl(name, field_type),
+            Visibility::Default => self.field_decl(name, field_type),
+            Visibility::Path(..) => unimplemented!(),
+        }
+    }
+
+    pub(crate) fn derive(&mut self, derive: &[&str]) {
+        let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
+        self.write_line(&format!("#[derive({})]", v.join(",")));
+    }
+
+    pub(crate) fn allow(&mut self, what: &[&str]) {
+        let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
+        self.write_line(&format!("#[allow({})]", v.join(",")));
+    }
+
+    pub(crate) fn comment(&mut self, comment: &str) {
+        if comment.is_empty() {
+            self.write_line("//");
+        } else {
+            self.write_line(&format!("// {}", comment));
+        }
+    }
+
+    fn documentation(&mut self, comment: &str) {
+        if comment.is_empty() {
+            self.write_line("///");
+        } else {
+            self.write_line(&format!("/// {}", comment));
+        }
+    }
+
+    pub(crate) fn mod_doc(&mut self, comment: &str) {
+        if comment.is_empty() {
+            self.write_line("//!");
+        } else {
+            self.write_line(&format!("//! {}", comment));
+        }
+    }
+
+    /// Writes the documentation of the given path.
+    ///
+    /// Protobuf paths are defined in proto/google/protobuf/descriptor.proto,
+    /// in the `SourceCodeInfo` message.
+    ///
+    /// For example, say we have a file like:
+    ///
+    /// ```ignore
+    /// message Foo {
+    ///   optional string foo = 1;
+    /// }
+    /// ```
+    ///
+    /// Let's look at just the field definition. We have the following paths:
+    ///
+    /// ```ignore
+    /// path               represents
+    /// [ 4, 0, 2, 0 ]     The whole field definition.
+    /// [ 4, 0, 2, 0, 4 ]  The label (optional).
+    /// [ 4, 0, 2, 0, 5 ]  The type (string).
+    /// [ 4, 0, 2, 0, 1 ]  The name (foo).
+    /// [ 4, 0, 2, 0, 3 ]  The number (1).
+    /// ```
+    ///
+    /// The `4`s can be obtained using simple introspection:
+    ///
+    /// ```
+    /// use protobuf::descriptor::FileDescriptorProto;
+    /// use protobuf::reflect::MessageDescriptor;
+    ///
+    /// let id = MessageDescriptor::for_type::<FileDescriptorProto>()
+    ///     .field_by_name("message_type")
+    ///     .expect("`message_type` must exist")
+    ///     .proto()
+    ///     .number();
+    ///
+    /// assert_eq!(id, 4);
+    /// ```
+    ///
+    /// The first `0` here means this path refers to the first message.
+    ///
+    /// The `2` then refers to the `field` field on the `DescriptorProto` message.
+    ///
+    /// Then comes another `0` to refer to the first field of the current message.
+    ///
+    /// Etc.
+
+    pub(crate) fn all_documentation(
+        &mut self,
+        info: Option<&protobuf::descriptor::SourceCodeInfo>,
+        path: &[i32],
+    ) {
+        let doc = info
+            .map(|v| &v.location)
+            .and_then(|ls| ls.iter().find(|l| l.path == path))
+            .map(|l| l.leading_comments());
+
+        let lines = doc
+            .iter()
+            .map(|doc| doc.lines())
+            .flatten()
+            .collect::<Vec<_>>();
+
+        // Skip comments with code blocks to avoid rustdoc trying to compile them.
+        if !lines.iter().any(|line| line.starts_with("    ")) {
+            for doc in &lines {
+                self.documentation(doc);
+            }
+        }
+    }
+
+    pub(crate) fn fn_block<F>(&mut self, vis: Visibility, sig: &str, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        match vis {
+            Visibility::Public => self.expr_block(&format!("pub fn {}", sig), cb),
+            Visibility::Default => self.expr_block(&format!("fn {}", sig), cb),
+            Visibility::Path(p) if p.is_empty() => self.expr_block(&format!("fn {}", sig), cb),
+            Visibility::Path(p) => self.expr_block(&format!("pub(in {}) fn {}", p, sig), cb),
+        }
+    }
+
+    pub(crate) fn pub_fn<F>(&mut self, sig: &str, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.fn_block(Visibility::Public, sig, cb);
+    }
+
+    pub(crate) fn def_fn<F>(&mut self, sig: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.fn_block(Visibility::Default, sig, cb);
+    }
+
+    pub(crate) fn pub_mod<F>(&mut self, name: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("pub mod {}", name), cb)
+    }
+
+    pub(crate) fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("while {}", cond.as_ref()), cb);
+    }
+
+    // if ... { ... }
+    pub(crate) fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.expr_block(&format!("if {}", cond.as_ref()), cb);
+    }
+
+    // if ... {} else { ... }
+    pub(crate) fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.write_line(&format!("if {} {{", cond.as_ref()));
+        self.write_line("} else {");
+        self.indented(cb);
+        self.write_line("}");
+    }
+
+    // if let ... = ... { ... }
+    pub(crate) fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.if_stmt(&format!("let {} = {}", decl, expr), cb);
+    }
+
+    // if let ... = ... { } else { ... }
+    pub(crate) fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
+    }
+
+    pub(crate) fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
+    }
+
+    pub(crate) fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
+    where
+        F: FnOnce(&mut CodeWriter),
+    {
+        self.stmt_block(&format!("match {}", value.as_ref()), cb);
+    }
+
+    pub(crate) fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.expr_block(&format!("match {}", value.as_ref()), cb);
+    }
+
+    pub(crate) fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
+    }
+
+    pub(crate) fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
+        self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
+    }
+}
diff --git a/src/gen/descriptor.rs b/src/gen/descriptor.rs
new file mode 100644
index 0000000..53ed061
--- /dev/null
+++ b/src/gen/descriptor.rs
@@ -0,0 +1,68 @@
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::MessageDescriptor;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::file_descriptor::file_descriptor_call_expr;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::scope::Scope;
+use crate::Customize;
+
+/// Abstract message or enum descriptor.
+pub(crate) trait Descriptor {
+    const DESCRIPTOR_FN: &'static str;
+    const TYPE_NAME: &'static str;
+    const GET_BY_RELATIVE_NAME_NAME: &'static str;
+    fn name_to_package(&self) -> &str;
+}
+
+impl Descriptor for MessageDescriptor {
+    const DESCRIPTOR_FN: &'static str = "descriptor";
+    const TYPE_NAME: &'static str = "MessageDescriptor";
+    const GET_BY_RELATIVE_NAME_NAME: &'static str = "message_by_package_relative_name";
+
+    fn name_to_package(&self) -> &str {
+        self.name_to_package()
+    }
+}
+
+impl Descriptor for EnumDescriptor {
+    const DESCRIPTOR_FN: &'static str = "enum_descriptor";
+    const TYPE_NAME: &'static str = "EnumDescriptor";
+    const GET_BY_RELATIVE_NAME_NAME: &'static str = "enum_by_package_relative_name";
+
+    fn name_to_package(&self) -> &str {
+        self.name_to_package()
+    }
+}
+
+pub(crate) fn write_fn_descriptor<D: Descriptor>(
+    descriptor: &D,
+    scope: &Scope,
+    customize: &Customize,
+    w: &mut CodeWriter,
+) {
+    let sig = format!(
+        "{}() -> {}::reflect::{}",
+        D::DESCRIPTOR_FN,
+        protobuf_crate_path(customize),
+        D::TYPE_NAME,
+    );
+    w.def_fn(&sig, |w| {
+        let expr = format!(
+            "{}.{}(\"{}\").unwrap()",
+            file_descriptor_call_expr(scope),
+            D::GET_BY_RELATIVE_NAME_NAME,
+            descriptor.name_to_package()
+        );
+        w.lazy_static(
+            "descriptor",
+            &format!(
+                "{}::reflect::{}",
+                protobuf_crate_path(customize),
+                D::TYPE_NAME,
+            ),
+            &protobuf_crate_path(customize).to_string(),
+        );
+        w.write_line(&format!("descriptor.get(|| {}).clone()", expr));
+    });
+}
diff --git a/src/gen/enums.rs b/src/gen/enums.rs
new file mode 100644
index 0000000..69e9e14
--- /dev/null
+++ b/src/gen/enums.rs
@@ -0,0 +1,413 @@
+use std::collections::HashSet;
+
+use protobuf::descriptor::*;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_enum;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::descriptor::write_fn_descriptor;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum_value;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::scope::EnumValueWithContext;
+use crate::gen::scope::EnumWithScope;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+
+#[derive(Clone)]
+pub(crate) struct EnumValueGen<'a> {
+    value: EnumValueWithContext<'a>,
+    enum_rust_name: RustIdentWithPath,
+}
+
+impl<'a> EnumValueGen<'a> {
+    fn parse(
+        value: EnumValueWithContext<'a>,
+        enum_rust_name: &RustIdentWithPath,
+    ) -> EnumValueGen<'a> {
+        EnumValueGen {
+            value: value.clone(),
+            enum_rust_name: enum_rust_name.clone(),
+        }
+    }
+
+    // enum value
+    fn number(&self) -> i32 {
+        self.value.proto.proto().number()
+    }
+
+    // name of enum variant in generated rust code
+    pub fn rust_name_inner(&self) -> RustIdent {
+        self.value.rust_name()
+    }
+
+    pub fn rust_name_outer(&self) -> RustIdentWithPath {
+        self.enum_rust_name
+            .to_path()
+            .with_ident(self.rust_name_inner())
+    }
+}
+
+// Codegen for enum definition
+pub(crate) struct EnumGen<'a> {
+    enum_with_scope: &'a EnumWithScope<'a>,
+    type_name: RustIdentWithPath,
+    lite_runtime: bool,
+    customize: CustomizeElemCtx<'a>,
+    path: &'a [i32],
+    info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> EnumGen<'a> {
+    pub fn new(
+        enum_with_scope: &'a EnumWithScope<'a>,
+        customize: &CustomizeElemCtx<'a>,
+        _root_scope: &RootScope,
+        path: &'a [i32],
+        info: Option<&'a SourceCodeInfo>,
+    ) -> EnumGen<'a> {
+        let customize = customize.child(
+            &customize_from_rustproto_for_enum(enum_with_scope.en.proto().options.get_or_default()),
+            &enum_with_scope.en,
+        );
+        let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+            enum_with_scope
+                .file_descriptor()
+                .proto()
+                .options
+                .optimize_for()
+                == file_options::OptimizeMode::LITE_RUNTIME
+        });
+
+        EnumGen {
+            enum_with_scope,
+            type_name: enum_with_scope.rust_name().to_path(),
+            lite_runtime,
+            customize,
+            path,
+            info,
+        }
+    }
+
+    fn allow_alias(&self) -> bool {
+        self.enum_with_scope
+            .en
+            .proto()
+            .options
+            .get_or_default()
+            .allow_alias()
+    }
+
+    fn values_all(&self) -> Vec<EnumValueGen> {
+        let mut r = Vec::new();
+        for p in self.enum_with_scope.values() {
+            r.push(EnumValueGen::parse(p, &self.type_name));
+        }
+        r
+    }
+
+    fn values_unique(&self) -> Vec<EnumValueGen> {
+        let mut used = HashSet::new();
+        let mut r = Vec::new();
+        for p in self.enum_with_scope.values() {
+            if !used.insert(p.proto.proto().number()) {
+                continue;
+            }
+            r.push(EnumValueGen::parse(p, &self.type_name));
+        }
+        r
+    }
+
+    pub fn write(&self, w: &mut CodeWriter) {
+        self.write_enum(w);
+        if self.allow_alias() {
+            w.write_line("");
+            self.write_impl_eq(w);
+            w.write_line("");
+            self.write_impl_hash(w);
+        }
+        w.write_line("");
+        self.write_impl_enum(w);
+        if !self.lite_runtime {
+            w.write_line("");
+            self.write_impl_enum_full(w);
+        }
+        w.write_line("");
+        self.write_impl_default(w);
+        w.write_line("");
+        self.write_impl_self(w);
+    }
+
+    fn write_impl_self(&self, w: &mut CodeWriter) {
+        if !self.lite_runtime {
+            w.impl_self_block(&format!("{}", self.type_name), |w| {
+                self.write_generated_enum_descriptor_data(w);
+            });
+        }
+    }
+
+    fn write_enum(&self, w: &mut CodeWriter) {
+        w.all_documentation(self.info, self.path);
+
+        let mut derive = Vec::new();
+        derive.push("Clone");
+        derive.push("Copy");
+        if !self.allow_alias() {
+            derive.push("PartialEq");
+        }
+        derive.push("Eq");
+        derive.push("Debug");
+        if !self.allow_alias() {
+            derive.push("Hash");
+        } else {
+            w.comment("Note: you cannot use pattern matching for enums with allow_alias option");
+        }
+        w.derive(&derive);
+        let ref type_name = self.type_name;
+        write_protoc_insertion_point_for_enum(
+            w,
+            &self.customize.for_elem,
+            &self.enum_with_scope.en,
+        );
+        w.expr_block(&format!("pub enum {}", type_name), |w| {
+            for value in self.values_all() {
+                write_protoc_insertion_point_for_enum_value(
+                    w,
+                    &self.customize.for_children,
+                    &value.value.proto,
+                );
+                if self.allow_alias() {
+                    w.write_line(&format!(
+                        "{}, // {}",
+                        value.rust_name_inner(),
+                        value.number()
+                    ));
+                } else {
+                    w.write_line(&format!(
+                        "{} = {},",
+                        value.rust_name_inner(),
+                        value.number()
+                    ));
+                }
+            }
+        });
+    }
+
+    fn write_impl_enum_fn_value(&self, w: &mut CodeWriter) {
+        w.def_fn("value(&self) -> i32", |w| {
+            if self.allow_alias() {
+                w.match_expr("*self", |w| {
+                    for value in self.values_all() {
+                        w.case_expr(
+                            &format!("{}", value.rust_name_outer()),
+                            &format!("{}", value.number()),
+                        );
+                    }
+                });
+            } else {
+                w.write_line("*self as i32")
+            }
+        });
+    }
+
+    fn write_impl_enum_const_name(&self, w: &mut CodeWriter) {
+        w.write_line(&format!(
+            "const NAME: &'static str = \"{}\";",
+            self.enum_with_scope.en.name()
+        ));
+    }
+
+    fn write_impl_enum_fn_from_i32(&self, w: &mut CodeWriter) {
+        w.def_fn(
+            &format!(
+                "from_i32(value: i32) -> ::std::option::Option<{}>",
+                self.type_name
+            ),
+            |w| {
+                w.match_expr("value", |w| {
+                    let values = self.values_unique();
+                    for value in values {
+                        w.write_line(&format!(
+                            "{} => ::std::option::Option::Some({}),",
+                            value.number(),
+                            value.rust_name_outer()
+                        ));
+                    }
+                    w.write_line(&format!("_ => {}", EXPR_NONE));
+                });
+            },
+        );
+    }
+
+    fn write_impl_enum_const_values(&self, w: &mut CodeWriter) {
+        w.write_line(&format!("const VALUES: &'static [{}] = &[", self.type_name));
+        w.indented(|w| {
+            for value in self.values_all() {
+                w.write_line(&format!("{},", value.rust_name_outer()));
+            }
+        });
+        w.write_line("];");
+    }
+
+    fn write_impl_enum(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!("{}::Enum", protobuf_crate_path(&self.customize.for_elem)),
+            &format!("{}", self.type_name),
+            |w| {
+                self.write_impl_enum_const_name(w);
+                w.write_line("");
+                self.write_impl_enum_fn_value(w);
+                w.write_line("");
+                self.write_impl_enum_fn_from_i32(w);
+                w.write_line("");
+                self.write_impl_enum_const_values(w);
+            },
+        );
+    }
+
+    fn write_impl_enum_full(&self, w: &mut CodeWriter) {
+        let ref type_name = self.type_name;
+        w.impl_for_block(
+            &format!(
+                "{}::EnumFull",
+                protobuf_crate_path(&self.customize.for_elem)
+            ),
+            &format!("{}", type_name),
+            |w| {
+                self.write_impl_enum_full_fn_enum_descriptor(w);
+                w.write_line("");
+                self.write_impl_enum_full_fn_descriptor(w);
+            },
+        );
+    }
+
+    fn write_impl_enum_full_fn_enum_descriptor(&self, w: &mut CodeWriter) {
+        write_fn_descriptor(
+            &self.enum_with_scope.en,
+            self.enum_with_scope.scope(),
+            &self.customize.for_elem,
+            w,
+        );
+    }
+
+    fn rust_enum_descriptor_is_enum_index(&self) -> bool {
+        if self.allow_alias() {
+            false
+        } else {
+            self.values_all()
+                .into_iter()
+                .enumerate()
+                .all(|(i, value)| (i as i32) == value.number())
+        }
+    }
+
+    fn write_impl_enum_full_fn_descriptor(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "descriptor(&self) -> {}::reflect::EnumValueDescriptor",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.def_fn(&sig, |w| {
+            if self.rust_enum_descriptor_is_enum_index() {
+                w.write_line("let index = *self as usize;");
+            } else {
+                w.write_line("let index = match self {");
+                w.indented(|w| {
+                    for (i, value) in self.values_all().into_iter().enumerate() {
+                        w.write_line(&format!(
+                            "{}::{} => {},",
+                            self.type_name,
+                            value.rust_name_inner(),
+                            i
+                        ));
+                    }
+                });
+                w.write_line("};");
+            }
+            w.write_line(&format!("Self::enum_descriptor().value_by_index(index)"));
+        });
+    }
+
+    fn write_generated_enum_descriptor_data(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "generated_enum_descriptor_data() -> {}::reflect::GeneratedEnumDescriptorData",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.fn_block(
+            Visibility::Path(
+                self.enum_with_scope
+                    .scope()
+                    .rust_path_to_file()
+                    .to_reverse(),
+            ),
+            &sig,
+            |w| {
+                w.write_line(&format!(
+                    "{}::reflect::GeneratedEnumDescriptorData::new::<{}>(\"{}\")",
+                    protobuf_crate_path(&self.customize.for_elem),
+                    self.type_name,
+                    self.enum_with_scope.name_to_package(),
+                ));
+            },
+        );
+    }
+
+    fn write_impl_eq(&self, w: &mut CodeWriter) {
+        assert!(self.allow_alias());
+        w.impl_for_block(
+            "::std::cmp::PartialEq",
+            &format!("{}", self.type_name),
+            |w| {
+                w.def_fn("eq(&self, other: &Self) -> bool", |w| {
+                    w.write_line(&format!(
+                        "{}::Enum::value(self) == {}::Enum::value(other)",
+                        protobuf_crate_path(&self.customize.for_elem),
+                        protobuf_crate_path(&self.customize.for_elem)
+                    ));
+                });
+            },
+        );
+    }
+
+    fn write_impl_hash(&self, w: &mut CodeWriter) {
+        assert!(self.allow_alias());
+        w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
+            w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
+                w.write_line(&format!(
+                    "state.write_i32({}::Enum::value(self))",
+                    protobuf_crate_path(&self.customize.for_elem)
+                ));
+            });
+        });
+    }
+
+    fn write_impl_default(&self, w: &mut CodeWriter) {
+        let first_value = &self.enum_with_scope.values()[0];
+        if first_value.proto.proto().number() != 0 {
+            // This warning is emitted only for proto2
+            // (because in proto3 first enum variant number is always 0).
+            // `Default` implemented unconditionally to simplify certain
+            // generic operations, e. g. reading a map.
+            // Also, note that even in proto2 some operations fallback to
+            // first enum value, e. g. `get_xxx` for unset field,
+            // so this implementation is not completely unreasonable.
+            w.comment("Note, `Default` is implemented although default value is not 0");
+        }
+        w.impl_for_block(
+            "::std::default::Default",
+            &format!("{}", self.type_name),
+            |w| {
+                w.def_fn("default() -> Self", |w| {
+                    w.write_line(&format!(
+                        "{}::{}",
+                        &self.type_name,
+                        &first_value.rust_name()
+                    ))
+                });
+            },
+        );
+    }
+}
diff --git a/src/gen/extensions.rs b/src/gen/extensions.rs
new file mode 100644
index 0000000..bfd4267
--- /dev/null
+++ b/src/gen/extensions.rs
@@ -0,0 +1,133 @@
+use protobuf::descriptor::*;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::rust_field_name_for_protobuf_field_name;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust_types_values::*;
+use crate::gen::scope::RootScope;
+
+struct ExtGen<'a> {
+    file: &'a FileDescriptor,
+    root_scope: &'a RootScope<'a>,
+    field: &'a FieldDescriptorProto,
+    customize: Customize,
+}
+
+impl<'a> ExtGen<'a> {
+    fn extendee_rust_name(&self) -> RustIdentWithPath {
+        type_name_to_rust_relative(
+            &ProtobufAbsPath::from(self.field.extendee()),
+            &FileAndMod {
+                file: self.file.proto().name().to_owned(),
+                relative_mod: RustRelativePath::from("exts"),
+                customize: self.customize.clone(),
+            },
+            self.root_scope,
+        )
+    }
+
+    fn repeated(&self) -> bool {
+        match self.field.label() {
+            field_descriptor_proto::Label::LABEL_REPEATED => true,
+            field_descriptor_proto::Label::LABEL_OPTIONAL => false,
+            field_descriptor_proto::Label::LABEL_REQUIRED => {
+                panic!("required ext field: {}", self.field.name())
+            }
+        }
+    }
+
+    fn return_type_gen(&self) -> ProtobufTypeGen {
+        if self.field.has_type_name() {
+            let rust_name_relative = type_name_to_rust_relative(
+                &ProtobufAbsPath::from(self.field.type_name()),
+                &FileAndMod {
+                    file: self.file.proto().name().to_owned(),
+                    relative_mod: RustRelativePath::from("exts"),
+                    customize: self.customize.clone(),
+                },
+                self.root_scope,
+            );
+            match self.field.type_() {
+                field_descriptor_proto::Type::TYPE_MESSAGE => {
+                    ProtobufTypeGen::Message(RustTypeMessage(rust_name_relative))
+                }
+                field_descriptor_proto::Type::TYPE_ENUM => {
+                    ProtobufTypeGen::EnumOrUnknown(rust_name_relative)
+                }
+                t => panic!("unknown type: {:?}", t),
+            }
+        } else {
+            ProtobufTypeGen::Primitive(self.field.type_(), PrimitiveTypeVariant::Default)
+        }
+    }
+
+    fn write(&self, w: &mut CodeWriter) {
+        let suffix = if self.repeated() {
+            "ExtFieldRepeated"
+        } else {
+            "ExtFieldOptional"
+        };
+        let field_type = format!(
+            "{protobuf_crate}::ext::{suffix}",
+            protobuf_crate = protobuf_crate_path(&self.customize)
+        );
+        w.pub_const(
+            &rust_field_name_for_protobuf_field_name(self.field.name()).to_string(),
+            &format!(
+                "{field_type}<{extendee}, {rust_type}>",
+                extendee=self.extendee_rust_name(),
+                rust_type=self.return_type_gen().protobuf_value(&self.customize),
+            ),
+            &format!(
+                "{field_type}::new({field_number}, {protobuf_crate}::descriptor::field_descriptor_proto::Type::{t:?})",
+                field_number=self.field.number(),
+                protobuf_crate = protobuf_crate_path(&self.customize),
+                t=self.field.type_(),
+            ),
+        );
+    }
+}
+
+pub(crate) fn write_extensions(
+    file: &FileDescriptor,
+    root_scope: &RootScope,
+    w: &mut CodeWriter,
+    customize: &CustomizeElemCtx,
+) {
+    if file.proto().extension.is_empty() {
+        return;
+    }
+
+    if customize.for_elem.lite_runtime.unwrap_or(false) {
+        w.write_line("");
+        w.comment("Extension generation with lite runtime is not supported");
+        return;
+    }
+
+    w.write_line("");
+    w.write_line("/// Extension fields");
+    w.pub_mod("exts", |w| {
+        for field in &file.proto().extension {
+            if field.type_() == field_descriptor_proto::Type::TYPE_GROUP {
+                continue;
+            }
+
+            w.write_line("");
+            ExtGen {
+                file,
+                root_scope,
+                field,
+                customize: customize.for_elem.clone(),
+            }
+            .write(w);
+        }
+    });
+}
diff --git a/src/gen/field/accessor.rs b/src/gen/field/accessor.rs
new file mode 100644
index 0000000..26f0ef4
--- /dev/null
+++ b/src/gen/field/accessor.rs
@@ -0,0 +1,241 @@
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::elem::FieldElemEnum;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::field::repeated::RepeatedField;
+use crate::gen::field::repeated::RepeatedFieldKind;
+use crate::gen::field::singular::SingularField;
+use crate::gen::field::singular::SingularFieldFlag;
+use crate::gen::field::FieldGen;
+use crate::gen::field::FieldKind;
+use crate::gen::field::MapField;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::oneof::OneofField;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::scope::WithScope;
+
+struct AccessorFn {
+    name: String,
+    // function type params after first underscore
+    type_params: Vec<String>,
+    callback_params: Vec<String>,
+}
+
+impl AccessorFn {
+    fn sig(&self) -> String {
+        let mut s = self.name.clone();
+        s.push_str("::<_");
+        for p in &self.type_params {
+            s.push_str(", ");
+            s.push_str(&p);
+        }
+        s.push_str(">");
+        s
+    }
+}
+
+impl FieldGen<'_> {
+    fn make_accessor_fns_lambda(&self) -> Vec<String> {
+        let message = self.proto_field.message.rust_name();
+        vec![
+            format!("|m: &{}| {{ &m.{} }}", message, self.rust_name),
+            format!("|m: &mut {}| {{ &mut m.{} }}", message, self.rust_name),
+        ]
+    }
+
+    fn make_accessor_fns_has_get_set(&self) -> Vec<String> {
+        let message = self.proto_field.message.rust_name();
+        vec![
+            format!("{}::{}", message, self.has_name()),
+            format!("{}::{}", message, self.rust_name),
+            format!("{}::{}", message, self.set_name()),
+        ]
+    }
+
+    fn make_accessor_fns_has_get_mut_set(&self) -> Vec<String> {
+        let message = self.proto_field.message.rust_name();
+        vec![
+            format!("{}::{}", message, self.has_name()),
+            format!("{}::{}", message, self.rust_name),
+            format!("{}::{}", message, self.mut_name()),
+            format!("{}::{}", message, self.set_name()),
+        ]
+    }
+
+    fn accessor_fn_map(&self, map_field: &MapField) -> AccessorFn {
+        let MapField { .. } = map_field;
+        AccessorFn {
+            name: "make_map_simpler_accessor".to_owned(),
+            type_params: vec![format!("_"), format!("_")],
+            callback_params: self.make_accessor_fns_lambda(),
+        }
+    }
+
+    fn accessor_fn_repeated(&self, repeated_field: &RepeatedField) -> AccessorFn {
+        let RepeatedField { .. } = repeated_field;
+        let name = match repeated_field.kind() {
+            RepeatedFieldKind::Vec => "make_vec_simpler_accessor",
+        };
+        AccessorFn {
+            name: name.to_owned(),
+            type_params: vec![format!("_")],
+            callback_params: self.make_accessor_fns_lambda(),
+        }
+    }
+
+    fn accessor_fn_oneof_enum(&self, oneof: &OneofField, en: &FieldElemEnum) -> AccessorFn {
+        let message = self.proto_field.message.rust_name();
+
+        let variant_path = oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file());
+
+        let getter = CodeWriter::with_no_error(|w| {
+            w.expr_block(
+                &format!(
+                    "|message: &{}| match &message.{}",
+                    message, oneof.oneof_field_name
+                ),
+                |w| {
+                    w.case_expr(
+                        &format!("::std::option::Option::Some({}(e))", variant_path),
+                        "::std::option::Option::Some(*e)",
+                    );
+                    w.case_expr("_", "::std::option::Option::None");
+                },
+            );
+        });
+
+        let setter = CodeWriter::with_no_error(|w| {
+            w.expr_block(
+                &format!(
+                    "|message: &mut {}, e: {}::EnumOrUnknown<{}>|",
+                    message,
+                    protobuf_crate_path(&self.customize),
+                    en.enum_rust_type(&self.file_and_mod())
+                        .to_code(&self.customize)
+                ),
+                |w| {
+                    w.write_line(&format!(
+                        "message.{} = ::std::option::Option::Some({}(e));",
+                        oneof.oneof_field_name, variant_path
+                    ));
+                },
+            )
+        });
+
+        let default = self.xxx_default_value_rust();
+
+        AccessorFn {
+            name: "make_oneof_enum_accessors".to_owned(),
+            type_params: vec![format!("_")],
+            callback_params: vec![getter, setter, default],
+        }
+    }
+
+    fn accessor_fn_singular_without_flag(&self, _elem: &FieldElem) -> AccessorFn {
+        AccessorFn {
+            name: "make_simpler_field_accessor".to_owned(),
+            type_params: vec![format!("_")],
+            callback_params: self.make_accessor_fns_lambda(),
+        }
+    }
+
+    fn accessor_fn_singular_with_flag(
+        &self,
+        elem: &FieldElem,
+        _option_kind: OptionKind,
+    ) -> AccessorFn {
+        match elem {
+            FieldElem::Message(m) => AccessorFn {
+                name: "make_message_field_accessor".to_owned(),
+                type_params: vec![format!("{}", m.rust_name_relative(&self.file_and_mod()))],
+                callback_params: self.make_accessor_fns_lambda(),
+            },
+            FieldElem::Primitive(..) | FieldElem::Enum(..) => AccessorFn {
+                name: "make_option_accessor".to_owned(),
+                type_params: vec!["_".to_owned()],
+                callback_params: self.make_accessor_fns_lambda(),
+            },
+            FieldElem::Group => {
+                unreachable!("no accessor for group field");
+            }
+        }
+    }
+
+    fn accessor_fn_oneof(&self, oneof: &OneofField) -> AccessorFn {
+        let OneofField { ref elem, .. } = oneof;
+
+        let reference = self
+            .proto_field
+            .message
+            .scope
+            .file_and_mod(self.customize.clone());
+
+        if let FieldElem::Enum(en) = &oneof.elem {
+            return self.accessor_fn_oneof_enum(oneof, en);
+        }
+
+        if elem.is_copy() {
+            return AccessorFn {
+                name: "make_oneof_copy_has_get_set_simpler_accessors".to_owned(),
+                type_params: vec![format!("_")],
+                callback_params: self.make_accessor_fns_has_get_set(),
+            };
+        }
+
+        if let RustType::Message(name) = elem.rust_storage_elem_type(&reference) {
+            return AccessorFn {
+                name: "make_oneof_message_has_get_mut_set_accessor".to_owned(),
+                type_params: vec![format!("{}", name)],
+                callback_params: self.make_accessor_fns_has_get_mut_set(),
+            };
+        }
+
+        // string or bytes
+        AccessorFn {
+            name: "make_oneof_deref_has_get_set_simpler_accessor".to_owned(),
+            type_params: vec![format!("_")],
+            callback_params: self.make_accessor_fns_has_get_set(),
+        }
+    }
+
+    fn accessor_fn(&self) -> AccessorFn {
+        match self.kind {
+            FieldKind::Repeated(ref repeated_field) => self.accessor_fn_repeated(repeated_field),
+            FieldKind::Map(ref map_field) => self.accessor_fn_map(map_field),
+            FieldKind::Singular(SingularField {
+                ref elem,
+                flag: SingularFieldFlag::WithoutFlag,
+            }) => self.accessor_fn_singular_without_flag(elem),
+            FieldKind::Singular(SingularField {
+                ref elem,
+                flag: SingularFieldFlag::WithFlag { option_kind, .. },
+            }) => self.accessor_fn_singular_with_flag(elem, option_kind),
+            FieldKind::Oneof(ref oneof) => self.accessor_fn_oneof(oneof),
+        }
+    }
+
+    pub fn write_push_accessor(&self, fields_var: &str, w: &mut CodeWriter) {
+        let accessor_fn = self.accessor_fn();
+        w.write_line(&format!(
+            "{}.push({}::reflect::rt::v2::{}(",
+            fields_var,
+            protobuf_crate_path(&self.customize),
+            accessor_fn.sig()
+        ));
+        w.indented(|w| {
+            w.write_line(&format!("\"{}\",", self.proto_field.name()));
+            for callback in &accessor_fn.callback_params {
+                let callback_lines: Vec<&str> = callback.lines().collect();
+                for (i, callback_line) in callback_lines.iter().enumerate() {
+                    let comma = if i == callback_lines.len() - 1 {
+                        ","
+                    } else {
+                        ""
+                    };
+                    w.write_line(&format!("{}{}", callback_line, comma));
+                }
+            }
+        });
+        w.write_line("));");
+    }
+}
diff --git a/src/gen/field/elem.rs b/src/gen/field/elem.rs
new file mode 100644
index 0000000..fd06b4c
--- /dev/null
+++ b/src/gen/field/elem.rs
@@ -0,0 +1,319 @@
+use protobuf::descriptor::field_descriptor_proto::Type;
+use protobuf::reflect::RuntimeFieldType;
+use protobuf::rt::tag_size;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust_types_values::message_or_enum_to_rust_relative;
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::rust_types_values::RustValueTyped;
+use crate::gen::scope::EnumValueWithContext;
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageOrEnumWithScope;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+use crate::Customize;
+
+#[derive(Clone, Debug)]
+pub(crate) struct FieldElemEnum<'a> {
+    /// Enum default value variant, either from proto or from enum definition
+    default_value: EnumValueWithContext<'a>,
+}
+
+impl<'a> FieldElemEnum<'a> {
+    fn rust_name_relative(&self, reference: &FileAndMod) -> RustIdentWithPath {
+        message_or_enum_to_rust_relative(&self.default_value.en, reference)
+    }
+
+    pub(crate) fn enum_rust_type(&self, reference: &FileAndMod) -> RustType {
+        RustType::Enum(
+            self.rust_name_relative(reference),
+            self.default_value.rust_name(),
+            self.default_value.proto.proto().number(),
+        )
+    }
+
+    fn enum_or_unknown_rust_type(&self, reference: &FileAndMod) -> RustType {
+        RustType::EnumOrUnknown(
+            self.rust_name_relative(reference),
+            self.default_value.rust_name(),
+            self.default_value.proto.proto().number(),
+        )
+    }
+
+    pub(crate) fn default_value_rust_expr(&self, reference: &FileAndMod) -> RustIdentWithPath {
+        self.rust_name_relative(reference)
+            .to_path()
+            .with_ident(self.default_value.rust_name())
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FieldElemMessage<'a> {
+    pub message: MessageWithScope<'a>,
+}
+
+impl<'a> FieldElemMessage<'a> {
+    pub(crate) fn rust_name_relative(&self, reference: &FileAndMod) -> RustTypeMessage {
+        RustTypeMessage(message_or_enum_to_rust_relative(&self.message, reference))
+    }
+
+    fn rust_type(&self, reference: &FileAndMod) -> RustType {
+        RustType::Message(self.rust_name_relative(reference))
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum FieldElem<'a> {
+    Primitive(Type, PrimitiveTypeVariant),
+    Message(FieldElemMessage<'a>),
+    Enum(FieldElemEnum<'a>),
+    Group,
+}
+
+pub(crate) enum HowToGetMessageSize {
+    Compute,
+    GetCached,
+}
+
+impl<'a> FieldElem<'a> {
+    pub(crate) fn proto_type(&self) -> Type {
+        match *self {
+            FieldElem::Primitive(t, ..) => t,
+            FieldElem::Group => Type::TYPE_GROUP,
+            FieldElem::Message(..) => Type::TYPE_MESSAGE,
+            FieldElem::Enum(..) => Type::TYPE_ENUM,
+        }
+    }
+
+    pub(crate) fn is_copy(&self) -> bool {
+        self.proto_type().is_copy()
+    }
+
+    pub(crate) fn rust_storage_elem_type(&self, reference: &FileAndMod) -> RustType {
+        match *self {
+            FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => t.rust_type(),
+            FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+                RustType::Chars
+            }
+            FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+                RustType::Bytes
+            }
+            FieldElem::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(),
+            FieldElem::Group => RustType::Group,
+            FieldElem::Message(ref m) => m.rust_type(reference),
+            FieldElem::Enum(ref en) => en.enum_or_unknown_rust_type(reference),
+        }
+    }
+
+    // Type of set_xxx function parameter type for singular fields
+    pub(crate) fn rust_set_xxx_param_type(&self, reference: &FileAndMod) -> RustType {
+        if let FieldElem::Enum(ref en) = *self {
+            en.enum_rust_type(reference)
+        } else {
+            self.rust_storage_elem_type(reference)
+        }
+    }
+
+    pub(crate) fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
+        match self {
+            &FieldElem::Primitive(_, v) => v,
+            _ => PrimitiveTypeVariant::Default,
+        }
+    }
+
+    pub(crate) fn singular_field_size(
+        &self,
+        field_number: u32,
+        var: &RustValueTyped,
+        customize: &Customize,
+    ) -> String {
+        let tag_size = tag_size(field_number);
+        match self.proto_type().encoded_size() {
+            Some(data_size) => format!("{tag_size} + {data_size}"),
+            None => match self.proto_type() {
+                Type::TYPE_MESSAGE => panic!("not a single-liner"),
+                // We are not inlining `bytes_size` here,
+                // assuming the compiler is smart enough to do it for us.
+                // https://rust.godbolt.org/z/GrKa5zxq6
+                Type::TYPE_BYTES => format!(
+                    "{}::rt::bytes_size({}, &{})",
+                    protobuf_crate_path(customize),
+                    field_number,
+                    var.value
+                ),
+                Type::TYPE_STRING => format!(
+                    "{}::rt::string_size({}, &{})",
+                    protobuf_crate_path(customize),
+                    field_number,
+                    var.value
+                ),
+                Type::TYPE_ENUM => {
+                    format!(
+                        "{}::rt::int32_size({}, {}.value())",
+                        protobuf_crate_path(customize),
+                        field_number,
+                        var.value,
+                    )
+                }
+                _ => {
+                    let param_type = match &var.rust_type {
+                        RustType::Ref(t) => (**t).clone(),
+                        t => t.clone(),
+                    };
+                    let f = match self.proto_type() {
+                        Type::TYPE_SINT32 => "sint32_size",
+                        Type::TYPE_SINT64 => "sint64_size",
+                        Type::TYPE_INT32 => "int32_size",
+                        Type::TYPE_INT64 => "int64_size",
+                        Type::TYPE_UINT32 => "uint32_size",
+                        Type::TYPE_UINT64 => "uint64_size",
+                        t => unreachable!("unexpected type: {:?}", t),
+                    };
+                    format!(
+                        "{}::rt::{f}({}, {})",
+                        protobuf_crate_path(customize),
+                        field_number,
+                        var.into_type(param_type, customize).value
+                    )
+                }
+            },
+        }
+    }
+
+    pub(crate) fn write_element_size(
+        &self,
+        field_number: u32,
+        item_var: &RustValueTyped,
+        how_to_get_message_size: HowToGetMessageSize,
+        sum_var: &str,
+        customize: &Customize,
+        w: &mut CodeWriter,
+    ) {
+        let tag_size = tag_size(field_number);
+        match self.proto_type() {
+            Type::TYPE_MESSAGE => {
+                match how_to_get_message_size {
+                    HowToGetMessageSize::Compute => {
+                        w.write_line(&format!("let len = {}.compute_size();", item_var.value))
+                    }
+                    HowToGetMessageSize::GetCached => w.write_line(&format!(
+                        "let len = {}.cached_size() as u64;",
+                        item_var.value
+                    )),
+                }
+                w.write_line(&format!(
+                    "{sum_var} += {tag_size} + {}::rt::compute_raw_varint64_size(len) + len;",
+                    protobuf_crate_path(customize),
+                ));
+            }
+            _ => {
+                w.write_line(&format!(
+                    "{sum_var} += {};",
+                    self.singular_field_size(field_number, item_var, customize)
+                ));
+            }
+        }
+    }
+
+    pub(crate) fn write_write_element(
+        &self,
+        field_number: u32,
+        v: &RustValueTyped,
+        file_and_mod: &FileAndMod,
+        customize: &Customize,
+        os: &str,
+        w: &mut CodeWriter,
+    ) {
+        match self.proto_type() {
+            Type::TYPE_MESSAGE => {
+                let param_type = RustType::Ref(Box::new(self.rust_storage_elem_type(file_and_mod)));
+
+                w.write_line(&format!(
+                    "{}::rt::write_message_field_with_cached_size({}, {}, {})?;",
+                    protobuf_crate_path(customize),
+                    field_number,
+                    v.into_type(param_type, customize).value,
+                    os
+                ));
+            }
+            _ => {
+                let param_type = self.proto_type().os_write_fn_param_type();
+                let os_write_fn_suffix = self.proto_type().protobuf_name();
+                w.write_line(&format!(
+                    "{}.write_{}({}, {})?;",
+                    os,
+                    os_write_fn_suffix,
+                    field_number,
+                    v.into_type(param_type, customize).value
+                ));
+            }
+        }
+    }
+
+    pub(crate) fn read_one_liner(&self) -> String {
+        format!(
+            "{}?",
+            self.proto_type().read("is", self.primitive_type_variant())
+        )
+    }
+}
+
+pub(crate) fn field_elem<'a>(
+    field: &FieldWithContext,
+    root_scope: &'a RootScope<'a>,
+    customize: &Customize,
+) -> FieldElem<'a> {
+    if let RuntimeFieldType::Map(..) = field.field.runtime_field_type() {
+        unreachable!();
+    }
+
+    if field.field.proto().type_() == Type::TYPE_GROUP {
+        FieldElem::Group
+    } else if field.field.proto().has_type_name() {
+        let message_or_enum = root_scope
+            .find_message_or_enum(&ProtobufAbsPath::from(field.field.proto().type_name()));
+        match (field.field.proto().type_(), message_or_enum) {
+            (Type::TYPE_MESSAGE, MessageOrEnumWithScope::Message(message)) => {
+                FieldElem::Message(FieldElemMessage {
+                    message: message.clone(),
+                })
+            }
+            (Type::TYPE_ENUM, MessageOrEnumWithScope::Enum(enum_with_scope)) => {
+                let default_value = if field.field.proto().has_default_value() {
+                    enum_with_scope.value_by_name(field.field.proto().default_value())
+                } else {
+                    enum_with_scope.values()[0].clone()
+                };
+                FieldElem::Enum(FieldElemEnum { default_value })
+            }
+            _ => panic!("unknown named type: {:?}", field.field.proto().type_()),
+        }
+    } else if field.field.proto().has_type() {
+        let tokio_for_bytes = customize.tokio_bytes.unwrap_or(false);
+        let tokio_for_string = customize.tokio_bytes_for_string.unwrap_or(false);
+
+        let elem = match field.field.proto().type_() {
+            Type::TYPE_STRING if tokio_for_string => {
+                FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes)
+            }
+            Type::TYPE_BYTES if tokio_for_bytes => {
+                FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes)
+            }
+            t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default),
+        };
+
+        elem
+    } else {
+        panic!(
+            "neither type_name, nor field_type specified for field: {}",
+            field.field.name()
+        );
+    }
+}
diff --git a/src/gen/field/mod.rs b/src/gen/field/mod.rs
new file mode 100644
index 0000000..d6a5e44
--- /dev/null
+++ b/src/gen/field/mod.rs
@@ -0,0 +1,1850 @@
+mod accessor;
+pub(crate) mod elem;
+mod option_kind;
+mod repeated;
+mod singular;
+mod tag;
+pub(crate) mod type_ext;
+
+use protobuf::descriptor::field_descriptor_proto::Type;
+use protobuf::descriptor::*;
+use protobuf::reflect::ReflectValueRef;
+use protobuf::reflect::RuntimeFieldType;
+use protobuf::reflect::Syntax;
+use protobuf::rt;
+use protobuf::rt::WireType;
+use protobuf_parse::camel_case;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_field;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::field::elem::field_elem;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::elem::FieldElemEnum;
+use crate::gen::field::elem::HowToGetMessageSize;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::field::repeated::RepeatedField;
+use crate::gen::field::singular::SingularField;
+use crate::gen::field::singular::SingularFieldFlag;
+use crate::gen::field::tag::make_tag;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::map::map_entry;
+use crate::gen::oneof::OneofField;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::quote::quote_escape_bytes;
+use crate::gen::rust::quote::quote_escape_str;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::rust_types_values::RustValueTyped;
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+
+fn field_type_protobuf_name<'a>(field: &'a FieldDescriptorProto) -> &'a str {
+    if field.has_type_name() {
+        field.type_name()
+    } else {
+        field.type_().protobuf_name()
+    }
+}
+
+#[derive(Clone)]
+pub struct MapField<'a> {
+    _message: MessageWithScope<'a>,
+    key: FieldElem<'a>,
+    value: FieldElem<'a>,
+}
+
+#[derive(Clone)]
+pub(crate) enum FieldKind<'a> {
+    // optional or required
+    Singular(SingularField<'a>),
+    // repeated except map
+    Repeated(RepeatedField<'a>),
+    // map
+    Map(MapField<'a>),
+    // part of oneof
+    Oneof(OneofField<'a>),
+}
+
+impl<'a> FieldKind<'a> {
+    pub(crate) fn default(
+        &self,
+        customize: &Customize,
+        reference: &FileAndMod,
+        const_expr: bool,
+    ) -> String {
+        match self {
+            FieldKind::Singular(s) => s.default_value(customize, reference, const_expr),
+            FieldKind::Repeated(r) => r.default(),
+            FieldKind::Oneof(..) => EXPR_NONE.to_owned(),
+            FieldKind::Map(..) => panic!("map fields cannot have field value"),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub(crate) enum SingularOrOneofField<'a> {
+    Singular(SingularField<'a>),
+    Oneof(OneofField<'a>),
+}
+
+impl<'a> SingularOrOneofField<'a> {
+    fn elem(&self) -> &FieldElem {
+        match self {
+            SingularOrOneofField::Singular(SingularField { ref elem, .. }) => elem,
+            SingularOrOneofField::Oneof(OneofField { ref elem, .. }) => elem,
+        }
+    }
+
+    // Type of `xxx` function for singular type.
+    pub(crate) fn getter_return_type(&self, reference: &FileAndMod) -> RustType {
+        let elem = self.elem();
+        if let FieldElem::Enum(ref en) = elem {
+            en.enum_rust_type(reference)
+        } else if elem.is_copy() {
+            elem.rust_storage_elem_type(reference)
+        } else {
+            elem.rust_storage_elem_type(reference).ref_type()
+        }
+    }
+}
+
+// Representation of map entry: key type and value type
+#[derive(Clone, Debug)]
+pub struct EntryKeyValue<'a>(FieldElem<'a>, FieldElem<'a>);
+
+#[derive(Clone)]
+pub(crate) struct FieldGen<'a> {
+    syntax: Syntax,
+    pub proto_field: FieldWithContext<'a>,
+    // field name in generated code
+    pub rust_name: RustIdent,
+    pub proto_type: Type,
+    wire_type: WireType,
+    pub kind: FieldKind<'a>,
+    pub generate_accessors: bool,
+    pub generate_getter: bool,
+    customize: Customize,
+    path: Vec<i32>,
+    info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> FieldGen<'a> {
+    pub(crate) fn parse(
+        field: FieldWithContext<'a>,
+        root_scope: &'a RootScope<'a>,
+        parent_customize: &CustomizeElemCtx<'a>,
+        path: Vec<i32>,
+        info: Option<&'a SourceCodeInfo>,
+    ) -> anyhow::Result<FieldGen<'a>> {
+        let customize = parent_customize
+            .child(
+                &customize_from_rustproto_for_field(field.field.proto().options.get_or_default()),
+                &field.field,
+            )
+            .for_elem;
+
+        let syntax = field.message.scope.file_scope.syntax();
+
+        let field_may_have_custom_default_value = syntax == Syntax::Proto2
+            && field.field.proto().label() != field_descriptor_proto::Label::LABEL_REPEATED
+            && field.field.proto().type_() != Type::TYPE_MESSAGE;
+
+        let generate_accessors = customize
+            .generate_accessors
+            .unwrap_or(field_may_have_custom_default_value)
+            || field.is_oneof();
+
+        let default_generate_getter = generate_accessors || field_may_have_custom_default_value;
+        let generate_getter =
+            customize.generate_getter.unwrap_or(default_generate_getter) || field.is_oneof();
+
+        let kind = match field.field.runtime_field_type() {
+            RuntimeFieldType::Map(..) => {
+                let message = root_scope
+                    .find_message(&ProtobufAbsPath::from(field.field.proto().type_name()));
+
+                let (key, value) = map_entry(&message).unwrap();
+
+                let key = field_elem(&key, root_scope, &customize);
+                let value = field_elem(&value, root_scope, &customize);
+
+                FieldKind::Map(MapField {
+                    _message: message,
+                    key,
+                    value,
+                })
+            }
+            RuntimeFieldType::Repeated(..) => {
+                let elem = field_elem(&field, root_scope, &customize);
+
+                FieldKind::Repeated(RepeatedField {
+                    elem,
+                    packed: field.field.proto().options.get_or_default().packed(),
+                })
+            }
+            RuntimeFieldType::Singular(..) => {
+                let elem = field_elem(&field, root_scope, &customize);
+
+                if let Some(oneof) = field.oneof() {
+                    FieldKind::Oneof(OneofField::parse(&oneof, &field.field, elem, root_scope))
+                } else {
+                    let flag = if field.message.scope.file_scope.syntax() == Syntax::Proto3
+                        && field.field.proto().type_() != field_descriptor_proto::Type::TYPE_MESSAGE
+                        && !field.field.proto().proto3_optional()
+                    {
+                        SingularFieldFlag::WithoutFlag
+                    } else {
+                        let required = field.field.proto().label()
+                            == field_descriptor_proto::Label::LABEL_REQUIRED;
+                        let option_kind = match field.field.proto().type_() {
+                            field_descriptor_proto::Type::TYPE_MESSAGE => OptionKind::MessageField,
+                            _ => OptionKind::Option,
+                        };
+
+                        SingularFieldFlag::WithFlag {
+                            required,
+                            option_kind,
+                        }
+                    };
+                    FieldKind::Singular(SingularField { elem, flag })
+                }
+            }
+        };
+
+        Ok(FieldGen {
+            syntax: field.message.message.file_descriptor().syntax(),
+            rust_name: rust_field_name_for_protobuf_field_name(&field.field.name()),
+            proto_type: field.field.proto().type_(),
+            wire_type: WireType::for_type(field.field.proto().type_()),
+            proto_field: field,
+            kind,
+            generate_accessors,
+            generate_getter,
+            customize,
+            path,
+            info,
+        })
+    }
+
+    // for message level
+    fn file_and_mod(&self) -> FileAndMod {
+        self.proto_field
+            .message
+            .scope
+            .file_and_mod(self.customize.clone())
+    }
+
+    fn tag_size(&self) -> u32 {
+        rt::tag_size(self.proto_field.number() as u32) as u32
+    }
+
+    fn is_singular(&self) -> bool {
+        match self.kind {
+            FieldKind::Singular(..) => true,
+            _ => false,
+        }
+    }
+
+    fn is_repeated_packed(&self) -> bool {
+        match self.kind {
+            FieldKind::Repeated(RepeatedField { packed: true, .. }) => true,
+            _ => false,
+        }
+    }
+
+    pub(crate) fn elem(&self) -> &FieldElem {
+        match self.kind {
+            FieldKind::Singular(SingularField { ref elem, .. }) => &elem,
+            FieldKind::Repeated(RepeatedField { ref elem, .. }) => &elem,
+            FieldKind::Oneof(OneofField { ref elem, .. }) => &elem,
+            FieldKind::Map(..) => unreachable!(),
+        }
+    }
+
+    // type of field in struct
+    pub(crate) fn full_storage_type(&self, reference: &FileAndMod) -> RustType {
+        match self.kind {
+            FieldKind::Repeated(ref repeated) => repeated.rust_type(reference),
+            FieldKind::Map(MapField {
+                ref key, ref value, ..
+            }) => RustType::HashMap(
+                Box::new(key.rust_storage_elem_type(reference)),
+                Box::new(value.rust_storage_elem_type(reference)),
+            ),
+            FieldKind::Singular(ref singular) => singular.rust_storage_type(reference),
+            FieldKind::Oneof(..) => unreachable!(),
+        }
+    }
+
+    // type of `v` in `for v in field`
+    fn full_storage_iter_elem_type(&self, reference: &FileAndMod) -> RustType {
+        if let FieldKind::Oneof(ref oneof) = self.kind {
+            oneof.elem.rust_storage_elem_type(reference)
+        } else {
+            self.full_storage_type(reference).iter_elem_type()
+        }
+    }
+
+    // suffix `xxx` as in `os.write_xxx_no_tag(..)`
+    fn os_write_fn_suffix(&self) -> &str {
+        self.proto_type.protobuf_name()
+    }
+
+    fn os_write_fn_suffix_with_unknown_for_enum(&self) -> &str {
+        if self.proto_type == field_descriptor_proto::Type::TYPE_ENUM {
+            "enum_or_unknown"
+        } else {
+            self.os_write_fn_suffix()
+        }
+    }
+
+    // for field `foo`, type of param of `fn set_foo(..)`
+    fn set_xxx_param_type(&self, reference: &FileAndMod) -> RustType {
+        match self.kind {
+            FieldKind::Singular(SingularField { ref elem, .. })
+            | FieldKind::Oneof(OneofField { ref elem, .. }) => {
+                elem.rust_set_xxx_param_type(reference)
+            }
+            FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference),
+        }
+    }
+
+    // for field `foo`, return type if `fn take_foo(..)`
+    fn take_xxx_return_type(&self, reference: &FileAndMod) -> RustType {
+        self.set_xxx_param_type(reference)
+    }
+
+    // for field `foo`, return type of `fn mut_foo(..)`
+    fn mut_xxx_return_type(&self, reference: &FileAndMod) -> RustType {
+        RustType::Ref(Box::new(match self.kind {
+            FieldKind::Singular(SingularField { ref elem, .. })
+            | FieldKind::Oneof(OneofField { ref elem, .. }) => {
+                elem.rust_storage_elem_type(reference)
+            }
+            FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference),
+        }))
+    }
+
+    // for field `foo`, return type of `fn foo(..)`
+    fn getter_return_type(&self) -> RustType {
+        let reference = self
+            .proto_field
+            .message
+            .scope
+            .file_and_mod(self.customize.clone());
+        match &self.kind {
+            FieldKind::Singular(s) => {
+                SingularOrOneofField::Singular(s.clone()).getter_return_type(&reference)
+            }
+            FieldKind::Oneof(o) => {
+                SingularOrOneofField::Oneof(o.clone()).getter_return_type(&reference)
+            }
+            FieldKind::Repeated(RepeatedField { ref elem, .. }) => RustType::Ref(Box::new(
+                RustType::Slice(Box::new(elem.rust_storage_elem_type(&reference))),
+            )),
+            FieldKind::Map(..) => RustType::Ref(Box::new(self.full_storage_type(&reference))),
+        }
+    }
+
+    // elem data is not stored in heap
+    pub(crate) fn elem_type_is_copy(&self) -> bool {
+        self.proto_type.is_copy()
+    }
+
+    fn defaut_value_from_proto_float(f: f64, type_name: &str) -> String {
+        if f.is_nan() {
+            format!("::std::{}::NAN", type_name)
+        } else if f.is_infinite() {
+            if f > 0.0 {
+                format!("::std::{}::INFINITY", type_name)
+            } else {
+                format!("::std::{}::NEG_INFINITY", type_name)
+            }
+        } else {
+            format!("{:?}{}", f, type_name)
+        }
+    }
+
+    fn singular_or_oneof_default_value_from_proto(&self, elem: &FieldElem) -> Option<String> {
+        if !self.proto_field.field.proto().has_default_value() {
+            return None;
+        }
+
+        let default_value = self.proto_field.field.singular_default_value();
+        Some(match default_value {
+            ReflectValueRef::Bool(b) => format!("{}", b),
+            ReflectValueRef::I32(v) => format!("{}i32", v),
+            ReflectValueRef::I64(v) => format!("{}i64", v),
+            ReflectValueRef::U32(v) => format!("{}u32", v),
+            ReflectValueRef::U64(v) => format!("{}u64", v),
+            ReflectValueRef::String(v) => quote_escape_str(v),
+            ReflectValueRef::Bytes(v) => quote_escape_bytes(v),
+            ReflectValueRef::F32(v) => Self::defaut_value_from_proto_float(v as f64, "f32"),
+            ReflectValueRef::F64(v) => Self::defaut_value_from_proto_float(v as f64, "f64"),
+            ReflectValueRef::Enum(_e, _v) => {
+                if let &FieldElem::Enum(ref e) = elem {
+                    format!("{}", e.default_value_rust_expr(&self.file_and_mod()))
+                } else {
+                    unreachable!()
+                }
+            }
+            t => panic!("default value is not implemented for type: {:?}", t),
+        })
+    }
+
+    fn default_value_from_proto(&self) -> Option<String> {
+        match self.kind {
+            FieldKind::Oneof(OneofField { ref elem, .. })
+            | FieldKind::Singular(SingularField { ref elem, .. }) => {
+                self.singular_or_oneof_default_value_from_proto(elem)
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    fn default_value_from_proto_typed(&self) -> Option<RustValueTyped> {
+        self.default_value_from_proto().map(|v| {
+            let default_value_type = match self.proto_type {
+                field_descriptor_proto::Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
+                field_descriptor_proto::Type::TYPE_BYTES => {
+                    RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::u8()))))
+                }
+                _ => self.full_storage_iter_elem_type(
+                    &self
+                        .proto_field
+                        .message
+                        .scope
+                        .file_and_mod(self.customize.clone()),
+                ),
+            };
+
+            RustValueTyped {
+                value: v,
+                rust_type: default_value_type,
+            }
+        })
+    }
+
+    // default value to be returned from `fn xxx` for field `xxx`.
+    fn xxx_default_value_rust(&self) -> String {
+        match self.kind {
+            FieldKind::Singular(..) | FieldKind::Oneof(..) => {
+                self.default_value_from_proto().unwrap_or_else(|| {
+                    self.getter_return_type()
+                        .default_value(&self.customize, false)
+                })
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    // default to be assigned to field
+    fn element_default_value_rust(&self) -> RustValueTyped {
+        match self.kind {
+            FieldKind::Singular(..) | FieldKind::Oneof(..) => {
+                self.default_value_from_proto_typed().unwrap_or_else(|| {
+                    self.elem()
+                        .rust_storage_elem_type(
+                            &self
+                                .proto_field
+                                .message
+                                .scope
+                                .file_and_mod(self.customize.clone()),
+                        )
+                        .default_value_typed(&self.customize, false)
+                })
+            }
+            _ => unreachable!(),
+        }
+    }
+
+    pub(crate) fn reconstruct_def(&self) -> String {
+        let prefix = match (self.proto_field.field.proto().label(), self.syntax) {
+            (field_descriptor_proto::Label::LABEL_REPEATED, _) => "repeated ",
+            (_, Syntax::Proto3) => "",
+            (field_descriptor_proto::Label::LABEL_OPTIONAL, _) => "optional ",
+            (field_descriptor_proto::Label::LABEL_REQUIRED, _) => "required ",
+        };
+        format!(
+            "{}{} {} = {}",
+            prefix,
+            field_type_protobuf_name(self.proto_field.field.proto()),
+            self.proto_field.name(),
+            self.proto_field.number()
+        )
+    }
+
+    pub(crate) fn write_clear(&self, w: &mut CodeWriter) {
+        match self.kind {
+            FieldKind::Oneof(ref o) => {
+                w.write_line(&format!(
+                    "self.{} = ::std::option::Option::None;",
+                    o.oneof_field_name
+                ));
+            }
+            _ => {
+                let clear_expr = self
+                    .full_storage_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .clear(&self.self_field(), &self.customize);
+                w.write_line(&format!("{};", clear_expr));
+            }
+        }
+    }
+
+    // output code that writes single element to stream
+    pub(crate) fn write_write_element(
+        &self,
+        elem: &FieldElem,
+        w: &mut CodeWriter,
+        os: &str,
+        v: &RustValueTyped,
+    ) {
+        assert!(!self.is_repeated_packed());
+
+        elem.write_write_element(
+            self.proto_field.number() as u32,
+            v,
+            &self.file_and_mod(),
+            &self.customize,
+            os,
+            w,
+        );
+    }
+
+    fn self_field(&self) -> String {
+        format!("self.{}", self.rust_name)
+    }
+
+    fn self_field_is_some(&self) -> String {
+        assert!(self.is_singular());
+        format!("{}.is_some()", self.self_field())
+    }
+
+    fn self_field_is_none(&self) -> String {
+        assert!(self.is_singular());
+        format!("{}.is_none()", self.self_field())
+    }
+
+    // field data viewed as Option
+    fn self_field_as_option(&self, elem: &FieldElem, option_kind: OptionKind) -> RustValueTyped {
+        match self.full_storage_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        ) {
+            RustType::Option(ref e) if e.is_copy() => {
+                return RustType::Option(e.clone()).value(self.self_field());
+            }
+            _ => {}
+        };
+
+        let as_option_type = option_kind.as_ref_type(
+            elem.rust_storage_elem_type(
+                &self
+                    .proto_field
+                    .message
+                    .scope
+                    .file_and_mod(self.customize.clone()),
+            ),
+        );
+
+        as_option_type.value(format!("{}.as_ref()", self.self_field()))
+    }
+
+    pub(crate) fn write_struct_field(&self, w: &mut CodeWriter) {
+        if self.proto_type == field_descriptor_proto::Type::TYPE_GROUP {
+            w.comment(&format!("{}: <group>", &self.rust_name));
+        } else {
+            w.all_documentation(self.info, &self.path);
+
+            write_protoc_insertion_point_for_field(w, &self.customize, &self.proto_field.field);
+            w.field_decl_vis(
+                Visibility::Public,
+                &self.rust_name.to_string(),
+                &self
+                    .full_storage_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .to_code(&self.customize),
+            );
+        }
+    }
+
+    fn write_if_let_self_field_is_some<F>(&self, s: &SingularField, w: &mut CodeWriter, cb: F)
+    where
+        F: Fn(&RustValueTyped, &mut CodeWriter),
+    {
+        match s {
+            SingularField {
+                flag: SingularFieldFlag::WithFlag { option_kind, .. },
+                ref elem,
+            } => {
+                let var = "v";
+                let ref_prefix = match elem
+                    .rust_storage_elem_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .is_copy()
+                {
+                    true => "",
+                    false => "",
+                };
+                let as_option = self.self_field_as_option(elem, *option_kind);
+                w.if_let_stmt(
+                    &format!("Some({}{})", ref_prefix, var),
+                    &as_option.value,
+                    |w| {
+                        let v = RustValueTyped {
+                            value: var.to_owned(),
+                            rust_type: as_option.rust_type.elem_type(),
+                        };
+                        cb(&v, w);
+                    },
+                );
+            }
+            SingularField {
+                flag: SingularFieldFlag::WithoutFlag,
+                ref elem,
+            } => match *elem {
+                FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..)
+                | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => {
+                    w.if_stmt(format!("!{}.is_empty()", self.self_field()), |w| {
+                        let v = RustValueTyped {
+                            value: self.self_field(),
+                            rust_type: self.full_storage_type(
+                                &self
+                                    .proto_field
+                                    .message
+                                    .scope
+                                    .file_and_mod(self.customize.clone()),
+                            ),
+                        };
+                        cb(&v, w);
+                    });
+                }
+                _ => {
+                    w.if_stmt(
+                        format!(
+                            "{} != {}",
+                            self.self_field(),
+                            self.full_storage_type(
+                                &self
+                                    .proto_field
+                                    .message
+                                    .scope
+                                    .file_and_mod(self.customize.clone())
+                            )
+                            .default_value(&self.customize, false)
+                        ),
+                        |w| {
+                            let v = RustValueTyped {
+                                value: self.self_field(),
+                                rust_type: self.full_storage_type(
+                                    &self
+                                        .proto_field
+                                        .message
+                                        .scope
+                                        .file_and_mod(self.customize.clone()),
+                                ),
+                            };
+                            cb(&v, w);
+                        },
+                    );
+                }
+            },
+        }
+    }
+
+    pub(crate) fn write_if_self_field_is_none<F>(&self, w: &mut CodeWriter, cb: F)
+    where
+        F: Fn(&mut CodeWriter),
+    {
+        let self_field_is_none = self.self_field_is_none();
+        w.if_stmt(self_field_is_none, cb)
+    }
+
+    // repeated or singular
+    pub(crate) fn write_for_self_field<F>(&self, w: &mut CodeWriter, varn: &str, cb: F)
+    where
+        F: Fn(&mut CodeWriter, &RustType),
+    {
+        let file_and_mod = self
+            .proto_field
+            .message
+            .scope
+            .file_and_mod(self.customize.clone());
+
+        match &self.kind {
+            FieldKind::Oneof(oneof_field) => {
+                let cond = format!(
+                    "Some({}(ref {}))",
+                    oneof_field.variant_path(&file_and_mod.relative_mod),
+                    varn
+                );
+                w.if_let_stmt(
+                    &cond,
+                    &format!("self.{}", oneof_field.oneof_field_name),
+                    |w| cb(w, &oneof_field.elem.rust_storage_elem_type(&file_and_mod)),
+                )
+            }
+            _ => {
+                let v_type = self.full_storage_iter_elem_type(&file_and_mod);
+                let self_field = self.self_field();
+                w.for_stmt(&format!("&{}", self_field), varn, |w| cb(w, &v_type));
+            }
+        }
+    }
+
+    fn write_self_field_assign(&self, w: &mut CodeWriter, value: &str) {
+        let self_field = self.self_field();
+        w.write_line(&format!("{} = {};", self_field, value));
+    }
+
+    fn write_self_field_assign_some(&self, w: &mut CodeWriter, s: &SingularField, value: &str) {
+        match s {
+            &SingularField {
+                flag: SingularFieldFlag::WithFlag { option_kind, .. },
+                ..
+            } => {
+                self.write_self_field_assign(w, &option_kind.wrap_value(value, &self.customize));
+            }
+            &SingularField {
+                flag: SingularFieldFlag::WithoutFlag,
+                ..
+            } => {
+                self.write_self_field_assign(w, value);
+            }
+        }
+    }
+
+    fn write_self_field_assign_value_singular(
+        &self,
+        w: &mut CodeWriter,
+        s: &SingularField,
+        value: &RustValueTyped,
+    ) {
+        let SingularField { ref elem, ref flag } = s;
+        let converted = value.into_type(
+            elem.rust_storage_elem_type(
+                &self
+                    .proto_field
+                    .message
+                    .scope
+                    .file_and_mod(self.customize.clone()),
+            )
+            .clone(),
+            &self.customize,
+        );
+        let wrapped = match flag {
+            SingularFieldFlag::WithoutFlag => converted.value,
+            SingularFieldFlag::WithFlag { option_kind, .. } => {
+                option_kind.wrap_value(&converted.value, &self.customize)
+            }
+        };
+        self.write_self_field_assign(w, &wrapped);
+    }
+
+    fn write_self_field_assign_value(&self, w: &mut CodeWriter, value: &RustValueTyped) {
+        match self.kind {
+            FieldKind::Repeated(..) | FieldKind::Map(..) => {
+                let converted = value.into_type(
+                    self.full_storage_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    ),
+                    &self.customize,
+                );
+                self.write_self_field_assign(w, &converted.value);
+            }
+            FieldKind::Singular(ref s) => {
+                self.write_self_field_assign_value_singular(w, s, value);
+            }
+            FieldKind::Oneof(..) => unreachable!(),
+        }
+    }
+
+    fn write_self_field_assign_default(
+        &self,
+        field_kind: &SingularOrOneofField,
+        w: &mut CodeWriter,
+    ) {
+        match field_kind {
+            SingularOrOneofField::Oneof(oneof) => {
+                w.write_line(format!(
+                    "self.{} = ::std::option::Option::Some({}({}))",
+                    oneof.oneof_field_name,
+                    oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+                    // TODO: default from .proto is not needed here (?)
+                    self.element_default_value_rust()
+                        .into_type(
+                            self.full_storage_iter_elem_type(
+                                &self
+                                    .proto_field
+                                    .message
+                                    .scope
+                                    .file_and_mod(self.customize.clone())
+                            ),
+                            &self.customize
+                        )
+                        .value
+                ));
+            }
+            SingularOrOneofField::Singular(singular) => {
+                // Note it is different from C++ protobuf, where field is initialized
+                // with default value
+                match singular.flag {
+                    SingularFieldFlag::WithFlag { option_kind, .. } => match option_kind {
+                        OptionKind::MessageField => {
+                            let self_field = self.self_field();
+                            w.write_line(&format!("{}.set_default();", self_field));
+                        }
+                        _ => {
+                            self.write_self_field_assign_some(
+                                w,
+                                singular,
+                                &self
+                                    .elem()
+                                    .rust_storage_elem_type(
+                                        &self
+                                            .proto_field
+                                            .message
+                                            .scope
+                                            .file_and_mod(self.customize.clone()),
+                                    )
+                                    .default_value_typed(&self.customize, false)
+                                    .into_type(
+                                        singular.elem.rust_storage_elem_type(
+                                            &self
+                                                .proto_field
+                                                .message
+                                                .scope
+                                                .file_and_mod(self.customize.clone()),
+                                        ),
+                                        &self.customize,
+                                    )
+                                    .value,
+                            );
+                        }
+                    },
+                    SingularFieldFlag::WithoutFlag => unimplemented!(),
+                }
+            }
+        }
+    }
+
+    fn self_field_vec_packed_size(&self) -> String {
+        let fn_name = match self.proto_type {
+            Type::TYPE_ENUM => "vec_packed_enum_or_unknown_size",
+            Type::TYPE_SINT32 => "vec_packed_sint32_size",
+            Type::TYPE_SINT64 => "vec_packed_sint64_size",
+            Type::TYPE_INT32 => "vec_packed_int32_size",
+            Type::TYPE_INT64 => "vec_packed_int64_size",
+            Type::TYPE_UINT32 => "vec_packed_uint32_size",
+            Type::TYPE_UINT64 => "vec_packed_uint64_size",
+            Type::TYPE_BOOL => "vec_packed_bool_size",
+            Type::TYPE_FIXED32 => "vec_packed_fixed32_size",
+            Type::TYPE_FIXED64 => "vec_packed_fixed64_size",
+            Type::TYPE_SFIXED32 => "vec_packed_sfixed32_size",
+            Type::TYPE_SFIXED64 => "vec_packed_sfixed64_size",
+            Type::TYPE_FLOAT => "vec_packed_float_size",
+            Type::TYPE_DOUBLE => "vec_packed_double_size",
+            t => unreachable!("{:?}", t),
+        };
+        format!(
+            "{}::rt::{fn_name}({}, &{})",
+            protobuf_crate_path(&self.customize),
+            self.proto_field.number(),
+            self.self_field()
+        )
+    }
+
+    pub(crate) fn clear_field_func(&self) -> String {
+        format!("clear_{}", self.rust_name)
+    }
+
+    fn write_merge_from_field_message_string_bytes_repeated(
+        &self,
+        r: &RepeatedField,
+        w: &mut CodeWriter,
+    ) {
+        let read_fn = match &r.elem {
+            FieldElem::Message(..) => "read_message",
+            FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::Default) => "read_string",
+            FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+                "read_tokio_chars"
+            }
+            FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::Default) => "read_bytes",
+            FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+                "read_tokio_bytes"
+            }
+            _ => unreachable!("for field {}", self.proto_field.field),
+        };
+        w.write_line(&format!("self.{}.push(is.{}()?);", self.rust_name, read_fn,));
+    }
+
+    fn tag_with_wire_type(&self, wire_type: WireType) -> u32 {
+        make_tag(self.proto_field.number() as u32, wire_type)
+    }
+
+    fn tag(&self) -> u32 {
+        self.tag_with_wire_type(self.wire_type)
+    }
+
+    // Write `merge_from` part for this oneof field
+    fn write_merge_from_oneof_case_block(&self, o: &OneofField, w: &mut CodeWriter) {
+        w.case_block(&format!("{}", self.tag()), |w| {
+            let typed = RustValueTyped {
+                value: format!(
+                    "{}?",
+                    self.proto_type.read("is", o.elem.primitive_type_variant())
+                ),
+                rust_type: self.full_storage_iter_elem_type(
+                    &self
+                        .proto_field
+                        .message
+                        .scope
+                        .file_and_mod(self.customize.clone()),
+                ),
+            };
+
+            let maybe_boxed = if o.boxed {
+                typed.boxed(&self.customize)
+            } else {
+                typed
+            };
+
+            w.write_line(&format!(
+                "self.{} = ::std::option::Option::Some({}({}));",
+                o.oneof_field_name,
+                o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+                maybe_boxed.value
+            ));
+        })
+    }
+
+    // Write `merge_from` part for this map field
+    fn write_merge_from_map_case_block(&self, map: &MapField, w: &mut CodeWriter) {
+        let MapField { key, value, .. } = map;
+        w.case_block(&format!("{}", self.tag()), |w| {
+            w.write_line(&format!("let len = is.read_raw_varint32()?;",));
+            w.write_line(&format!("let old_limit = is.push_limit(len as u64)?;"));
+            w.write_line(&format!(
+                "let mut key = ::std::default::Default::default();"
+            ));
+            w.write_line(&format!(
+                "let mut value = ::std::default::Default::default();"
+            ));
+            w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| {
+                w.match_block("tag", |w| {
+                    let key_tag = make_tag(1, WireType::for_type(key.proto_type()));
+                    let value_tag = make_tag(2, WireType::for_type(value.proto_type()));
+                    w.case_expr(
+                        &format!("{key_tag}"),
+                        &format!("key = {read}", read = key.read_one_liner()),
+                    );
+                    w.case_expr(
+                        &format!("{value_tag}"),
+                        &format!("value = {read}", read = value.read_one_liner()),
+                    );
+                    w.case_expr(
+                        "_",
+                        &format!(
+                            "{protobuf_crate}::rt::skip_field_for_tag(tag, is)?",
+                            protobuf_crate = protobuf_crate_path(&self.customize)
+                        ),
+                    );
+                });
+            });
+            w.write_line(&format!("is.pop_limit(old_limit);"));
+            w.write_line(&format!(
+                "{field}.insert(key, value);",
+                field = self.self_field()
+            ));
+        });
+    }
+
+    // Write `merge_from` part for this singular field
+    fn write_merge_from_singular_case_block(&self, s: &SingularField, w: &mut CodeWriter) {
+        w.case_block(&format!("{}", self.tag()), |w| match s.elem {
+            FieldElem::Message(..) => {
+                w.write_line(&format!(
+                    "{}::rt::read_singular_message_into_field(is, &mut self.{})?;",
+                    protobuf_crate_path(&self.customize),
+                    self.rust_name,
+                ));
+            }
+            _ => {
+                let read_proc = s.elem.read_one_liner();
+                self.write_self_field_assign_some(w, s, &read_proc);
+            }
+        })
+    }
+
+    // Write `merge_from` part for this repeated field
+    fn write_merge_from_repeated_case_block(&self, w: &mut CodeWriter) {
+        let field = match self.kind {
+            FieldKind::Repeated(ref field) => field,
+            _ => panic!(),
+        };
+
+        match field.elem {
+            FieldElem::Message(..)
+            | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..)
+            | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => {
+                w.case_block(&format!("{}", self.tag()), |w| {
+                    self.write_merge_from_field_message_string_bytes_repeated(field, w);
+                })
+            }
+            FieldElem::Enum(..) => {
+                w.case_block(
+                    &format!("{}", self.tag_with_wire_type(WireType::Varint)),
+                    |w| {
+                        w.write_line(&format!(
+                            "self.{}.push(is.read_enum_or_unknown()?);",
+                            self.rust_name,
+                        ));
+                    },
+                );
+                w.case_block(
+                    &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)),
+                    |w| {
+                        w.write_line(&format!(
+                            "{}::rt::read_repeated_packed_enum_or_unknown_into(is, &mut self.{})?",
+                            protobuf_crate_path(&self.customize),
+                            self.rust_name,
+                        ));
+                    },
+                );
+            }
+            _ => {
+                assert_ne!(self.wire_type, WireType::LengthDelimited);
+                w.case_block(
+                    &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)),
+                    |w| {
+                        w.write_line(&format!(
+                            "is.read_repeated_packed_{}_into(&mut self.{})?;",
+                            self.proto_type.protobuf_name(),
+                            self.rust_name
+                        ));
+                    },
+                );
+                w.case_block(&format!("{}", self.tag()), |w| {
+                    w.write_line(&format!(
+                        "self.{}.push(is.read_{}()?);",
+                        self.rust_name,
+                        self.proto_type.protobuf_name(),
+                    ));
+                });
+            }
+        }
+    }
+
+    /// Write `merge_from` part for this field
+    pub(crate) fn write_merge_from_field_case_block(&self, w: &mut CodeWriter) {
+        match &self.kind {
+            FieldKind::Oneof(oneof) => self.write_merge_from_oneof_case_block(oneof, w),
+            FieldKind::Map(map) => self.write_merge_from_map_case_block(map, w),
+            FieldKind::Singular(ref s) => self.write_merge_from_singular_case_block(s, w),
+            FieldKind::Repeated(..) => self.write_merge_from_repeated_case_block(w),
+        }
+    }
+
+    pub(crate) fn write_element_size(
+        &self,
+        elem: &FieldElem,
+        w: &mut CodeWriter,
+        item_var: &RustValueTyped,
+        sum_var: &str,
+    ) {
+        assert!(!self.is_repeated_packed());
+
+        elem.write_element_size(
+            self.proto_field.number() as u32,
+            item_var,
+            HowToGetMessageSize::Compute,
+            sum_var,
+            &self.customize,
+            w,
+        );
+    }
+
+    fn write_write_map_field(
+        &self,
+        key: &FieldElem,
+        value: &FieldElem,
+        os: &str,
+        w: &mut CodeWriter,
+    ) {
+        self.for_each_map_entry(key, value, w, |k, v, w| {
+            w.write_line("let mut entry_size = 0;");
+            key.write_element_size(
+                1,
+                k,
+                HowToGetMessageSize::GetCached,
+                "entry_size",
+                &self.customize,
+                w,
+            );
+            value.write_element_size(
+                2,
+                v,
+                HowToGetMessageSize::GetCached,
+                "entry_size",
+                &self.customize,
+                w,
+            );
+            w.write_line(&format!(
+                "{os}.write_raw_varint32({tag})?; // Tag.",
+                tag = make_tag(self.proto_field.number() as u32, WireType::LengthDelimited),
+            ));
+            w.write_line(&format!("{os}.write_raw_varint32(entry_size as u32)?;",));
+            key.write_write_element(1, k, &self.file_and_mod(), &self.customize, os, w);
+            value.write_write_element(2, v, &self.file_and_mod(), &self.customize, os, w);
+        });
+    }
+
+    pub(crate) fn write_message_write_field(&self, os: &str, w: &mut CodeWriter) {
+        match &self.kind {
+            FieldKind::Singular(s @ SingularField { elem, .. }) => {
+                self.write_if_let_self_field_is_some(s, w, |v, w| {
+                    self.write_write_element(&elem, w, os, &v);
+                });
+            }
+            FieldKind::Repeated(RepeatedField {
+                packed: false,
+                elem,
+                ..
+            }) => {
+                self.write_for_self_field(w, "v", |w, v_type| {
+                    let v = RustValueTyped {
+                        value: "v".to_owned(),
+                        rust_type: v_type.clone(),
+                    };
+                    self.write_write_element(elem, w, "os", &v);
+                });
+            }
+            FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
+                w.write_line(&format!(
+                    "os.write_repeated_packed_{}({}, &{})?;",
+                    self.os_write_fn_suffix_with_unknown_for_enum(),
+                    self.proto_field.number(),
+                    self.self_field()
+                ));
+            }
+            FieldKind::Map(MapField { key, value, .. }) => {
+                self.write_write_map_field(key, value, os, w)
+            }
+            FieldKind::Oneof(..) => unreachable!(),
+        };
+    }
+
+    fn for_each_map_entry(
+        &self,
+        key: &FieldElem,
+        value: &FieldElem,
+        w: &mut CodeWriter,
+        cb: impl FnOnce(&RustValueTyped, &RustValueTyped, &mut CodeWriter),
+    ) {
+        w.for_stmt(&format!("&{}", self.self_field()), "(k, v)", move |w| {
+            let k = RustValueTyped {
+                value: "k".to_owned(),
+                rust_type: key.rust_storage_elem_type(&self.file_and_mod()).wrap_ref(),
+            };
+            let v = RustValueTyped {
+                value: "v".to_owned(),
+                rust_type: value
+                    .rust_storage_elem_type(&self.file_and_mod())
+                    .wrap_ref(),
+            };
+            cb(&k, &v, w)
+        });
+    }
+
+    fn write_compute_map_field_size(
+        &self,
+        sum_var: &str,
+        key: &FieldElem<'a>,
+        value: &FieldElem<'a>,
+        w: &mut CodeWriter,
+    ) {
+        self.for_each_map_entry(key, value, w, |k, v, w| {
+                w.write_line("let mut entry_size = 0;");
+                key.write_element_size(1, k, HowToGetMessageSize::Compute, "entry_size", &self.customize, w);
+                value.write_element_size(2, v, HowToGetMessageSize::Compute, "entry_size", &self.customize, w);
+                w.write_line(&format!("{sum_var} += {tag_size} + {protobuf_crate}::rt::compute_raw_varint64_size(entry_size) + entry_size",
+                    tag_size = self.tag_size(),
+                    protobuf_crate = protobuf_crate_path(&self.customize),
+                ));
+        });
+    }
+
+    pub(crate) fn write_message_compute_field_size(&self, sum_var: &str, w: &mut CodeWriter) {
+        match &self.kind {
+            FieldKind::Singular(s @ SingularField { elem, .. }) => {
+                self.write_if_let_self_field_is_some(s, w, |v, w| {
+                    self.write_element_size(&elem, w, v, sum_var)
+                });
+            }
+            FieldKind::Repeated(RepeatedField {
+                packed: false,
+                elem,
+                ..
+            }) => {
+                match elem.proto_type().encoded_size() {
+                    Some(s) => {
+                        let tag_size = self.tag_size();
+                        let self_field = self.self_field();
+                        w.write_line(&format!(
+                            "{} += {} * {}.len() as u64;",
+                            sum_var,
+                            (s + tag_size) as isize,
+                            self_field
+                        ));
+                    }
+                    None => {
+                        self.write_for_self_field(w, "value", |w, value_type| {
+                            self.write_element_size(
+                                elem,
+                                w,
+                                &RustValueTyped {
+                                    value: "value".to_owned(),
+                                    rust_type: value_type.clone(),
+                                },
+                                sum_var,
+                            );
+                        });
+                    }
+                };
+            }
+            FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
+                let size_expr = self.self_field_vec_packed_size();
+                w.write_line(&format!("{} += {};", sum_var, size_expr));
+            }
+            FieldKind::Map(MapField { key, value, .. }) => {
+                self.write_compute_map_field_size(sum_var, key, value, w)
+            }
+            FieldKind::Oneof(..) => unreachable!(),
+        }
+    }
+
+    fn write_message_field_get_singular_message(&self, s: &SingularField, w: &mut CodeWriter) {
+        match s.flag {
+            SingularFieldFlag::WithoutFlag => unimplemented!(),
+            SingularFieldFlag::WithFlag { option_kind, .. } => {
+                let self_field = self.self_field();
+                let ref field_type_name = self.elem().rust_storage_elem_type(
+                    &self
+                        .proto_field
+                        .message
+                        .scope
+                        .file_and_mod(self.customize.clone()),
+                );
+                w.write_line(option_kind.unwrap_ref_or_else(
+                    &format!("{}.as_ref()", self_field),
+                    &format!(
+                        "<{} as {}::Message>::default_instance()",
+                        field_type_name.to_code(&self.customize),
+                        protobuf_crate_path(&self.customize),
+                    ),
+                ));
+            }
+        }
+    }
+
+    fn write_message_field_get_singular_enum(
+        &self,
+        flag: SingularFieldFlag,
+        _elem: &FieldElemEnum,
+        w: &mut CodeWriter,
+    ) {
+        match flag {
+            SingularFieldFlag::WithoutFlag => {
+                w.write_line(&format!("self.{}.enum_value_or_default()", self.rust_name));
+            }
+            SingularFieldFlag::WithFlag { .. } => {
+                w.match_expr(&self.self_field(), |w| {
+                    let default_value = self.xxx_default_value_rust();
+                    w.case_expr("Some(e)", &format!("e.enum_value_or({})", default_value));
+                    w.case_expr("None", &format!("{}", default_value));
+                });
+            }
+        }
+    }
+
+    fn write_message_field_get_singular(&self, singular: &SingularField, w: &mut CodeWriter) {
+        let get_xxx_return_type = self.getter_return_type();
+
+        match singular.elem {
+            FieldElem::Message(..) => self.write_message_field_get_singular_message(singular, w),
+            FieldElem::Enum(ref en) => {
+                self.write_message_field_get_singular_enum(singular.flag, en, w)
+            }
+            _ => {
+                let get_xxx_default_value_rust = self.xxx_default_value_rust();
+                let self_field = self.self_field();
+                match singular {
+                    &SingularField {
+                        ref elem,
+                        flag: SingularFieldFlag::WithFlag { option_kind, .. },
+                        ..
+                    } => {
+                        if get_xxx_return_type.is_ref().is_some() {
+                            let as_option = self.self_field_as_option(elem, option_kind);
+                            w.match_expr(&as_option.value, |w| {
+                                let v_type = as_option.rust_type.elem_type();
+                                let r_type = self.getter_return_type();
+                                w.case_expr(
+                                    "Some(v)",
+                                    v_type.into_target(&r_type, "v", &self.customize),
+                                );
+                                let get_xxx_default_value_rust = self.xxx_default_value_rust();
+                                w.case_expr("None", get_xxx_default_value_rust);
+                            });
+                        } else {
+                            w.write_line(&format!(
+                                "{}.unwrap_or({})",
+                                self_field, get_xxx_default_value_rust
+                            ));
+                        }
+                    }
+                    &SingularField {
+                        flag: SingularFieldFlag::WithoutFlag,
+                        ..
+                    } => {
+                        w.write_line(
+                            self.full_storage_type(
+                                &self
+                                    .proto_field
+                                    .message
+                                    .scope
+                                    .file_and_mod(self.customize.clone()),
+                            )
+                            .into_target(
+                                &get_xxx_return_type,
+                                &self_field,
+                                &self.customize,
+                            ),
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn write_message_field_get_oneof(&self, o: &OneofField, w: &mut CodeWriter) {
+        let get_xxx_return_type = SingularOrOneofField::Oneof(o.clone()).getter_return_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        );
+        let OneofField { ref elem, .. } = o;
+        w.match_expr(&format!("self.{}", o.oneof_field_name), |w| {
+            let (refv, vtype) = if !elem.is_copy() {
+                (
+                    "ref v",
+                    elem.rust_storage_elem_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .ref_type(),
+                )
+            } else {
+                (
+                    "v",
+                    elem.rust_storage_elem_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    ),
+                )
+            };
+            w.case_expr(
+                format!(
+                    "::std::option::Option::Some({}({}))",
+                    o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+                    refv
+                ),
+                vtype.into_target(&get_xxx_return_type, "v", &self.customize),
+            );
+            w.case_expr("_", self.xxx_default_value_rust());
+        });
+    }
+
+    fn write_message_field_get(&self, w: &mut CodeWriter) {
+        let get_xxx_return_type = self.getter_return_type();
+        let fn_def = format!(
+            "{}(&self) -> {}",
+            self.rust_name,
+            get_xxx_return_type.to_code(&self.customize)
+        );
+
+        w.pub_fn(&fn_def, |w| match self.kind {
+            FieldKind::Oneof(ref o) => {
+                self.write_message_field_get_oneof(o, w);
+            }
+            FieldKind::Singular(ref s) => {
+                self.write_message_field_get_singular(s, w);
+            }
+            FieldKind::Repeated(..) | FieldKind::Map(..) => {
+                let self_field = self.self_field();
+                w.write_line(&format!("&{}", self_field));
+            }
+        });
+    }
+
+    fn has_has(&self) -> bool {
+        match self.kind {
+            FieldKind::Repeated(..) | FieldKind::Map(..) => false,
+            FieldKind::Singular(SingularField {
+                flag: SingularFieldFlag::WithFlag { .. },
+                ..
+            }) => true,
+            FieldKind::Singular(SingularField {
+                flag: SingularFieldFlag::WithoutFlag,
+                ..
+            }) => false,
+            FieldKind::Oneof(..) => true,
+        }
+    }
+
+    fn has_mut(&self) -> bool {
+        match self.kind {
+            FieldKind::Repeated(..) | FieldKind::Map(..) => true,
+            // TODO: string should be public, and mut is not needed
+            FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
+        }
+    }
+
+    fn has_take(&self) -> bool {
+        match self.kind {
+            FieldKind::Repeated(..) | FieldKind::Map(..) => true,
+            // TODO: string should be public, and mut is not needed
+            FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
+        }
+    }
+
+    fn has_name(&self) -> RustIdent {
+        RustIdent::new(&format!("has_{}", self.rust_name.get()))
+    }
+
+    fn set_name(&self) -> RustIdent {
+        RustIdent::new(&format!("set_{}", self.rust_name.get()))
+    }
+
+    fn mut_name(&self) -> RustIdent {
+        RustIdent::new(&format!("mut_{}", self.rust_name.get()))
+    }
+
+    fn write_message_field_has(&self, w: &mut CodeWriter) {
+        w.pub_fn(
+            &format!("{}(&self) -> bool", self.has_name()),
+            |w| match self.kind {
+                FieldKind::Oneof(ref oneof) => {
+                    w.match_expr(&format!("self.{}", oneof.oneof_field_name), |w| {
+                        w.case_expr(
+                            format!(
+                                "::std::option::Option::Some({}(..))",
+                                oneof.variant_path(
+                                    &self.proto_field.message.scope.rust_path_to_file()
+                                )
+                            ),
+                            "true",
+                        );
+                        w.case_expr("_", "false");
+                    });
+                }
+                _ => {
+                    let self_field_is_some = self.self_field_is_some();
+                    w.write_line(self_field_is_some);
+                }
+            },
+        );
+    }
+
+    fn write_message_field_set(&self, w: &mut CodeWriter) {
+        let set_xxx_param_type = self.set_xxx_param_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        );
+        w.comment("Param is passed by value, moved");
+        w.pub_fn(
+            &format!(
+                "{}(&mut self, v: {})",
+                self.set_name(),
+                set_xxx_param_type.to_code(&self.customize)
+            ),
+            |w| {
+                let value_typed = RustValueTyped {
+                    value: "v".to_owned(),
+                    rust_type: set_xxx_param_type.clone(),
+                };
+                match self.kind {
+                    FieldKind::Oneof(ref oneof) => {
+                        let v = set_xxx_param_type.into_target(
+                            &oneof.rust_type(
+                                &self
+                                    .proto_field
+                                    .message
+                                    .scope
+                                    .file_and_mod(self.customize.clone()),
+                            ),
+                            "v",
+                            &self.customize,
+                        );
+                        w.write_line(&format!(
+                            "self.{} = ::std::option::Option::Some({}({}))",
+                            oneof.oneof_field_name,
+                            oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+                            v
+                        ));
+                    }
+                    _ => {
+                        self.write_self_field_assign_value(w, &value_typed);
+                    }
+                }
+            },
+        );
+    }
+
+    fn write_message_field_mut_singular_with_flag(
+        &self,
+        s: &SingularField,
+        option_kind: OptionKind,
+        w: &mut CodeWriter,
+    ) {
+        let self_field = self.self_field();
+        match option_kind {
+            OptionKind::MessageField => {
+                w.write_line(&format!("{}.mut_or_insert_default()", self_field))
+            }
+            OptionKind::Option => {
+                self.write_if_self_field_is_none(w, |w| {
+                    self.write_self_field_assign_default(
+                        &SingularOrOneofField::Singular(s.clone()),
+                        w,
+                    );
+                });
+                w.write_line(&format!("{}.as_mut().unwrap()", self_field));
+            }
+        }
+    }
+
+    fn write_message_field_mut_singular(&self, s: &SingularField, w: &mut CodeWriter) {
+        match s {
+            s @ SingularField {
+                flag: SingularFieldFlag::WithFlag { option_kind, .. },
+                ..
+            } => self.write_message_field_mut_singular_with_flag(s, *option_kind, w),
+            SingularField {
+                flag: SingularFieldFlag::WithoutFlag,
+                ..
+            } => w.write_line(&format!("&mut {}", self.self_field())),
+        }
+    }
+
+    fn write_message_field_mut(&self, w: &mut CodeWriter) {
+        let mut_xxx_return_type = self.mut_xxx_return_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        );
+        w.comment("Mutable pointer to the field.");
+        if self.is_singular() {
+            w.comment("If field is not initialized, it is initialized with default value first.");
+        }
+        let fn_def = match mut_xxx_return_type {
+            RustType::Ref(ref param) => format!(
+                "{}(&mut self) -> &mut {}",
+                self.mut_name(),
+                param.to_code(&self.customize)
+            ),
+            _ => panic!(
+                "not a ref: {}",
+                mut_xxx_return_type.to_code(&self.customize)
+            ),
+        };
+        w.pub_fn(&fn_def, |w| {
+            match self.kind {
+                FieldKind::Repeated(..) | FieldKind::Map(..) => {
+                    let self_field = self.self_field();
+                    w.write_line(&format!("&mut {}", self_field));
+                }
+                FieldKind::Singular(ref s) => {
+                    self.write_message_field_mut_singular(s, w);
+                }
+                FieldKind::Oneof(ref o) => {
+                    let self_field_oneof = format!("self.{}", o.oneof_field_name);
+
+                    // if oneof does not contain current field
+                    w.if_let_else_stmt(
+                        &format!(
+                            "::std::option::Option::Some({}(_))",
+                            o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+                        )[..],
+                        &self_field_oneof[..],
+                        |w| {
+                            // initialize it with default value
+                            w.write_line(&format!(
+                                "{} = ::std::option::Option::Some({}({}));",
+                                self_field_oneof,
+                                o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+                                self.element_default_value_rust()
+                                    .into_type(
+                                        o.rust_type(
+                                            &self
+                                                .proto_field
+                                                .message
+                                                .scope
+                                                .file_and_mod(self.customize.clone())
+                                        ),
+                                        &self.customize
+                                    )
+                                    .value
+                            ));
+                        },
+                    );
+
+                    // extract field
+                    w.match_expr(self_field_oneof, |w| {
+                        w.case_expr(
+                            format!(
+                                "::std::option::Option::Some({}(ref mut v))",
+                                o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+                            ),
+                            "v",
+                        );
+                        w.case_expr("_", "panic!()");
+                    });
+                }
+            }
+        });
+    }
+
+    fn write_message_field_take_oneof(&self, o: &OneofField, w: &mut CodeWriter) {
+        let take_xxx_return_type = self.take_xxx_return_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        );
+
+        // TODO: replace with if let
+        w.write_line(&format!("if self.{}() {{", self.has_name()));
+        w.indented(|w| {
+            let self_field_oneof = format!("self.{}", o.oneof_field_name);
+            w.match_expr(format!("{}.take()", self_field_oneof), |w| {
+                let value_in_some = o
+                    .rust_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .value("v".to_owned());
+                let converted = value_in_some.into_type(
+                    self.take_xxx_return_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    ),
+                    &self.customize,
+                );
+                w.case_expr(
+                    format!(
+                        "::std::option::Option::Some({}(v))",
+                        o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+                    ),
+                    &converted.value,
+                );
+                w.case_expr("_", "panic!()");
+            });
+        });
+        w.write_line("} else {");
+        w.indented(|w| {
+            w.write_line(
+                self.elem()
+                    .rust_storage_elem_type(
+                        &self
+                            .proto_field
+                            .message
+                            .scope
+                            .file_and_mod(self.customize.clone()),
+                    )
+                    .default_value_typed(&self.customize, false)
+                    .into_type(take_xxx_return_type.clone(), &self.customize)
+                    .value,
+            );
+        });
+        w.write_line("}");
+    }
+
+    fn write_message_field_take_singular(&self, s: &SingularField, w: &mut CodeWriter) {
+        match s {
+            SingularField {
+                ref elem,
+                flag: SingularFieldFlag::WithFlag { option_kind, .. },
+            } => {
+                if !elem.is_copy() {
+                    w.write_line(
+                        &option_kind.unwrap_or_else(
+                            &format!("{}.take()", self.self_field()),
+                            &elem
+                                .rust_storage_elem_type(
+                                    &self
+                                        .proto_field
+                                        .message
+                                        .scope
+                                        .file_and_mod(self.customize.clone()),
+                                )
+                                .default_value(&self.customize, false),
+                        ),
+                    );
+                } else {
+                    w.write_line(&format!(
+                        "{}.take().unwrap_or({})",
+                        self.self_field(),
+                        self.element_default_value_rust().value
+                    ));
+                }
+            }
+            SingularField {
+                flag: SingularFieldFlag::WithoutFlag,
+                ..
+            } => w.write_line(&format!(
+                "::std::mem::replace(&mut {}, {})",
+                self.self_field(),
+                self.full_storage_type(
+                    &self
+                        .proto_field
+                        .message
+                        .scope
+                        .file_and_mod(self.customize.clone())
+                )
+                .default_value(&self.customize, false)
+            )),
+        }
+    }
+
+    fn write_message_field_take(&self, w: &mut CodeWriter) {
+        let take_xxx_return_type = self.take_xxx_return_type(
+            &self
+                .proto_field
+                .message
+                .scope
+                .file_and_mod(self.customize.clone()),
+        );
+        w.comment("Take field");
+        w.pub_fn(
+            &format!(
+                "take_{}(&mut self) -> {}",
+                self.rust_name,
+                take_xxx_return_type.to_code(&self.customize)
+            ),
+            |w| match self.kind {
+                FieldKind::Singular(ref s) => self.write_message_field_take_singular(&s, w),
+                FieldKind::Oneof(ref o) => self.write_message_field_take_oneof(o, w),
+                FieldKind::Repeated(..) | FieldKind::Map(..) => {
+                    w.write_line(&format!(
+                        "::std::mem::replace(&mut self.{}, {})",
+                        self.rust_name,
+                        take_xxx_return_type.default_value(&self.customize, false)
+                    ));
+                }
+            },
+        );
+    }
+
+    pub(crate) fn write_message_single_field_accessors(&self, w: &mut CodeWriter) {
+        if self.generate_accessors || self.generate_getter {
+            w.write_line("");
+            let reconstruct_def = self.reconstruct_def();
+            w.comment(&(reconstruct_def + ";"));
+        }
+
+        if self.generate_getter {
+            w.write_line("");
+            self.write_message_field_get(w);
+        }
+
+        if !self.generate_accessors {
+            return;
+        }
+
+        w.write_line("");
+        let clear_field_func = self.clear_field_func();
+        w.pub_fn(&format!("{}(&mut self)", clear_field_func), |w| {
+            self.write_clear(w);
+        });
+
+        if self.has_has() {
+            w.write_line("");
+            self.write_message_field_has(w);
+        }
+
+        w.write_line("");
+        self.write_message_field_set(w);
+
+        if self.has_mut() {
+            w.write_line("");
+            self.write_message_field_mut(w);
+        }
+
+        if self.has_take() {
+            w.write_line("");
+            self.write_message_field_take(w);
+        }
+    }
+}
+
+pub(crate) fn rust_field_name_for_protobuf_field_name(name: &str) -> RustIdent {
+    RustIdent::new(name)
+}
+
+pub(crate) fn rust_variant_name_for_protobuf_oneof_field_name(name: &str) -> RustIdent {
+    let name = camel_case(name);
+    RustIdent::new(&name)
+}
diff --git a/src/gen/field/option_kind.rs b/src/gen/field/option_kind.rs
new file mode 100644
index 0000000..0fde11a
--- /dev/null
+++ b/src/gen/field/option_kind.rs
@@ -0,0 +1,59 @@
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::rust_types_values::RustType;
+use crate::Customize;
+
+/// Optional fields can be stored are `Option<T>` or `SingularPtrField<T>`.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum OptionKind {
+    /// Field is `Option<T>`
+    Option,
+    /// Field is `SingularPtrField<T>`
+    MessageField,
+}
+
+impl OptionKind {
+    pub(crate) fn wrap_element(&self, element_type: RustType) -> RustType {
+        let element_type = Box::new(element_type);
+        match self {
+            OptionKind::Option => RustType::Option(element_type),
+            OptionKind::MessageField => RustType::MessageField(element_type),
+        }
+    }
+
+    // Type of `as_ref()` operation
+    pub(crate) fn as_ref_type(&self, element_type: RustType) -> RustType {
+        match self {
+            OptionKind::Option => RustType::Option(Box::new(element_type.ref_type())),
+            OptionKind::MessageField => RustType::MessageField(Box::new(element_type.ref_type())),
+        }
+    }
+
+    fn _as_option_ref(&self, v: &str) -> String {
+        match self {
+            OptionKind::Option | OptionKind::MessageField => format!("{}.as_ref()", v),
+        }
+    }
+
+    pub(crate) fn unwrap_or_else(&self, what: &str, default_value: &str) -> String {
+        match self {
+            _ => format!("{}.unwrap_or_else(|| {})", what, default_value),
+        }
+    }
+
+    pub(crate) fn unwrap_ref_or_else(&self, what: &str, default_value: &str) -> String {
+        match self {
+            _ => format!("{}.unwrap_or_else(|| {})", what, default_value),
+        }
+    }
+
+    pub(crate) fn wrap_value(&self, value: &str, customize: &Customize) -> String {
+        match self {
+            OptionKind::Option => format!("::std::option::Option::Some({})", value),
+            OptionKind::MessageField => format!(
+                "{}::MessageField::some({})",
+                protobuf_crate_path(customize),
+                value
+            ),
+        }
+    }
+}
diff --git a/src/gen/field/repeated.rs b/src/gen/field/repeated.rs
new file mode 100644
index 0000000..56e0d0b
--- /dev/null
+++ b/src/gen/field/repeated.rs
@@ -0,0 +1,46 @@
+use crate::gen::field::elem::FieldElem;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::rust::snippets::EXPR_VEC_NEW;
+use crate::gen::rust_types_values::RustType;
+
+/// Repeated field can be `Vec<T>` or `RepeatedField<T>`.
+#[derive(Eq, PartialEq, Copy, Clone)]
+pub enum RepeatedFieldKind {
+    Vec,
+}
+
+impl RepeatedFieldKind {
+    fn wrap_element(&self, element_type: RustType) -> RustType {
+        let element_type = Box::new(element_type);
+        match self {
+            RepeatedFieldKind::Vec => RustType::Vec(element_type),
+        }
+    }
+
+    fn default(&self) -> String {
+        match self {
+            RepeatedFieldKind::Vec => EXPR_VEC_NEW.to_owned(),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct RepeatedField<'a> {
+    pub elem: FieldElem<'a>,
+    pub packed: bool,
+}
+
+impl<'a> RepeatedField<'a> {
+    pub(crate) fn kind(&self) -> RepeatedFieldKind {
+        RepeatedFieldKind::Vec
+    }
+
+    pub(crate) fn rust_type(&self, reference: &FileAndMod) -> RustType {
+        self.kind()
+            .wrap_element(self.elem.rust_storage_elem_type(reference))
+    }
+
+    pub(crate) fn default(&self) -> String {
+        self.kind().default()
+    }
+}
diff --git a/src/gen/field/singular.rs b/src/gen/field/singular.rs
new file mode 100644
index 0000000..3bd223f
--- /dev/null
+++ b/src/gen/field/singular.rs
@@ -0,0 +1,52 @@
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::rust_types_values::RustType;
+use crate::Customize;
+
+#[derive(Clone, PartialEq, Eq, Copy)]
+pub enum SingularFieldFlag {
+    // proto2 or proto3 message
+    WithFlag {
+        required: bool,
+        option_kind: OptionKind,
+    },
+    // proto3
+    WithoutFlag,
+}
+
+impl SingularFieldFlag {
+    pub fn is_required(&self) -> bool {
+        match *self {
+            SingularFieldFlag::WithFlag { required, .. } => required,
+            SingularFieldFlag::WithoutFlag => false,
+        }
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct SingularField<'a> {
+    pub flag: SingularFieldFlag,
+    pub elem: FieldElem<'a>,
+}
+
+impl<'a> SingularField<'a> {
+    pub(crate) fn rust_storage_type(&self, reference: &FileAndMod) -> RustType {
+        match self.flag {
+            SingularFieldFlag::WithFlag { option_kind, .. } => {
+                option_kind.wrap_element(self.elem.rust_storage_elem_type(reference))
+            }
+            SingularFieldFlag::WithoutFlag => self.elem.rust_storage_elem_type(reference),
+        }
+    }
+
+    pub(crate) fn default_value(
+        &self,
+        customize: &Customize,
+        reference: &FileAndMod,
+        const_expr: bool,
+    ) -> String {
+        self.rust_storage_type(reference)
+            .default_value(customize, const_expr)
+    }
+}
diff --git a/src/gen/field/tag.rs b/src/gen/field/tag.rs
new file mode 100644
index 0000000..401a140
--- /dev/null
+++ b/src/gen/field/tag.rs
@@ -0,0 +1,5 @@
+use protobuf::rt::WireType;
+
+pub(crate) fn make_tag(field_number: u32, wire_type: WireType) -> u32 {
+    (field_number << 3) | (wire_type as u32)
+}
diff --git a/src/gen/field/type_ext.rs b/src/gen/field/type_ext.rs
new file mode 100644
index 0000000..334168f
--- /dev/null
+++ b/src/gen/field/type_ext.rs
@@ -0,0 +1,115 @@
+use protobuf::descriptor::field_descriptor_proto::Type;
+
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+
+pub(crate) trait TypeExt {
+    fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String;
+    fn is_s_varint(&self) -> bool;
+    fn is_copy(&self) -> bool;
+    fn protobuf_name(&self) -> &'static str;
+    fn rust_type(&self) -> RustType;
+    fn os_write_fn_param_type(&self) -> RustType;
+    fn encoded_size(&self) -> Option<u32>;
+}
+
+impl TypeExt for Type {
+    fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String {
+        match (self, primitive_type_variant) {
+            (Type::TYPE_ENUM, _) => format!("{}.read_enum_or_unknown()", is),
+            (Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+                format!("{}.read_tokio_bytes()", is)
+            }
+            (Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+                format!("{}.read_tokio_chars()", is)
+            }
+            _ => format!("{}.read_{}()", is, self.protobuf_name()),
+        }
+    }
+
+    /// True if self is signed integer with zigzag encoding
+    fn is_s_varint(&self) -> bool {
+        match *self {
+            Type::TYPE_SINT32 | Type::TYPE_SINT64 => true,
+            _ => false,
+        }
+    }
+
+    fn is_copy(&self) -> bool {
+        match self {
+            Type::TYPE_MESSAGE | Type::TYPE_STRING | Type::TYPE_BYTES => false,
+            _ => true,
+        }
+    }
+
+    fn protobuf_name(&self) -> &'static str {
+        match self {
+            Type::TYPE_DOUBLE => "double",
+            Type::TYPE_FLOAT => "float",
+            Type::TYPE_INT32 => "int32",
+            Type::TYPE_INT64 => "int64",
+            Type::TYPE_UINT32 => "uint32",
+            Type::TYPE_UINT64 => "uint64",
+            Type::TYPE_SINT32 => "sint32",
+            Type::TYPE_SINT64 => "sint64",
+            Type::TYPE_FIXED32 => "fixed32",
+            Type::TYPE_FIXED64 => "fixed64",
+            Type::TYPE_SFIXED32 => "sfixed32",
+            Type::TYPE_SFIXED64 => "sfixed64",
+            Type::TYPE_BOOL => "bool",
+            Type::TYPE_STRING => "string",
+            Type::TYPE_BYTES => "bytes",
+            Type::TYPE_ENUM => "enum",
+            Type::TYPE_MESSAGE => "message",
+            Type::TYPE_GROUP => "group",
+        }
+    }
+
+    /// Rust type for protobuf base type.
+    fn rust_type(&self) -> RustType {
+        match self {
+            Type::TYPE_DOUBLE => RustType::Float(64),
+            Type::TYPE_FLOAT => RustType::Float(32),
+            Type::TYPE_INT32 => RustType::Int(true, 32),
+            Type::TYPE_INT64 => RustType::Int(true, 64),
+            Type::TYPE_UINT32 => RustType::Int(false, 32),
+            Type::TYPE_UINT64 => RustType::Int(false, 64),
+            Type::TYPE_SINT32 => RustType::Int(true, 32),
+            Type::TYPE_SINT64 => RustType::Int(true, 64),
+            Type::TYPE_FIXED32 => RustType::Int(false, 32),
+            Type::TYPE_FIXED64 => RustType::Int(false, 64),
+            Type::TYPE_SFIXED32 => RustType::Int(true, 32),
+            Type::TYPE_SFIXED64 => RustType::Int(true, 64),
+            Type::TYPE_BOOL => RustType::Bool,
+            Type::TYPE_STRING => RustType::String,
+            Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::u8())),
+            Type::TYPE_ENUM | Type::TYPE_GROUP | Type::TYPE_MESSAGE => {
+                panic!("there is no rust name for {:?}", self)
+            }
+        }
+    }
+
+    // type of `v` in `os.write_xxx_no_tag(v)`
+    fn os_write_fn_param_type(&self) -> RustType {
+        match self {
+            Type::TYPE_STRING => RustType::amp_str(),
+            Type::TYPE_BYTES => RustType::amp_slice_of_u8(),
+            Type::TYPE_ENUM => RustType::i32(),
+            t => t.rust_type(),
+        }
+    }
+
+    /// Size of value for type, None if variable.
+    fn encoded_size(&self) -> Option<u32> {
+        match self {
+            Type::TYPE_BOOL => Some(1),
+            Type::TYPE_FIXED32 => Some(4),
+            Type::TYPE_FIXED64 => Some(8),
+            Type::TYPE_SFIXED32 => Some(4),
+            Type::TYPE_SFIXED64 => Some(8),
+            Type::TYPE_FLOAT => Some(4),
+            Type::TYPE_DOUBLE => Some(8),
+            _ => None,
+        }
+    }
+}
diff --git a/src/gen/file.rs b/src/gen/file.rs
new file mode 100644
index 0000000..411b069
--- /dev/null
+++ b/src/gen/file.rs
@@ -0,0 +1,149 @@
+use std::collections::HashMap;
+
+use protobuf::descriptor::file_options;
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtoPath;
+
+use crate::compiler_plugin;
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_file;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::enums::EnumGen;
+use crate::gen::extensions::write_extensions;
+use crate::gen::file_descriptor::write_file_descriptor_data;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::MessageGen;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::scope::FileScope;
+use crate::gen::scope::RootScope;
+use crate::proto_name_to_rs;
+
+pub(crate) struct GenFileResult {
+    pub(crate) compiler_plugin_result: compiler_plugin::GenResult,
+    pub(crate) mod_name: String,
+}
+
+pub(crate) fn gen_file(
+    file_descriptor: &FileDescriptor,
+    _files_map: &HashMap<&ProtoPath, &FileDescriptor>,
+    root_scope: &RootScope,
+    parent_customize: &CustomizeElemCtx,
+    parser: &str,
+) -> anyhow::Result<GenFileResult> {
+    let lite_runtime_from_builtin_option = file_descriptor
+        .proto()
+        .options
+        .get_or_default()
+        .optimize_for()
+        == file_options::OptimizeMode::LITE_RUNTIME;
+
+    let mut customize_from_proto =
+        customize_from_rustproto_for_file(file_descriptor.proto().options.get_or_default());
+    if customize_from_proto.lite_runtime.is_none()
+        && parent_customize.for_elem.lite_runtime.is_none()
+    {
+        customize_from_proto.lite_runtime = Some(lite_runtime_from_builtin_option);
+    }
+
+    let customize = parent_customize.child(&customize_from_proto, file_descriptor);
+
+    let file_scope = FileScope { file_descriptor };
+    let scope = file_scope.to_scope();
+
+    let lite_runtime = customize.for_elem.lite_runtime.unwrap_or(false);
+
+    let v = CodeWriter::with(|w| {
+        w.write_generated_by("rust-protobuf", "3.2.0", parser);
+
+        w.write_line("");
+        w.write_line(&format!(
+            "//! Generated file from `{}`",
+            file_descriptor.proto().name()
+        ));
+
+        if customize.for_elem.lite_runtime.unwrap_or(false) {
+            w.comment("Generated for lite runtime");
+        }
+
+        if customize.for_elem.inside_protobuf != Some(true) {
+            w.write_line("");
+            w.write_line("/// Generated files are compatible only with the same version");
+            w.write_line("/// of protobuf runtime.");
+            w.write_line(&format!(
+                "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
+                protobuf_crate_path(&customize.for_elem),
+                protobuf::VERSION_IDENT
+            ));
+        }
+
+        static NESTED_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+        let message_type_number = *NESTED_TYPE_NUMBER.get(|| {
+            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
+                .field_by_name("message_type")
+                .expect("`message_type` must exist")
+                .proto()
+                .number()
+        });
+
+        let mut path = vec![message_type_number, 0];
+        for (id, message) in scope.messages().iter().enumerate() {
+            // ignore map entries, because they are not used in map fields
+            if !message.is_map() {
+                path[1] = id as i32;
+
+                w.write_line("");
+                MessageGen::new(
+                    file_descriptor,
+                    message,
+                    &root_scope,
+                    &customize,
+                    &path,
+                    file_descriptor.proto().source_code_info.as_ref(),
+                )?
+                .write(w)?;
+            }
+        }
+
+        static ENUM_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+        let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
+            protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
+                .field_by_name("enum_type")
+                .expect("`enum_type` must exist")
+                .proto()
+                .number()
+        });
+
+        let mut path = vec![enum_type_number, 0];
+        for (id, enum_type) in scope.enums().iter().enumerate() {
+            path[1] = id as i32;
+
+            w.write_line("");
+            EnumGen::new(
+                enum_type,
+                &customize,
+                root_scope,
+                &path,
+                file_descriptor.proto().source_code_info.as_ref(),
+            )
+            .write(w);
+        }
+
+        write_extensions(file_descriptor, &root_scope, w, &customize);
+
+        if !lite_runtime {
+            w.write_line("");
+            write_file_descriptor_data(file_descriptor, &customize.for_elem, w);
+        }
+
+        Ok(())
+    })?;
+
+    Ok(GenFileResult {
+        compiler_plugin_result: compiler_plugin::GenResult {
+            name: proto_name_to_rs(file_descriptor.proto().name()),
+            content: v.into_bytes(),
+        },
+        mod_name: proto_path_to_rust_mod(file_descriptor.proto().name()).into_string(),
+    })
+}
diff --git a/src/file_and_mod.rs b/src/gen/file_and_mod.rs
similarity index 60%
rename from src/file_and_mod.rs
rename to src/gen/file_and_mod.rs
index 4f4e8c6..8862c53 100644
--- a/src/file_and_mod.rs
+++ b/src/gen/file_and_mod.rs
@@ -1,7 +1,6 @@
-use rust_name::RustRelativePath;
-use Customize;
+use crate::customize::Customize;
+use crate::gen::rust::rel_path::RustRelativePath;
 
-#[allow(dead_code)]
 pub(crate) struct FileAndMod {
     pub file: String,
     pub relative_mod: RustRelativePath,
diff --git a/src/gen/file_descriptor.rs b/src/gen/file_descriptor.rs
new file mode 100644
index 0000000..2c8249c
--- /dev/null
+++ b/src/gen/file_descriptor.rs
@@ -0,0 +1,208 @@
+use std::fmt::Write as _;
+
+use protobuf::reflect::FileDescriptor;
+use protobuf::Message;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::paths::proto_path_to_fn_file_descriptor;
+use crate::gen::rust::snippets::expr_vec_with_capacity_const;
+use crate::gen::scope::FileScope;
+use crate::gen::scope::Scope;
+use crate::gen::scope::WithScope;
+use crate::Customize;
+
+fn escape_byte(s: &mut String, b: u8) {
+    if b == b'\n' {
+        write!(s, "\\n").unwrap();
+    } else if b == b'\r' {
+        write!(s, "\\r").unwrap();
+    } else if b == b'\t' {
+        write!(s, "\\t").unwrap();
+    } else if b == b'\\' || b == b'"' {
+        write!(s, "\\{}", b as char).unwrap();
+    } else if b == b'\0' {
+        write!(s, "\\0").unwrap();
+    // ASCII printable except space
+    } else if b > 0x20 && b < 0x7f {
+        write!(s, "{}", b as char).unwrap();
+    } else {
+        write!(s, "\\x{:02x}", b).unwrap();
+    }
+}
+
+fn write_generate_file_descriptor(
+    file_descriptor: &FileDescriptor,
+    customize: &Customize,
+    w: &mut CodeWriter,
+) {
+    let deps = &file_descriptor.proto().dependency;
+    w.write_line(&format!(
+        "let mut deps = {vec_with_capacity};",
+        vec_with_capacity = expr_vec_with_capacity_const(deps.len())
+    ));
+    for f in deps {
+        w.write_line(&format!(
+            "deps.push({}().clone());",
+            proto_path_to_fn_file_descriptor(f, customize)
+        ));
+    }
+
+    let scope = FileScope { file_descriptor };
+
+    let messages = scope.find_messages_except_map();
+    w.write_line(&format!(
+        "let mut messages = {vec_with_capacity};",
+        vec_with_capacity = expr_vec_with_capacity_const(messages.len())
+    ));
+    for m in &messages {
+        w.write_line(&format!(
+            "messages.push({}::generated_message_descriptor_data());",
+            m.rust_name_to_file(),
+        ));
+    }
+
+    let enums = scope.find_enums();
+    w.write_line(&format!(
+        "let mut enums = {};",
+        expr_vec_with_capacity_const(enums.len())
+    ));
+    for e in &enums {
+        w.write_line(&format!(
+            "enums.push({}::generated_enum_descriptor_data());",
+            e.rust_name_to_file(),
+        ));
+    }
+
+    w.write_line(&format!(
+        "{}::reflect::GeneratedFileDescriptor::new_generated(",
+        protobuf_crate_path(&customize),
+    ));
+    w.indented(|w| {
+        w.write_line(&format!("file_descriptor_proto(),"));
+        w.write_line(&format!("deps,"));
+        w.write_line(&format!("messages,"));
+        w.write_line(&format!("enums,"));
+    });
+    w.write_line(")");
+}
+
+fn write_file_descriptor(
+    file_descriptor: &FileDescriptor,
+    customize: &Customize,
+    w: &mut CodeWriter,
+) {
+    w.write_line("/// `FileDescriptor` object which allows dynamic access to files");
+    w.pub_fn(
+        &format!(
+            "file_descriptor() -> &'static {protobuf_crate}::reflect::FileDescriptor",
+            protobuf_crate = protobuf_crate_path(customize)
+        ),
+        |w| {
+            w.lazy_static(
+                "generated_file_descriptor_lazy",
+                &format!(
+                    "{protobuf_crate}::reflect::GeneratedFileDescriptor",
+                    protobuf_crate = protobuf_crate_path(customize)
+                ),
+                &format!("{}", protobuf_crate_path(customize)),
+            );
+            w.lazy_static_decl_get(
+                "file_descriptor",
+                &format!(
+                    "{protobuf_crate}::reflect::FileDescriptor",
+                    protobuf_crate = protobuf_crate_path(customize)
+                ),
+                &format!("{}", protobuf_crate_path(customize)),
+                |w| {
+                    w.block(
+                        "let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {",
+                        "});",
+                        |w| write_generate_file_descriptor(file_descriptor, customize, w),
+                    );
+                    w.write_line(&format!(
+                        "{protobuf_crate}::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)",
+                        protobuf_crate=protobuf_crate_path(&customize),
+                    ));
+                }
+            );
+        },
+    );
+}
+
+pub(crate) fn write_file_descriptor_data(
+    file: &FileDescriptor,
+    customize: &Customize,
+    w: &mut CodeWriter,
+) {
+    let fdp_bytes = file.proto().write_to_bytes().unwrap();
+    w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
+    w.indented(|w| {
+        const MAX_LINE_LEN: usize = 72;
+
+        let mut s = String::new();
+        for &b in &fdp_bytes {
+            let prev_len = s.len();
+            escape_byte(&mut s, b);
+            let truncate = s.len() > MAX_LINE_LEN;
+            if truncate {
+                s.truncate(prev_len);
+            }
+            if truncate || s.len() == MAX_LINE_LEN {
+                write!(s, "\\").unwrap();
+                w.write_line(&s);
+                s.clear();
+            }
+            if truncate {
+                escape_byte(&mut s, b);
+            }
+        }
+        if !s.is_empty() {
+            write!(s, "\\").unwrap();
+            w.write_line(&s);
+            s.clear();
+        }
+    });
+    w.write_line("\";");
+    w.write_line("");
+    write_file_descriptor_proto(&customize, w);
+    w.write_line("");
+    write_file_descriptor(file, &customize, w);
+}
+
+fn write_file_descriptor_proto(customize: &Customize, w: &mut CodeWriter) {
+    w.write_line("/// `FileDescriptorProto` object which was a source for this generated file");
+    w.def_fn(
+        &format!(
+            "file_descriptor_proto() -> &'static {protobuf_crate}::descriptor::FileDescriptorProto",
+            protobuf_crate=protobuf_crate_path(customize)
+        ),
+        |w| {
+            w.lazy_static_decl_get(
+                "file_descriptor_proto_lazy",
+                &format!(
+                    "{protobuf_crate}::descriptor::FileDescriptorProto",
+                    protobuf_crate=protobuf_crate_path(customize)
+                ),
+                &format!("{}", protobuf_crate_path(customize)),
+                |w| {
+                    w.write_line(&format!(
+                        "{protobuf_crate}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
+                        protobuf_crate=protobuf_crate_path(customize)
+                    ));
+                },
+            );
+        },
+    );
+}
+
+/// Code to generate call `module::file_descriptor()`.
+pub(crate) fn file_descriptor_call_expr(scope: &Scope) -> String {
+    format!(
+        "{}()",
+        scope
+            .rust_path_to_file()
+            .to_reverse()
+            .append_ident("file_descriptor".into())
+    )
+}
diff --git a/src/gen/inside.rs b/src/gen/inside.rs
new file mode 100644
index 0000000..d416031
--- /dev/null
+++ b/src/gen/inside.rs
@@ -0,0 +1,11 @@
+use crate::customize::Customize;
+use crate::gen::rust::path::RustPath;
+
+/// Path to `protobuf` crate, different when `.proto` file is
+/// used inside or outside of protobuf crate.
+pub(crate) fn protobuf_crate_path(customize: &Customize) -> RustPath {
+    match customize.inside_protobuf {
+        Some(true) => RustPath::from("crate"),
+        _ => RustPath::from("::protobuf"),
+    }
+}
diff --git a/src/gen/map.rs b/src/gen/map.rs
new file mode 100644
index 0000000..ba50eeb
--- /dev/null
+++ b/src/gen/map.rs
@@ -0,0 +1,16 @@
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageWithScope;
+
+/// Pair of (key, value) if this message is map entry
+pub(crate) fn map_entry<'a>(
+    d: &'a MessageWithScope,
+) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> {
+    if d.message.is_map_entry() {
+        // `MessageDescriptor` validated the fields.
+        let key = d.fields()[0].clone();
+        let value = d.fields()[1].clone();
+        Some((key, value))
+    } else {
+        None
+    }
+}
diff --git a/src/gen/message.rs b/src/gen/message.rs
new file mode 100644
index 0000000..3471f87
--- /dev/null
+++ b/src/gen/message.rs
@@ -0,0 +1,786 @@
+use std::fmt;
+
+use protobuf::descriptor::*;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf_parse::snake_case;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::ctx::SpecialFieldPseudoDescriptor;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_message;
+use crate::gen::code_writer::*;
+use crate::gen::descriptor::write_fn_descriptor;
+use crate::gen::enums::*;
+use crate::gen::field::FieldGen;
+use crate::gen::field::FieldKind;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::oneof::OneofGen;
+use crate::gen::oneof::OneofVariantGen;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_message;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_special_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust::snippets::expr_vec_with_capacity_const;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust_types_values::*;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+use crate::Customize;
+
+/// Protobuf message Rust type name
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct RustTypeMessage(pub RustIdentWithPath);
+
+impl fmt::Display for RustTypeMessage {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(&self.0, f)
+    }
+}
+
+impl<S: Into<RustIdentWithPath>> From<S> for RustTypeMessage {
+    fn from(s: S) -> Self {
+        RustTypeMessage(s.into())
+    }
+}
+
+impl RustTypeMessage {
+    /// Code which emits default instance.
+    pub fn default_instance(&self, customize: &Customize) -> String {
+        format!(
+            "<{} as {}::Message>::default_instance()",
+            self.0,
+            protobuf_crate_path(customize)
+        )
+    }
+}
+
+/// Message info for codegen
+pub(crate) struct MessageGen<'a> {
+    file_descriptor: &'a FileDescriptor,
+    message_descriptor: MessageDescriptor,
+    pub message: &'a MessageWithScope<'a>,
+    pub root_scope: &'a RootScope<'a>,
+    pub fields: Vec<FieldGen<'a>>,
+    pub lite_runtime: bool,
+    customize: CustomizeElemCtx<'a>,
+    path: &'a [i32],
+    info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> MessageGen<'a> {
+    pub fn new(
+        file_descriptor: &'a FileDescriptor,
+        message: &'a MessageWithScope<'a>,
+        root_scope: &'a RootScope<'a>,
+        parent_customize: &CustomizeElemCtx<'a>,
+        path: &'a [i32],
+        info: Option<&'a SourceCodeInfo>,
+    ) -> anyhow::Result<MessageGen<'a>> {
+        let message_descriptor = file_descriptor
+            .message_by_package_relative_name(&format!("{}", message.protobuf_name_to_package()))
+            .unwrap();
+
+        let customize = parent_customize.child(
+            &customize_from_rustproto_for_message(message.message.proto().options.get_or_default()),
+            &message.message,
+        );
+
+        static FIELD_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+        let field_number = *FIELD_NUMBER.get(|| {
+            protobuf::reflect::MessageDescriptor::for_type::<DescriptorProto>()
+                .field_by_name("field")
+                .expect("`field` must exist")
+                .proto()
+                .number()
+        });
+
+        let fields: Vec<_> = message
+            .fields()
+            .into_iter()
+            .enumerate()
+            .map(|(id, field)| {
+                let mut path = path.to_vec();
+                path.extend_from_slice(&[field_number, id as i32]);
+                FieldGen::parse(field, root_scope, &customize, path, info)
+            })
+            .collect::<anyhow::Result<Vec<_>>>()?;
+        let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+            message.file_descriptor().proto().options.optimize_for()
+                == file_options::OptimizeMode::LITE_RUNTIME
+        });
+        Ok(MessageGen {
+            message_descriptor,
+            file_descriptor,
+            message,
+            root_scope,
+            fields,
+            lite_runtime,
+            customize,
+            path,
+            info,
+        })
+    }
+
+    fn rust_name(&self) -> RustIdent {
+        self.message.rust_name()
+    }
+
+    fn mod_name(&self) -> RustRelativePath {
+        self.message.scope.rust_path_to_file()
+    }
+
+    pub fn file_and_mod(&self) -> FileAndMod {
+        self.message
+            .scope
+            .file_and_mod(self.customize.for_elem.clone())
+    }
+
+    fn oneofs(&'a self) -> Vec<OneofGen<'a>> {
+        self.message
+            .oneofs()
+            .into_iter()
+            .map(|oneof| OneofGen::parse(self, oneof, &self.customize))
+            .collect()
+    }
+
+    fn required_fields(&'a self) -> Vec<&'a FieldGen> {
+        self.fields
+            .iter()
+            .filter(|f| match f.kind {
+                FieldKind::Singular(ref singular) => singular.flag.is_required(),
+                _ => false,
+            })
+            .collect()
+    }
+
+    fn message_fields(&'a self) -> Vec<&'a FieldGen> {
+        self.fields
+            .iter()
+            .filter(|f| f.proto_type == field_descriptor_proto::Type::TYPE_MESSAGE)
+            .collect()
+    }
+
+    fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen> {
+        self.fields
+            .iter()
+            .filter(|f| match f.kind {
+                FieldKind::Oneof(..) => false,
+                _ => true,
+            })
+            .collect()
+    }
+
+    fn fields_except_group(&'a self) -> Vec<&'a FieldGen> {
+        self.fields
+            .iter()
+            .filter(|f| f.proto_type != field_descriptor_proto::Type::TYPE_GROUP)
+            .collect()
+    }
+
+    fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen> {
+        self.fields
+            .iter()
+            .filter(|f| match f.kind {
+                FieldKind::Oneof(..) => false,
+                _ => f.proto_type != field_descriptor_proto::Type::TYPE_GROUP,
+            })
+            .collect()
+    }
+
+    fn write_match_each_oneof_variant<F>(&self, w: &mut CodeWriter, cb: F)
+    where
+        F: Fn(&mut CodeWriter, &OneofVariantGen, &RustValueTyped),
+    {
+        for oneof in self.oneofs() {
+            let variants = oneof.variants_except_group();
+            if variants.is_empty() {
+                // Special case because
+                // https://github.com/rust-lang/rust/issues/50642
+                continue;
+            }
+            w.if_let_stmt(
+                "::std::option::Option::Some(ref v)",
+                &format!("self.{}", oneof.oneof.field_name())[..],
+                |w| {
+                    w.match_block("v", |w| {
+                        for variant in variants {
+                            let ref field = variant.field;
+
+                            let (refv, vtype) = if field.elem_type_is_copy() {
+                                ("v", variant.rust_type(&self.file_and_mod()))
+                            } else {
+                                ("ref v", variant.rust_type(&self.file_and_mod()).ref_type())
+                            };
+                            w.case_block(
+                                format!("&{}({})", variant.path(&self.file_and_mod()), refv),
+                                |w| {
+                                    cb(
+                                        w,
+                                        &variant,
+                                        &RustValueTyped {
+                                            value: "v".to_owned(),
+                                            rust_type: vtype.clone(),
+                                        },
+                                    );
+                                },
+                            );
+                        }
+                    });
+                },
+            );
+        }
+    }
+
+    fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "write_to_with_cached_sizes(&self, os: &mut {protobuf_crate}::CodedOutputStream<'_>) -> {protobuf_crate}::Result<()>",
+            protobuf_crate=protobuf_crate_path(&self.customize.for_elem),
+        );
+        w.def_fn(&sig, |w| {
+            // To have access to its methods but not polute the name space.
+            for f in self.fields_except_oneof_and_group() {
+                f.write_message_write_field("os", w);
+            }
+            self.write_match_each_oneof_variant(w, |w, variant, v| {
+                variant
+                    .field
+                    .write_write_element(variant.elem(), w, "os", v);
+            });
+            w.write_line("os.write_unknown_fields(self.special_fields.unknown_fields())?;");
+            w.write_line("::std::result::Result::Ok(())");
+        });
+    }
+
+    fn write_default_instance_lazy(&self, w: &mut CodeWriter) {
+        w.lazy_static_decl_get_simple(
+            "instance",
+            &format!("{}", self.rust_name()),
+            &format!("{}::new", self.rust_name()),
+            &format!("{}", protobuf_crate_path(&self.customize.for_elem)),
+        );
+    }
+
+    fn write_default_instance_static(&self, w: &mut CodeWriter) {
+        w.stmt_block(
+            &format!(
+                "static instance: {} = {}",
+                self.rust_name(),
+                self.rust_name()
+            ),
+            |w| {
+                for f in &self.fields_except_oneof_and_group() {
+                    w.field_entry(
+                        &f.rust_name.to_string(),
+                        &f.kind
+                            .default(&self.customize.for_elem, &self.file_and_mod(), true),
+                    );
+                }
+                for o in &self.oneofs() {
+                    w.field_entry(&o.oneof.field_name().to_string(), EXPR_NONE);
+                }
+                w.field_entry(
+                    "special_fields",
+                    &format!(
+                        "{}::SpecialFields::new()",
+                        protobuf_crate_path(&self.customize.for_elem)
+                    ),
+                );
+            },
+        );
+        w.write_line("&instance");
+    }
+
+    fn write_default_instance(&self, w: &mut CodeWriter) {
+        w.def_fn(
+            &format!("default_instance() -> &'static {}", self.rust_name()),
+            |w| {
+                let has_map_field = self.fields.iter().any(|f| match f.kind {
+                    FieldKind::Map(..) => true,
+                    _ => false,
+                });
+                if has_map_field {
+                    self.write_default_instance_lazy(w)
+                } else {
+                    self.write_default_instance_static(w)
+                }
+            },
+        );
+    }
+
+    fn write_compute_size(&self, w: &mut CodeWriter) {
+        // Append sizes of messages in the tree to the specified vector.
+        // First appended element is size of self, and then nested message sizes.
+        // in serialization order are appended recursively.");
+        w.comment("Compute sizes of nested messages");
+        // there are unused variables in oneof
+        w.allow(&["unused_variables"]);
+        w.def_fn("compute_size(&self) -> u64", |w| {
+            // To have access to its methods but not polute the name space.
+            w.write_line("let mut my_size = 0;");
+            for field in self.fields_except_oneof_and_group() {
+                field.write_message_compute_field_size("my_size", w);
+            }
+            self.write_match_each_oneof_variant(w, |w, variant, v| {
+                variant
+                    .field
+                    .write_element_size(variant.elem(), w, v, "my_size");
+            });
+            w.write_line(&format!(
+                "my_size += {}::rt::unknown_fields_size(self.special_fields.unknown_fields());",
+                protobuf_crate_path(&self.customize.for_elem)
+            ));
+            w.write_line("self.special_fields.cached_size().set(my_size as u32);");
+            w.write_line("my_size");
+        });
+    }
+
+    fn write_field_accessors(&self, w: &mut CodeWriter) {
+        for f in self.fields_except_group() {
+            f.write_message_single_field_accessors(w);
+        }
+    }
+
+    fn write_impl_self(&self, w: &mut CodeWriter) {
+        w.impl_self_block(&format!("{}", self.rust_name()), |w| {
+            w.pub_fn(&format!("new() -> {}", self.rust_name()), |w| {
+                w.write_line("::std::default::Default::default()");
+            });
+
+            self.write_field_accessors(w);
+
+            if !self.lite_runtime {
+                w.write_line("");
+                self.write_generated_message_descriptor_data(w);
+            }
+        });
+    }
+
+    fn write_unknown_fields(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "special_fields(&self) -> &{}::SpecialFields",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.def_fn(&sig, |w| {
+            w.write_line("&self.special_fields");
+        });
+        w.write_line("");
+        let sig = format!(
+            "mut_special_fields(&mut self) -> &mut {}::SpecialFields",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.def_fn(&sig, |w| {
+            w.write_line("&mut self.special_fields");
+        });
+    }
+
+    fn write_merge_from(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::Result<()>",
+            protobuf_crate_path(&self.customize.for_elem),
+            protobuf_crate_path(&self.customize.for_elem),
+        );
+        w.def_fn(&sig, |w| {
+            w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| {
+                w.match_block("tag", |w| {
+                    for f in &self.fields_except_group() {
+                        f.write_merge_from_field_case_block(w);
+                    }
+                    w.case_block("tag", |w| {
+                        w.write_line(&format!("{}::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;", protobuf_crate_path(&self.customize.for_elem)));
+                    });
+                });
+            });
+            w.write_line("::std::result::Result::Ok(())");
+        });
+    }
+
+    fn write_impl_message_full_fn_descriptor(&self, w: &mut CodeWriter) {
+        write_fn_descriptor(
+            &self.message.message,
+            self.message.scope(),
+            &self.customize.for_elem,
+            w,
+        );
+    }
+
+    fn write_generated_message_descriptor_data(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "generated_message_descriptor_data() -> {}::reflect::GeneratedMessageDescriptorData",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.fn_block(
+            Visibility::Path(self.message.scope().rust_path_to_file().to_reverse()),
+            &sig,
+            |w| {
+                let fields = self.fields_except_group();
+                let oneofs = self.oneofs();
+                w.write_line(&format!(
+                    "let mut fields = {};",
+                    expr_vec_with_capacity_const(fields.len())
+                ));
+                w.write_line(&format!(
+                    "let mut oneofs = {};",
+                    expr_vec_with_capacity_const(oneofs.len())
+                ));
+                for field in fields {
+                    field.write_push_accessor("fields", w);
+                }
+                for oneof in oneofs {
+                    w.write_line(&format!(
+                        "oneofs.push({}::generated_oneof_descriptor_data());",
+                        oneof.type_name_relative(&self.mod_name())
+                    ));
+                }
+                w.write_line(&format!(
+                    "{}::reflect::GeneratedMessageDescriptorData::new_2::<{}>(",
+                    protobuf_crate_path(&self.customize.for_elem),
+                    self.rust_name(),
+                ));
+                w.indented(|w| {
+                    w.write_line(&format!("\"{}\",", self.message.name_to_package()));
+                    w.write_line("fields,");
+                    w.write_line("oneofs,");
+                });
+                w.write_line(")");
+            },
+        );
+    }
+
+    fn write_is_initialized(&self, w: &mut CodeWriter) {
+        w.def_fn(&format!("is_initialized(&self) -> bool"), |w| {
+            if !self.message.message.is_initialized_is_always_true() {
+                // TODO: use single loop
+
+                for f in self.required_fields() {
+                    f.write_if_self_field_is_none(w, |w| {
+                        w.write_line("return false;");
+                    });
+                }
+
+                for f in self.message_fields() {
+                    if let FieldKind::Map(..) = f.kind {
+                        // TODO
+                        w.comment("TODO: check map values are initialized");
+                        continue;
+                    }
+
+                    f.write_for_self_field(w, "v", |w, _t| {
+                        w.if_stmt("!v.is_initialized()", |w| {
+                            w.write_line("return false;");
+                        });
+                    });
+                }
+            }
+            w.write_line("true");
+        });
+    }
+
+    fn write_impl_message(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!("{}::Message", protobuf_crate_path(&self.customize.for_elem),),
+            &format!("{}", self.rust_name()),
+            |w| {
+                w.write_line(&format!(
+                    "const NAME: &'static str = \"{}\";",
+                    self.message.message.name()
+                ));
+                w.write_line("");
+                self.write_is_initialized(w);
+                w.write_line("");
+                self.write_merge_from(w);
+                w.write_line("");
+                self.write_compute_size(w);
+                w.write_line("");
+                self.write_write_to_with_cached_sizes(w);
+                w.write_line("");
+                self.write_unknown_fields(w);
+                w.write_line("");
+                w.def_fn(&format!("new() -> {}", self.rust_name()), |w| {
+                    w.write_line(&format!("{}::new()", self.rust_name()));
+                });
+                w.write_line("");
+                w.def_fn("clear(&mut self)", |w| {
+                    for f in self.fields_except_group() {
+                        f.write_clear(w);
+                    }
+                    w.write_line("self.special_fields.clear();");
+                });
+                w.write_line("");
+                self.write_default_instance(w);
+            },
+        );
+    }
+
+    fn write_impl_message_full(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!(
+                "{}::MessageFull",
+                protobuf_crate_path(&self.customize.for_elem),
+            ),
+            &format!("{}", self.rust_name()),
+            |w| {
+                self.write_impl_message_full_fn_descriptor(w);
+            },
+        );
+    }
+
+    fn write_impl_value(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!(
+                "{}::reflect::ProtobufValue",
+                protobuf_crate_path(&self.customize.for_elem)
+            ),
+            &format!("{}", self.rust_name()),
+            |w| {
+                w.write_line(&format!(
+                    "type RuntimeType = {}::reflect::rt::RuntimeTypeMessage<Self>;",
+                    protobuf_crate_path(&self.customize.for_elem)
+                ));
+            },
+        )
+    }
+
+    fn write_impl_display(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            "::std::fmt::Display",
+            &format!("{}", self.rust_name()),
+            |w| {
+                w.def_fn(
+                    "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result",
+                    |w| {
+                        w.write_line(&format!(
+                            "{}::text_format::fmt(self, f)",
+                            protobuf_crate_path(&self.customize.for_elem)
+                        ));
+                    },
+                );
+            },
+        );
+    }
+
+    fn supports_derive_partial_eq(&self) -> bool {
+        // There's stack overflow in the compiler when struct has too many fields
+        // https://github.com/rust-lang/rust/issues/40119
+        self.fields.len() <= 500
+    }
+
+    fn write_struct(&self, w: &mut CodeWriter) {
+        let mut derive = Vec::new();
+        if self.supports_derive_partial_eq() {
+            derive.push("PartialEq");
+        }
+        derive.extend(&["Clone", "Default", "Debug"]);
+        w.derive(&derive);
+        write_protoc_insertion_point_for_message(
+            w,
+            &self.customize.for_elem,
+            &self.message_descriptor,
+        );
+        w.pub_struct(&format!("{}", self.rust_name()), |w| {
+            if !self.fields_except_oneof().is_empty() {
+                w.comment("message fields");
+                for field in self.fields_except_oneof() {
+                    field.write_struct_field(w);
+                }
+            }
+            if !self.oneofs().is_empty() {
+                w.comment("message oneof groups");
+                for oneof in self.oneofs() {
+                    w.field_decl_vis(
+                        Visibility::Public,
+                        &oneof.oneof.field_name().to_string(),
+                        &oneof.full_storage_type().to_code(&self.customize.for_elem),
+                    );
+                }
+            }
+            w.comment("special fields");
+
+            let customize_special_fields = self
+                .customize
+                .child(
+                    &Customize::default(),
+                    &SpecialFieldPseudoDescriptor {
+                        message: &self.message.message,
+                        field: "special_fields",
+                    },
+                )
+                .for_elem;
+
+            write_protoc_insertion_point_for_special_field(
+                w,
+                &customize_special_fields,
+                &self.message_descriptor,
+                "special_fields",
+            );
+            w.pub_field_decl(
+                "special_fields",
+                &format!(
+                    "{}::SpecialFields",
+                    protobuf_crate_path(&self.customize.for_elem)
+                ),
+            );
+        });
+    }
+
+    fn write_impl_default_for_amp(&self, w: &mut CodeWriter) {
+        w.impl_args_for_block(
+            &["'a"],
+            "::std::default::Default",
+            &format!("&'a {}", self.rust_name()),
+            |w| {
+                w.def_fn(&format!("default() -> &'a {}", self.rust_name()), |w| {
+                    w.write_line(&format!(
+                        "<{} as {}::Message>::default_instance()",
+                        self.rust_name(),
+                        protobuf_crate_path(&self.customize.for_elem),
+                    ));
+                });
+            },
+        );
+    }
+
+    fn write_dummy_impl_partial_eq(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            "::std::cmp::PartialEq",
+            &format!("{}", self.rust_name()),
+            |w| {
+                w.def_fn("eq(&self, _: &Self) -> bool", |w| {
+                    w.comment("https://github.com/rust-lang/rust/issues/40119");
+                    w.unimplemented();
+                });
+            },
+        );
+    }
+
+    pub fn write(&self, w: &mut CodeWriter) -> anyhow::Result<()> {
+        w.all_documentation(self.info, self.path);
+        self.write_struct(w);
+
+        w.write_line("");
+        self.write_impl_default_for_amp(w);
+
+        if !self.supports_derive_partial_eq() {
+            w.write_line("");
+            self.write_dummy_impl_partial_eq(w);
+        }
+
+        w.write_line("");
+        self.write_impl_self(w);
+        w.write_line("");
+        self.write_impl_message(w);
+        if !self.lite_runtime {
+            w.write_line("");
+            self.write_impl_message_full(w);
+        }
+        if !self.lite_runtime {
+            w.write_line("");
+            self.write_impl_display(w);
+
+            w.write_line("");
+            self.write_impl_value(w);
+        }
+
+        let mod_name = message_name_to_nested_mod_name(&self.message.message.name());
+
+        let oneofs = self.oneofs();
+        let nested_messages: Vec<_> = self
+            .message
+            .to_scope()
+            .messages()
+            .into_iter()
+            .filter(|nested| {
+                // ignore map entries, because they are not used in map fields
+                !nested.is_map()
+            })
+            .collect();
+        let nested_enums = self.message.to_scope().enums();
+
+        if !oneofs.is_empty() || !nested_messages.is_empty() || !nested_enums.is_empty() {
+            w.write_line("");
+            w.write_line(&format!(
+                "/// Nested message and enums of message `{}`",
+                self.message.message.name()
+            ));
+            w.pub_mod(&mod_name.to_string(), |w| {
+                let mut first = true;
+
+                for oneof in &oneofs {
+                    w.write_line("");
+                    oneof.write(w);
+                }
+
+                static NESTED_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+                let nested_type_number = *NESTED_TYPE_NUMBER.get(|| {
+                    MessageDescriptor::for_type::<DescriptorProto>()
+                        .field_by_name("nested_type")
+                        .expect("`nested_type` must exist")
+                        .proto()
+                        .number()
+                });
+
+                let mut path = self.path.to_vec();
+                path.extend(&[nested_type_number, 0]);
+                for (id, nested) in nested_messages.iter().enumerate() {
+                    let len = path.len() - 1;
+                    path[len] = id as i32;
+
+                    if !first {
+                        w.write_line("");
+                    }
+                    first = false;
+                    MessageGen::new(
+                        &self.file_descriptor,
+                        nested,
+                        self.root_scope,
+                        &self.customize,
+                        &path,
+                        self.info,
+                    )
+                    // TODO: do not unwrap.
+                    .unwrap()
+                    .write(w)
+                    // TODO: do not unwrap.
+                    .unwrap();
+                }
+
+                static ENUM_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+                let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
+                    MessageDescriptor::for_type::<DescriptorProto>()
+                        .field_by_name("enum_type")
+                        .expect("`enum_type` must exist")
+                        .proto()
+                        .number()
+                });
+
+                let len = path.len() - 2;
+                path[len] = enum_type_number;
+                for (id, enum_type) in self.message.to_scope().enums().iter().enumerate() {
+                    let len = path.len() - 1;
+                    path[len] = id as i32;
+
+                    if !first {
+                        w.write_line("");
+                    }
+                    first = false;
+                    EnumGen::new(
+                        enum_type,
+                        &self.customize,
+                        self.root_scope,
+                        &path,
+                        self.info,
+                    )
+                    .write(w);
+                }
+            });
+        }
+        Ok(())
+    }
+}
+
+pub(crate) fn message_name_to_nested_mod_name(message_name: &str) -> RustIdent {
+    let mod_name = snake_case(message_name);
+    RustIdent::new(&mod_name)
+}
diff --git a/src/gen/mod.rs b/src/gen/mod.rs
new file mode 100644
index 0000000..6d0435c
--- /dev/null
+++ b/src/gen/mod.rs
@@ -0,0 +1,21 @@
+pub(crate) mod all;
+pub(crate) mod code_writer;
+pub(crate) mod descriptor;
+pub(crate) mod enums;
+pub(crate) mod extensions;
+pub(crate) mod field;
+pub(crate) mod file;
+pub(crate) mod file_and_mod;
+pub(crate) mod file_descriptor;
+pub(crate) mod inside;
+mod map;
+pub(crate) mod message;
+pub(crate) mod mod_rs;
+pub(crate) mod oneof;
+pub(crate) mod paths;
+pub(crate) mod protoc_insertion_point;
+pub(crate) mod rust;
+pub(crate) mod rust_types_values;
+pub(crate) mod scope;
+pub(crate) mod strx;
+pub(crate) mod well_known_types;
diff --git a/src/gen/mod_rs.rs b/src/gen/mod_rs.rs
new file mode 100644
index 0000000..a149319
--- /dev/null
+++ b/src/gen/mod_rs.rs
@@ -0,0 +1,18 @@
+use crate::compiler_plugin;
+use crate::gen::code_writer::CodeWriter;
+
+pub(crate) fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
+    let v = CodeWriter::with_no_error(|w| {
+        w.comment(&format!("{}generated", "@"));
+        w.write_line("");
+        let mut mods: Vec<&String> = mods.into_iter().collect();
+        mods.sort();
+        for m in mods {
+            w.write_line(&format!("pub mod {};", m));
+        }
+    });
+    compiler_plugin::GenResult {
+        name: "mod.rs".to_owned(),
+        content: v.into_bytes(),
+    }
+}
diff --git a/src/gen/oneof.rs b/src/gen/oneof.rs
new file mode 100644
index 0000000..3f34cdc
--- /dev/null
+++ b/src/gen/oneof.rs
@@ -0,0 +1,373 @@
+//! Oneof-related codegen functions.
+
+use std::collections::HashSet;
+
+use protobuf::descriptor::field_descriptor_proto;
+use protobuf::descriptor::file_options;
+use protobuf::reflect::FieldDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name;
+use crate::gen::field::FieldGen;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::MessageGen;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::path::RustPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust_types_values::make_path;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::scope::OneofVariantWithContext;
+use crate::gen::scope::OneofWithContext;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+
+// oneof one { ... }
+#[derive(Clone)]
+pub(crate) struct OneofField<'a> {
+    pub elem: FieldElem<'a>,
+    pub oneof_variant_rust_name: RustIdent,
+    pub oneof_field_name: RustIdent,
+    pub type_name: RustIdentWithPath,
+    pub boxed: bool,
+}
+
+impl<'a> OneofField<'a> {
+    // Detecting recursion: if oneof fields contains a self-reference
+    // or another message which has a reference to self,
+    // put oneof variant into a box.
+    fn need_boxed(
+        field: &FieldDescriptor,
+        root_scope: &RootScope,
+        owner_name: &ProtobufAbsPath,
+    ) -> bool {
+        let mut visited_messages = HashSet::new();
+        let mut fields = vec![field.clone()];
+        while let Some(field) = fields.pop() {
+            if field.proto().type_() == field_descriptor_proto::Type::TYPE_MESSAGE {
+                let message_name = ProtobufAbsPath::from(field.proto().type_name());
+                if !visited_messages.insert(message_name.clone()) {
+                    continue;
+                }
+                if message_name == *owner_name {
+                    return true;
+                }
+                let message = root_scope.find_message(&message_name);
+                fields.extend(
+                    message
+                        .message
+                        .fields()
+                        .into_iter()
+                        .filter(|f| f.containing_oneof().is_some()),
+                );
+            }
+        }
+        false
+    }
+
+    pub fn parse(
+        oneof: &OneofWithContext<'a>,
+        field: &FieldDescriptor,
+        elem: FieldElem<'a>,
+        root_scope: &RootScope,
+    ) -> OneofField<'a> {
+        let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute());
+
+        OneofField {
+            elem,
+            type_name: oneof.rust_name(),
+            boxed,
+            oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()),
+            oneof_field_name: oneof.field_name(),
+        }
+    }
+
+    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
+        let t = self.elem.rust_storage_elem_type(reference);
+
+        if self.boxed {
+            RustType::Uniq(Box::new(t))
+        } else {
+            t
+        }
+    }
+
+    pub fn variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath {
+        make_path(
+            reference,
+            &self
+                .type_name
+                .to_path()
+                .with_ident(self.oneof_variant_rust_name.clone()),
+        )
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofVariantGen<'a> {
+    oneof: &'a OneofGen<'a>,
+    _variant: OneofVariantWithContext<'a>,
+    oneof_field: OneofField<'a>,
+    pub field: FieldGen<'a>,
+    _path: String,
+}
+
+impl<'a> OneofVariantGen<'a> {
+    fn parse(
+        oneof: &'a OneofGen<'a>,
+        variant: OneofVariantWithContext<'a>,
+        field: &'a FieldGen,
+        _root_scope: &RootScope,
+    ) -> OneofVariantGen<'a> {
+        OneofVariantGen {
+            oneof,
+            _variant: variant.clone(),
+            field: field.clone(),
+            _path: format!(
+                "{}::{}",
+                oneof.type_name_relative(&oneof.oneof.message.scope.rust_path_to_file()),
+                field.rust_name
+            ),
+            oneof_field: OneofField::parse(
+                variant.oneof,
+                &field.proto_field.field,
+                field.elem().clone(),
+                oneof.message.root_scope,
+            ),
+        }
+    }
+
+    pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
+        self.oneof_field.rust_type(reference)
+    }
+
+    pub fn path(&self, reference: &FileAndMod) -> RustPath {
+        RustPath::from(format!(
+            "{}::{}",
+            self.oneof.type_name_relative(&reference.relative_mod),
+            self.oneof_field.oneof_variant_rust_name,
+        ))
+    }
+
+    pub(crate) fn elem(&self) -> &FieldElem<'_> {
+        self.field.elem()
+    }
+}
+
+pub(crate) struct OneofGen<'a> {
+    // Message containing this oneof
+    message: &'a MessageGen<'a>,
+    pub oneof: OneofWithContext<'a>,
+    customize: CustomizeElemCtx<'a>,
+    lite_runtime: bool,
+}
+
+impl<'a> OneofGen<'a> {
+    pub fn parse(
+        message: &'a MessageGen,
+        oneof: OneofWithContext<'a>,
+        parent_customize: &CustomizeElemCtx<'a>,
+    ) -> OneofGen<'a> {
+        let customize = parent_customize.child(&Customize::default(), &oneof.oneof);
+        let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+            oneof
+                .message
+                .file_descriptor()
+                .proto()
+                .options
+                .optimize_for()
+                == file_options::OptimizeMode::LITE_RUNTIME
+        });
+        OneofGen {
+            message,
+            oneof,
+            customize,
+            lite_runtime,
+        }
+    }
+
+    pub fn type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath {
+        make_path(source, &self.oneof.rust_name())
+    }
+
+    pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
+        self.oneof
+            .variants()
+            .into_iter()
+            .filter_map(|v| {
+                let field = self
+                    .message
+                    .fields
+                    .iter()
+                    .filter(|f| f.proto_field.name() == v.field.name())
+                    .next()
+                    .expect(&format!("field not found by name: {}", v.field.name()));
+                match field.proto_type {
+                    field_descriptor_proto::Type::TYPE_GROUP => None,
+                    _ => Some(OneofVariantGen::parse(
+                        self,
+                        v,
+                        field,
+                        self.message.root_scope,
+                    )),
+                }
+            })
+            .collect()
+    }
+
+    pub fn full_storage_type(&self) -> RustType {
+        RustType::Option(Box::new(RustType::Oneof(
+            self.type_name_relative(
+                &self
+                    .oneof
+                    .message
+                    .scope
+                    .file_and_mod(self.customize.for_elem.clone())
+                    .relative_mod,
+            )
+            .clone(),
+        )))
+    }
+
+    fn file_and_mod(&self) -> FileAndMod {
+        let mut file_and_mod = self
+            .message
+            .message
+            .scope
+            .file_and_mod(self.customize.for_elem.clone());
+        file_and_mod
+            .relative_mod
+            .push_ident(self.message.message.mod_name());
+        file_and_mod
+    }
+
+    fn write_enum(&self, w: &mut CodeWriter) {
+        let derive = vec!["Clone", "PartialEq", "Debug"];
+        w.derive(&derive);
+        w.write_line("#[non_exhaustive]");
+        write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof);
+        w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| {
+            for variant in self.variants_except_group() {
+                write_protoc_insertion_point_for_oneof_field(
+                    w,
+                    &self.customize.for_children,
+                    &variant.field.proto_field.field,
+                );
+                w.write_line(&format!(
+                    "{}({}),",
+                    variant.oneof_field.oneof_variant_rust_name,
+                    &variant
+                        .rust_type(&self.file_and_mod())
+                        .to_code(&self.customize.for_elem)
+                ));
+            }
+        });
+    }
+
+    fn write_impl_oneof(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)),
+            self.oneof.rust_name().ident.to_string(),
+            |_w| {
+                // nothing here yet
+            },
+        );
+    }
+
+    fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "descriptor() -> {}::reflect::OneofDescriptor",
+            protobuf_crate_path(&self.customize.for_elem),
+        );
+        w.def_fn(&sig, |w| {
+            w.lazy_static(
+                "descriptor",
+                &format!(
+                    "{}::reflect::OneofDescriptor",
+                    protobuf_crate_path(&self.customize.for_elem),
+                ),
+                &protobuf_crate_path(&self.customize.for_elem).to_string(),
+            );
+            let message_type = make_path(
+                &self
+                    .oneof
+                    .message
+                    .scope()
+                    .rust_path_to_file()
+                    .append(self.oneof.message.mod_name().into_rel_path()),
+                &self.oneof.message.rust_name_to_file(),
+            );
+            let expr = format!(
+                "<{} as {}::MessageFull>::descriptor().oneof_by_name(\"{}\").unwrap()",
+                message_type,
+                protobuf_crate_path(&self.customize.for_elem),
+                self.oneof.oneof.name()
+            );
+            w.write_line(&format!("descriptor.get(|| {}).clone()", expr));
+        });
+    }
+
+    fn write_impl_oneof_full(&self, w: &mut CodeWriter) {
+        w.impl_for_block(
+            &format!(
+                "{}::OneofFull",
+                protobuf_crate_path(&self.customize.for_elem)
+            ),
+            self.oneof.rust_name().ident.to_string(),
+            |w| self.write_impl_oneof_full_fn_descriptor(w),
+        )
+    }
+
+    fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) {
+        let sig = format!(
+            "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData",
+            protobuf_crate_path(&self.customize.for_elem)
+        );
+        w.fn_block(
+            Visibility::Path(
+                self.oneof
+                    .rust_name()
+                    .path
+                    .into_relative_or_panic()
+                    .to_reverse(),
+            ),
+            &sig,
+            |w| {
+                w.write_line(&format!(
+                    "{}::reflect::GeneratedOneofDescriptorData::new::<{}>(\"{}\")",
+                    protobuf_crate_path(&self.customize.for_elem),
+                    &self.oneof.rust_name().ident,
+                    self.oneof.oneof.name(),
+                ));
+            },
+        );
+    }
+
+    fn write_impl_self(&self, w: &mut CodeWriter) {
+        w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| {
+            if !self.lite_runtime {
+                self.write_generated_oneof_descriptor_data(w);
+            }
+        });
+    }
+
+    pub fn write(&self, w: &mut CodeWriter) {
+        self.write_enum(w);
+        w.write_line("");
+        self.write_impl_oneof(w);
+        if !self.lite_runtime {
+            w.write_line("");
+            self.write_impl_oneof_full(w);
+        }
+        w.write_line("");
+        self.write_impl_self(w);
+    }
+}
diff --git a/src/gen/paths.rs b/src/gen/paths.rs
new file mode 100644
index 0000000..2bc958d
--- /dev/null
+++ b/src/gen/paths.rs
@@ -0,0 +1,109 @@
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+use crate::gen::strx;
+use crate::gen::well_known_types::WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES;
+use crate::Customize;
+
+// Copy-pasted from libsyntax.
+fn ident_start(c: char) -> bool {
+    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
+}
+
+// Copy-pasted from libsyntax.
+fn ident_continue(c: char) -> bool {
+    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'
+}
+
+pub(crate) fn proto_path_to_rust_mod(path: &str) -> RustIdent {
+    let without_dir = strx::remove_to(path, std::path::is_separator);
+    let without_suffix = strx::remove_suffix(without_dir, ".proto");
+
+    let name = without_suffix
+        .chars()
+        .enumerate()
+        .map(|(i, c)| {
+            let valid = if i == 0 {
+                ident_start(c)
+            } else {
+                ident_continue(c)
+            };
+            if valid {
+                c
+            } else {
+                '_'
+            }
+        })
+        .collect::<String>();
+
+    RustIdent::new(&name)
+}
+
+/// Used in protobuf-codegen-identical-test
+pub fn proto_name_to_rs(proto_file_path: &str) -> String {
+    format!("{}.rs", proto_path_to_rust_mod(proto_file_path))
+}
+
+pub(crate) fn proto_path_to_fn_file_descriptor(
+    proto_path: &str,
+    customize: &Customize,
+) -> RustPath {
+    let protobuf_crate = protobuf_crate_path(customize);
+    match proto_path {
+        "rustproto.proto" => protobuf_crate.append("rustproto::file_descriptor".into()),
+        "google/protobuf/descriptor.proto" => {
+            protobuf_crate.append("descriptor::file_descriptor".into())
+        }
+        s if WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES.contains(&s) => protobuf_crate
+            .append_ident("well_known_types".into())
+            .append_ident(proto_path_to_rust_mod(s))
+            .append_ident("file_descriptor".into()),
+        s => RustPath::super_path()
+            .append_ident(proto_path_to_rust_mod(s))
+            .append_ident("file_descriptor".into()),
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::proto_path_to_rust_mod;
+    use crate::gen::rust::ident::RustIdent;
+
+    #[test]
+    fn test_mod_path_proto_ext() {
+        assert_eq!(
+            RustIdent::from("proto"),
+            proto_path_to_rust_mod("proto.proto")
+        );
+    }
+
+    #[test]
+    fn test_mod_path_unknown_ext() {
+        assert_eq!(
+            RustIdent::from("proto_proto3"),
+            proto_path_to_rust_mod("proto.proto3")
+        );
+    }
+
+    #[test]
+    fn test_mod_path_empty_ext() {
+        assert_eq!(RustIdent::from("proto"), proto_path_to_rust_mod("proto"));
+    }
+
+    #[test]
+    fn test_mod_path_dir() {
+        assert_eq!(
+            RustIdent::from("baz"),
+            proto_path_to_rust_mod("foo/bar/baz.proto"),
+        )
+    }
+
+    #[cfg(target_os = "windows")]
+    #[test]
+    fn test_mod_path_dir_backslashes() {
+        assert_eq!(
+            RustIdent::from("baz"),
+            proto_path_to_rust_mod("foo\\bar\\baz.proto"),
+        )
+    }
+}
diff --git a/src/gen/protoc_insertion_point.rs b/src/gen/protoc_insertion_point.rs
new file mode 100644
index 0000000..0083215
--- /dev/null
+++ b/src/gen/protoc_insertion_point.rs
@@ -0,0 +1,80 @@
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::EnumValueDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::Customize;
+
+/// Write `// @protoc_insertion_point(...)` before the element.
+///
+/// This is similar to what `protoc` codegen does for C++ or Java.
+/// This can be used to modify the generated code.
+fn write_protoc_insertion_point(w: &mut CodeWriter, customize: &Customize, arg: &str) {
+    for line in customize.before.iter().flat_map(|s| s.lines()) {
+        w.write_line(line);
+    }
+    w.comment(&format!("@@protoc_insertion_point({})", arg));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_message(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    message: &MessageDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("message:{}", message.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_field(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    field: &FieldDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("field:{}", field.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_special_field(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    message: &MessageDescriptor,
+    field: &str,
+) {
+    write_protoc_insertion_point(
+        w,
+        customize,
+        &format!("special_field:{}.{}", message.full_name(), field),
+    );
+}
+
+pub(crate) fn write_protoc_insertion_point_for_enum(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    enumeration: &EnumDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("enum:{}", enumeration.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_enum_value(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    value: &EnumValueDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("enum_value:{}", value.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_oneof(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    oneof: &OneofDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("oneof:{}", oneof.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_oneof_field(
+    w: &mut CodeWriter,
+    customize: &Customize,
+    field: &FieldDescriptor,
+) {
+    write_protoc_insertion_point(w, customize, &format!("oneof_field:{}", field.full_name()));
+}
diff --git a/src/gen/rust/component.rs b/src/gen/rust/component.rs
new file mode 100644
index 0000000..8bf9c85
--- /dev/null
+++ b/src/gen/rust/component.rs
@@ -0,0 +1,34 @@
+use std::fmt;
+use std::fmt::Formatter;
+
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::keywords::parse_rust_keyword;
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum RustPathComponent {
+    Ident(RustIdent),
+    Keyword(&'static str),
+}
+
+impl fmt::Display for RustPathComponent {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            RustPathComponent::Ident(ident) => write!(f, "{}", ident),
+            RustPathComponent::Keyword(keyword) => write!(f, "{}", keyword),
+        }
+    }
+}
+
+impl RustPathComponent {
+    pub(crate) const SUPER: RustPathComponent = RustPathComponent::Keyword("super");
+
+    pub(crate) fn parse(s: &str) -> RustPathComponent {
+        if s.starts_with("r#") {
+            RustPathComponent::Ident(RustIdent::new(&s[2..]))
+        } else if let Some(kw) = parse_rust_keyword(s) {
+            RustPathComponent::Keyword(kw)
+        } else {
+            RustPathComponent::Ident(RustIdent::new(s))
+        }
+    }
+}
diff --git a/src/gen/rust/ident.rs b/src/gen/rust/ident.rs
new file mode 100644
index 0000000..df1d1f0
--- /dev/null
+++ b/src/gen/rust/ident.rs
@@ -0,0 +1,62 @@
+use std::fmt;
+
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::keywords::is_rust_keyword;
+use crate::gen::rust::rel_path::RustRelativePath;
+
+/// Valid Rust identifier
+#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+pub(crate) struct RustIdent(String);
+
+impl RustIdent {
+    pub fn new(s: &str) -> RustIdent {
+        assert!(!s.is_empty());
+        assert!(!s.contains("/"), "{}", s);
+        assert!(!s.contains("."), "{}", s);
+        assert!(!s.contains(":"), "{}", s);
+        assert!(!s.contains(" "), "{}", s);
+        assert!(!s.contains("#"), "{}", s);
+        RustIdent(s.to_owned())
+    }
+
+    pub(crate) fn get(&self) -> &str {
+        &self.0
+    }
+
+    pub fn into_string(self) -> String {
+        self.0
+    }
+
+    pub fn to_path(&self) -> RustIdentWithPath {
+        RustIdentWithPath::from(&self.0)
+    }
+
+    pub(crate) fn into_rel_path(self) -> RustRelativePath {
+        RustRelativePath::from_idents([self])
+    }
+}
+
+impl fmt::Display for RustIdent {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // Rust-protobuf uses `_` suffix to escape identifiers instead of raw identifiers
+        // because some identifiers cannot be escaped as raw identifiers,
+        // e.g. `r#self` is not a valid raw identifier.
+        if is_rust_keyword(&self.0) {
+            write!(f, "{}_", self.0)
+        } else {
+            write!(f, "{}", self.0)
+        }
+    }
+}
+
+impl From<&'_ str> for RustIdent {
+    fn from(s: &str) -> Self {
+        RustIdent::new(s)
+    }
+}
+
+impl From<String> for RustIdent {
+    fn from(s: String) -> Self {
+        RustIdent::new(&s)
+    }
+}
diff --git a/src/gen/rust/ident_with_path.rs b/src/gen/rust/ident_with_path.rs
new file mode 100644
index 0000000..bb121a7
--- /dev/null
+++ b/src/gen/rust/ident_with_path.rs
@@ -0,0 +1,45 @@
+use std::fmt;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+
+#[derive(Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustIdentWithPath {
+    pub path: RustPath,
+    pub ident: RustIdent,
+}
+
+impl RustIdentWithPath {
+    pub fn new(s: String) -> RustIdentWithPath {
+        let mut path = RustPath::from(s);
+        let ident = match path.path.path.pop() {
+            None => panic!("empty path"),
+            Some(RustPathComponent::Ident(ident)) => ident,
+            Some(RustPathComponent::Keyword(kw)) => {
+                panic!("last path component is a keyword: {}", kw)
+            }
+        };
+        RustIdentWithPath { path, ident }
+    }
+
+    pub fn prepend_ident(&mut self, ident: RustIdent) {
+        self.path.prepend_ident(ident)
+    }
+
+    pub fn to_path(&self) -> RustPath {
+        self.path.clone().append_ident(self.ident.clone())
+    }
+}
+
+impl<S: Into<String>> From<S> for RustIdentWithPath {
+    fn from(s: S) -> Self {
+        RustIdentWithPath::new(s.into())
+    }
+}
+
+impl fmt::Display for RustIdentWithPath {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Display::fmt(&self.to_path(), f)
+    }
+}
diff --git a/src/gen/rust/keywords.rs b/src/gen/rust/keywords.rs
new file mode 100644
index 0000000..155977d
--- /dev/null
+++ b/src/gen/rust/keywords.rs
@@ -0,0 +1,86 @@
+#[cfg_attr(rustfmt, rustfmt_skip)]
+static RUST_KEYWORDS: &'static [&'static str] = &[
+    "_",
+    "as",
+    "async",
+    "await",
+    "break",
+    "crate",
+    "dyn",
+    "else",
+    "enum",
+    "extern",
+    "false",
+    "fn",
+    "for",
+    "if",
+    "impl",
+    "in",
+    "let",
+    "loop",
+    "match",
+    "mod",
+    "move",
+    "mut",
+    "pub",
+    "ref",
+    "return",
+    "static",
+    "self",
+    "Self",
+    "struct",
+    "super",
+    "true",
+    "trait",
+    "type",
+    "unsafe",
+    "use",
+    "while",
+    "continue",
+    "box",
+    "const",
+    "where",
+    "virtual",
+    "proc",
+    "alignof",
+    "become",
+    "offsetof",
+    "priv",
+    "pure",
+    "sizeof",
+    "typeof",
+    "unsized",
+    "yield",
+    "do",
+    "abstract",
+    "final",
+    "override",
+    "macro",
+];
+
+// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/3
+#[cfg_attr(rustfmt, rustfmt_skip)]
+static RUST_KEYWORDS_WHICH_CANNOT_BE_RAW: &'static [&'static str] = &[
+    "super",
+    "self",
+    "Self",
+    "extern",
+    "crate",
+];
+
+pub(crate) fn parse_rust_keyword(word: &str) -> Option<&'static str> {
+    RUST_KEYWORDS.iter().cloned().find(|&kw| kw == word)
+}
+
+pub(crate) fn is_rust_keyword(ident: &str) -> bool {
+    parse_rust_keyword(ident).is_some()
+}
+
+#[allow(dead_code)]
+pub(crate) fn is_rust_keyword_which_cannot_be_raw(ident: &str) -> bool {
+    RUST_KEYWORDS_WHICH_CANNOT_BE_RAW
+        .iter()
+        .cloned()
+        .find(|&kw| kw == ident)
+        .is_some()
+}
diff --git a/src/gen/rust/mod.rs b/src/gen/rust/mod.rs
new file mode 100644
index 0000000..343ba92
--- /dev/null
+++ b/src/gen/rust/mod.rs
@@ -0,0 +1,8 @@
+pub(crate) mod component;
+pub(crate) mod ident;
+pub(crate) mod ident_with_path;
+pub(crate) mod keywords;
+pub(crate) mod path;
+pub(crate) mod quote;
+pub(crate) mod rel_path;
+pub(crate) mod snippets;
diff --git a/src/gen/rust/path.rs b/src/gen/rust/path.rs
new file mode 100644
index 0000000..84d38a3
--- /dev/null
+++ b/src/gen/rust/path.rs
@@ -0,0 +1,99 @@
+use std::fmt;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+
+#[derive(Default, Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustPath {
+    pub(crate) absolute: bool,
+    pub(crate) path: RustRelativePath,
+}
+
+impl RustPath {
+    pub fn super_path() -> RustPath {
+        RustPath::from("super")
+    }
+
+    pub fn is_absolute(&self) -> bool {
+        self.absolute
+    }
+
+    pub fn with_ident(self, ident: RustIdent) -> RustIdentWithPath {
+        RustIdentWithPath { path: self, ident }
+    }
+
+    pub fn first(&self) -> Option<RustPathComponent> {
+        assert!(!self.absolute);
+        self.path.first()
+    }
+
+    pub fn remove_first(&mut self) -> Option<RustPathComponent> {
+        assert!(!self.absolute);
+        self.path.remove_first()
+    }
+
+    pub fn prepend_ident(&mut self, ident: RustIdent) {
+        assert!(!self.absolute);
+        self.path.prepend_ident(ident);
+    }
+
+    pub fn append(self, path: RustPath) -> RustPath {
+        if path.absolute {
+            path
+        } else {
+            RustPath {
+                absolute: self.absolute,
+                path: self.path.append(path.path),
+            }
+        }
+    }
+
+    pub(crate) fn append_component(mut self, component: RustPathComponent) -> RustPath {
+        self.path.path.push(component);
+        self
+    }
+
+    pub fn append_ident(self, ident: RustIdent) -> RustPath {
+        self.append_component(RustPathComponent::Ident(ident))
+    }
+
+    pub fn append_with_ident(self, path: RustIdentWithPath) -> RustIdentWithPath {
+        self.append(path.path).with_ident(path.ident)
+    }
+
+    pub fn into_relative_or_panic(self) -> RustRelativePath {
+        assert!(!self.absolute);
+        self.path
+    }
+}
+
+impl From<&'_ str> for RustPath {
+    fn from(s: &str) -> Self {
+        let (s, absolute) = if s.starts_with("::") {
+            (&s[2..], true)
+        } else {
+            (s, false)
+        };
+        RustPath {
+            absolute,
+            path: RustRelativePath::from(s),
+        }
+    }
+}
+
+impl From<String> for RustPath {
+    fn from(s: String) -> Self {
+        RustPath::from(&s[..])
+    }
+}
+
+impl fmt::Display for RustPath {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        if self.absolute {
+            write!(f, "::")?;
+        }
+        write!(f, "{}", self.path)
+    }
+}
diff --git a/src/gen/rust/quote.rs b/src/gen/rust/quote.rs
new file mode 100644
index 0000000..f01b4ee
--- /dev/null
+++ b/src/gen/rust/quote.rs
@@ -0,0 +1,58 @@
+fn hex_digit(value: u32) -> char {
+    if value < 10 {
+        (b'0' + value as u8) as char
+    } else if value < 0x10 {
+        (b'a' + value as u8 - 10) as char
+    } else {
+        unreachable!()
+    }
+}
+
+pub fn quote_escape_str(s: &str) -> String {
+    let mut buf = String::new();
+    buf.push('"');
+    buf.extend(s.chars().flat_map(|c| c.escape_default()));
+    buf.push('"');
+    buf
+}
+
+pub fn quote_escape_bytes(bytes: &[u8]) -> String {
+    let mut buf = String::new();
+    buf.push('b');
+    buf.push('"');
+    for &b in bytes {
+        match b {
+            b'\n' => buf.push_str(r"\n"),
+            b'\r' => buf.push_str(r"\r"),
+            b'\t' => buf.push_str(r"\t"),
+            b'"' => buf.push_str("\\\""),
+            b'\\' => buf.push_str(r"\\"),
+            b'\x20'..=b'\x7e' => buf.push(b as char),
+            _ => {
+                buf.push_str(r"\x");
+                buf.push(hex_digit((b as u32) >> 4));
+                buf.push(hex_digit((b as u32) & 0x0f));
+            }
+        }
+    }
+    buf.push('"');
+    buf
+}
+
+#[cfg(test)]
+mod test {
+
+    use super::*;
+
+    #[test]
+    fn test_quote_escape_bytes() {
+        assert_eq!("b\"\"", quote_escape_bytes(b""));
+        assert_eq!("b\"xyZW\"", quote_escape_bytes(b"xyZW"));
+        assert_eq!("b\"aa\\\"bb\"", quote_escape_bytes(b"aa\"bb"));
+        assert_eq!("b\"aa\\r\\n\\tbb\"", quote_escape_bytes(b"aa\r\n\tbb"));
+        assert_eq!(
+            "b\"\\x00\\x01\\x12\\xfe\\xff\"",
+            quote_escape_bytes(b"\x00\x01\x12\xfe\xff")
+        );
+    }
+}
diff --git a/src/gen/rust/rel_path.rs b/src/gen/rust/rel_path.rs
new file mode 100644
index 0000000..6188ce6
--- /dev/null
+++ b/src/gen/rust/rel_path.rs
@@ -0,0 +1,97 @@
+use std::fmt;
+use std::iter;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+
+#[derive(Default, Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustRelativePath {
+    pub(crate) path: Vec<RustPathComponent>,
+}
+
+impl RustRelativePath {
+    pub fn into_path(self) -> RustPath {
+        RustPath {
+            absolute: false,
+            path: self,
+        }
+    }
+
+    pub fn _empty() -> RustRelativePath {
+        RustRelativePath { path: Vec::new() }
+    }
+
+    pub fn from_components<I: IntoIterator<Item = RustPathComponent>>(i: I) -> RustRelativePath {
+        RustRelativePath {
+            path: i.into_iter().collect(),
+        }
+    }
+
+    pub fn from_idents<I: IntoIterator<Item = RustIdent>>(i: I) -> RustRelativePath {
+        Self::from_components(i.into_iter().map(RustPathComponent::Ident))
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.path.is_empty()
+    }
+
+    pub fn first(&self) -> Option<RustPathComponent> {
+        self.path.iter().cloned().next()
+    }
+
+    pub fn remove_first(&mut self) -> Option<RustPathComponent> {
+        if self.path.is_empty() {
+            None
+        } else {
+            Some(self.path.remove(0))
+        }
+    }
+
+    pub fn prepend_ident(&mut self, ident: RustIdent) {
+        self.path.insert(0, RustPathComponent::Ident(ident));
+    }
+
+    pub fn append(mut self, path: RustRelativePath) -> RustRelativePath {
+        for c in path.path {
+            self.path.push(c);
+        }
+        self
+    }
+
+    pub fn push_ident(&mut self, ident: RustIdent) {
+        self.path.push(RustPathComponent::Ident(ident));
+    }
+
+    pub fn append_ident(mut self, ident: RustIdent) -> RustRelativePath {
+        self.push_ident(ident);
+        self
+    }
+
+    pub fn to_reverse(&self) -> RustRelativePath {
+        RustRelativePath::from_components(
+            iter::repeat(RustPathComponent::SUPER).take(self.path.len()),
+        )
+    }
+}
+
+impl fmt::Display for RustRelativePath {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        for (i, c) in self.path.iter().enumerate() {
+            if i != 0 {
+                write!(f, "::")?;
+            }
+            write!(f, "{}", c)?;
+        }
+        Ok(())
+    }
+}
+
+impl From<&'_ str> for RustRelativePath {
+    fn from(s: &str) -> Self {
+        assert!(!s.starts_with("::"), "path is absolute: {:?}", s);
+        RustRelativePath {
+            path: s.split("::").map(RustPathComponent::parse).collect(),
+        }
+    }
+}
diff --git a/src/gen/rust/snippets.rs b/src/gen/rust/snippets.rs
new file mode 100644
index 0000000..c10c4a9
--- /dev/null
+++ b/src/gen/rust/snippets.rs
@@ -0,0 +1,10 @@
+pub(crate) const EXPR_NONE: &str = "::std::option::Option::None";
+pub(crate) const EXPR_VEC_NEW: &str = "::std::vec::Vec::new()";
+
+fn expr_vec_with_capacity(capacity: &str) -> String {
+    format!("::std::vec::Vec::with_capacity({})", capacity)
+}
+
+pub(crate) fn expr_vec_with_capacity_const(capacity: usize) -> String {
+    expr_vec_with_capacity(&capacity.to_string())
+}
diff --git a/src/gen/rust_types_values.rs b/src/gen/rust_types_values.rs
new file mode 100644
index 0000000..cd67f2b
--- /dev/null
+++ b/src/gen/rust_types_values.rs
@@ -0,0 +1,617 @@
+use std::cmp;
+
+use once_cell::sync::Lazy;
+use protobuf::descriptor::*;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+use regex::Regex;
+
+use crate::customize::Customize;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::path::RustPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust::snippets::EXPR_VEC_NEW;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+use crate::gen::strx::capitalize;
+use crate::gen::well_known_types::is_well_known_type_full;
+
+// Represent subset of rust types used in generated code
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum RustType {
+    // integer: signed?, size in bits
+    Int(bool, u32),
+    // param is size in bits
+    Float(u32),
+    Bool,
+    Vec(Box<RustType>),
+    HashMap(Box<RustType>, Box<RustType>),
+    String,
+    // [T], not &[T]
+    Slice(Box<RustType>),
+    // str, not &str
+    Str,
+    Option(Box<RustType>),
+    MessageField(Box<RustType>),
+    // Box<T>
+    Uniq(Box<RustType>),
+    // &T
+    Ref(Box<RustType>),
+    // protobuf message
+    Message(RustTypeMessage),
+    // protobuf enum, not any enum
+    Enum(RustIdentWithPath, RustIdent, i32),
+    // protobuf enum or unknown
+    EnumOrUnknown(RustIdentWithPath, RustIdent, i32),
+    // oneof enum
+    Oneof(RustIdentWithPath),
+    // bytes::Bytes
+    Bytes,
+    // chars::Chars
+    Chars,
+    // group
+    Group,
+}
+
+impl RustType {
+    #[inline]
+    pub(crate) fn to_code(&self, customize: &Customize) -> String {
+        match *self {
+            RustType::Int(true, bits) => format!("i{}", bits),
+            RustType::Int(false, bits) => format!("u{}", bits),
+            RustType::Float(bits) => format!("f{}", bits),
+            RustType::Bool => format!("bool"),
+            RustType::Vec(ref param) => format!("::std::vec::Vec<{}>", param.to_code(customize)),
+            RustType::HashMap(ref key, ref value) => format!(
+                "::std::collections::HashMap<{}, {}>",
+                key.to_code(customize),
+                value.to_code(customize)
+            ),
+            RustType::String => format!("::std::string::String"),
+            RustType::Slice(ref param) => format!("[{}]", param.to_code(customize)),
+            RustType::Str => format!("str"),
+            RustType::Option(ref param) => {
+                format!("::std::option::Option<{}>", param.to_code(customize))
+            }
+            RustType::MessageField(ref param) => format!(
+                "{}::MessageField<{}>",
+                protobuf_crate_path(customize),
+                param.to_code(customize)
+            ),
+            RustType::Uniq(ref param) => format!("::std::boxed::Box<{}>", param.to_code(customize)),
+            RustType::Ref(ref param) => format!("&{}", param.to_code(customize)),
+            RustType::Message(ref name) => format!("{}", name),
+            RustType::Enum(ref name, ..) | RustType::Oneof(ref name) => format!("{}", name),
+            RustType::EnumOrUnknown(ref name, ..) => format!(
+                "{}::EnumOrUnknown<{}>",
+                protobuf_crate_path(customize),
+                name
+            ),
+            RustType::Group => format!("<group>"),
+            RustType::Bytes => format!("::bytes::Bytes"),
+            RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)),
+        }
+    }
+}
+
+impl RustType {
+    pub(crate) fn u8() -> RustType {
+        RustType::Int(false, 8)
+    }
+
+    pub(crate) fn i32() -> RustType {
+        RustType::Int(true, 32)
+    }
+
+    /// `&str`.
+    pub(crate) fn amp_str() -> RustType {
+        RustType::Str.wrap_ref()
+    }
+
+    /// `&[u8]`.
+    pub(crate) fn amp_slice_of_u8() -> RustType {
+        RustType::u8().wrap_slice().wrap_ref()
+    }
+
+    /// Type is rust primitive?
+    pub(crate) fn is_primitive(&self) -> bool {
+        match *self {
+            RustType::Int(..) | RustType::Float(..) | RustType::Bool => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_u8(&self) -> bool {
+        match *self {
+            RustType::Int(false, 8) => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_copy(&self) -> bool {
+        if self.is_primitive() {
+            true
+        } else if let RustType::Enum(..) = *self {
+            true
+        } else if let RustType::EnumOrUnknown(..) = *self {
+            true
+        } else {
+            false
+        }
+    }
+
+    fn is_str(&self) -> bool {
+        match *self {
+            RustType::Str => true,
+            _ => false,
+        }
+    }
+
+    fn is_string(&self) -> bool {
+        match *self {
+            RustType::String => true,
+            _ => false,
+        }
+    }
+
+    fn is_slice(&self) -> Option<&RustType> {
+        match *self {
+            RustType::Slice(ref v) => Some(&**v),
+            _ => None,
+        }
+    }
+
+    fn is_slice_u8(&self) -> bool {
+        match self.is_slice() {
+            Some(t) => t.is_u8(),
+            None => false,
+        }
+    }
+
+    fn is_message(&self) -> bool {
+        match *self {
+            RustType::Message(..) => true,
+            _ => false,
+        }
+    }
+
+    fn is_enum(&self) -> bool {
+        match *self {
+            RustType::Enum(..) => true,
+            _ => false,
+        }
+    }
+
+    fn is_enum_or_unknown(&self) -> bool {
+        match *self {
+            RustType::EnumOrUnknown(..) => true,
+            _ => false,
+        }
+    }
+
+    pub fn is_ref(&self) -> Option<&RustType> {
+        match *self {
+            RustType::Ref(ref v) => Some(&**v),
+            _ => None,
+        }
+    }
+
+    pub fn is_box(&self) -> Option<&RustType> {
+        match *self {
+            RustType::Uniq(ref v) => Some(&**v),
+            _ => None,
+        }
+    }
+
+    // default value for type
+    pub fn default_value(&self, customize: &Customize, const_expr: bool) -> String {
+        match *self {
+            RustType::Ref(ref t) if t.is_str() => "\"\"".to_string(),
+            RustType::Ref(ref t) if t.is_slice().is_some() => "&[]".to_string(),
+            RustType::Int(..) => "0".to_string(),
+            RustType::Float(..) => "0.".to_string(),
+            RustType::Bool => "false".to_string(),
+            RustType::Vec(..) => EXPR_VEC_NEW.to_string(),
+            RustType::HashMap(..) => "::std::collections::HashMap::new()".to_string(),
+            RustType::String => "::std::string::String::new()".to_string(),
+            RustType::Bytes => "::bytes::Bytes::new()".to_string(),
+            RustType::Chars => format!("{}::Chars::new()", protobuf_crate_path(customize)),
+            RustType::Option(..) => EXPR_NONE.to_string(),
+            RustType::MessageField(..) => {
+                format!("{}::MessageField::none()", protobuf_crate_path(customize))
+            }
+            RustType::Message(ref name) => format!("{}::new()", name),
+            RustType::Ref(ref m) if m.is_message() => match **m {
+                RustType::Message(ref name) => name.default_instance(customize),
+                _ => unreachable!(),
+            },
+            // Note: default value of enum type may not be equal to default value of field
+            RustType::Enum(ref name, ref default, ..) => format!("{}::{}", name, default),
+            RustType::EnumOrUnknown(_, _, number) if const_expr => format!(
+                "{}::EnumOrUnknown::from_i32({})",
+                protobuf_crate_path(customize),
+                number,
+            ),
+            RustType::EnumOrUnknown(ref name, ref default, ..) if !const_expr => format!(
+                "{}::EnumOrUnknown::new({}::{})",
+                protobuf_crate_path(customize),
+                name,
+                default
+            ),
+            _ => panic!("cannot create default value for: {:?}", self),
+        }
+    }
+
+    pub fn default_value_typed(self, customize: &Customize, const_expr: bool) -> RustValueTyped {
+        RustValueTyped {
+            value: self.default_value(customize, const_expr),
+            rust_type: self,
+        }
+    }
+
+    /// Emit a code to clear a variable `v`
+    pub fn clear(&self, v: &str, customize: &Customize) -> String {
+        match *self {
+            RustType::Option(..) => format!("{} = {}", v, EXPR_NONE),
+            RustType::Vec(..)
+            | RustType::Bytes
+            | RustType::Chars
+            | RustType::String
+            | RustType::MessageField(..)
+            | RustType::HashMap(..) => format!("{}.clear()", v),
+            RustType::Bool
+            | RustType::Float(..)
+            | RustType::Int(..)
+            | RustType::Enum(..)
+            | RustType::EnumOrUnknown(..) => {
+                format!("{} = {}", v, self.default_value(customize, false))
+            }
+            ref ty => panic!("cannot clear type: {:?}", ty),
+        }
+    }
+
+    // expression to convert `v` of type `self` to type `target`
+    pub fn into_target(&self, target: &RustType, v: &str, customize: &Customize) -> String {
+        self.try_into_target(target, v, customize)
+            .expect(&format!("failed to convert {:?} into {:?}", self, target))
+    }
+
+    // https://github.com/rust-lang-nursery/rustfmt/issues/3131
+    #[cfg_attr(rustfmt, rustfmt_skip)]
+    fn try_into_target(&self, target: &RustType, v: &str, customize: &Customize) -> Result<String, ()> {
+        {
+            if let Some(t1) = self.is_ref().and_then(|t| t.is_box()) {
+                if let Some(t2) = target.is_ref() {
+                    if t1 == t2 {
+                        return Ok(format!("&**{}", v));
+                    }
+                }
+            }
+        }
+
+        match (self, target) {
+            (x, y) if x == y => return Ok(format!("{}", v)),
+            (&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
+            (x, &RustType::Uniq(ref y)) if *x == **y => {
+                return Ok(format!("::std::boxed::Box::new({})", v))
+            }
+            (&RustType::Uniq(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
+            (&RustType::String, &RustType::Ref(ref t)) if **t == RustType::Str => {
+                return Ok(format!("&{}", v))
+            }
+            (&RustType::Chars, &RustType::Ref(ref t)) if **t == RustType::Str => {
+                return Ok(format!("&{}", v))
+            }
+            (&RustType::Ref(ref t1), &RustType::Ref(ref t2)) if t1.is_string() && t2.is_str() => {
+                return Ok(format!("&{}", v))
+            }
+            (&RustType::Ref(ref t1), &RustType::String)
+                if match **t1 {
+                       RustType::Str => true,
+                       _ => false,
+                   } => return Ok(format!("{}.to_owned()", v)),
+            (&RustType::Ref(ref t1), &RustType::Chars)
+                if match **t1 {
+                       RustType::Str => true,
+                       _ => false,
+                   } => {
+                return Ok(format!("<{}::Chars as ::std::convert::From<_>>::from({}.to_owned())",
+                    protobuf_crate_path(customize), v))
+            },
+            (&RustType::Ref(ref t1), &RustType::Vec(ref t2))
+                if match (&**t1, &**t2) {
+                       (&RustType::Slice(ref x), ref y) => **x == **y,
+                       _ => false,
+                   } => return Ok(format!("{}.to_vec()", v)),
+            (&RustType::Ref(ref t1), &RustType::Bytes)
+                if t1.is_slice_u8() =>
+                    return Ok(format!("<::bytes::Bytes as ::std::convert::From<_>>::from({}.to_vec())", v)),
+            (&RustType::Vec(ref x), &RustType::Ref(ref t))
+                if match **t {
+                       RustType::Slice(ref y) => x == y,
+                       _ => false,
+                   } => return Ok(format!("&{}", v)),
+            (&RustType::Bytes, &RustType::Ref(ref t))
+                if match **t {
+                       RustType::Slice(ref y) => **y == RustType::u8(),
+                       _ => false,
+                   } => return Ok(format!("&{}", v)),
+            (&RustType::Ref(ref t1), &RustType::Ref(ref t2))
+                if match (&**t1, &**t2) {
+                       (&RustType::Vec(ref x), &RustType::Slice(ref y)) => x == y,
+                       _ => false,
+                   } => return Ok(format!("&{}", v)),
+            (&RustType::Enum(..), &RustType::Int(true, 32)) => {
+                return Ok(format!("{}::Enum::value(&{})", protobuf_crate_path(customize), v))
+            },
+            (&RustType::EnumOrUnknown(..), &RustType::Int(true, 32)) => {
+                return Ok(format!("{}::EnumOrUnknown::value(&{})", protobuf_crate_path(customize), v))
+            },
+            (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum() => {
+                return Ok(format!("{}::Enum::value({})", protobuf_crate_path(customize), v))
+            }
+            (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum_or_unknown() => {
+                return Ok(format!("{}::EnumOrUnknown::value({})", protobuf_crate_path(customize), v))
+            },
+            (&RustType::EnumOrUnknown(ref f, ..), &RustType::Enum(ref t, ..)) if f == t => {
+                return Ok(format!("{}::EnumOrUnknown::enum_value_or_default(&{})", protobuf_crate_path(customize), v))
+            }
+            (&RustType::Enum(ref f, ..), &RustType::EnumOrUnknown(ref t, ..)) if f == t => {
+                return Ok(format!("{}::EnumOrUnknown::new({})", protobuf_crate_path(customize), v))
+            }
+            _ => (),
+        };
+
+        if let &RustType::Ref(ref s) = self {
+            if let Ok(conv) = s.try_into_target(target, v, customize) {
+                return Ok(conv);
+            }
+        }
+
+        Err(())
+    }
+
+    /// Type to view data of this type
+    pub fn ref_type(&self) -> RustType {
+        RustType::Ref(Box::new(match self {
+            &RustType::String | &RustType::Chars => RustType::Str,
+            &RustType::Vec(ref p) => RustType::Slice(p.clone()),
+            &RustType::Bytes => RustType::Slice(Box::new(RustType::u8())),
+            &RustType::Message(ref p) => RustType::Message(p.clone()),
+            &RustType::Uniq(ref p) => RustType::Uniq(p.clone()),
+            x => panic!("no ref type for {:?}", x),
+        }))
+    }
+
+    pub(crate) fn wrap_ref(&self) -> RustType {
+        RustType::Ref(Box::new(self.clone()))
+    }
+
+    pub(crate) fn wrap_slice(&self) -> RustType {
+        RustType::Slice(Box::new(self.clone()))
+    }
+
+    pub fn elem_type(&self) -> RustType {
+        match self {
+            &RustType::Option(ref ty) => (**ty).clone(),
+            &RustType::MessageField(ref ty) => (**ty).clone(),
+            x => panic!("cannot get elem type of {:?}", x),
+        }
+    }
+
+    // type of `v` in `for v in xxx`
+    pub fn iter_elem_type(&self) -> RustType {
+        match self {
+            &RustType::Vec(ref ty)
+            | &RustType::Option(ref ty)
+            | &RustType::MessageField(ref ty) => RustType::Ref(ty.clone()),
+            x => panic!("cannot iterate {:?}", x),
+        }
+    }
+
+    pub fn value(self, value: String) -> RustValueTyped {
+        RustValueTyped {
+            value: value,
+            rust_type: self,
+        }
+    }
+}
+
+/// Representation of an expression in code generator: text and type
+pub(crate) struct RustValueTyped {
+    pub value: String,
+    pub rust_type: RustType,
+}
+
+impl RustValueTyped {
+    pub fn into_type(&self, target: RustType, customize: &Customize) -> RustValueTyped {
+        let target_value = self.rust_type.into_target(&target, &self.value, customize);
+        RustValueTyped {
+            value: target_value,
+            rust_type: target,
+        }
+    }
+
+    pub fn boxed(self, customize: &Customize) -> RustValueTyped {
+        self.into_type(RustType::Uniq(Box::new(self.rust_type.clone())), customize)
+    }
+}
+
+fn file_last_component(file: &str) -> &str {
+    let bs = file.rfind('\\').map(|i| i + 1).unwrap_or(0);
+    let fs = file.rfind('/').map(|i| i + 1).unwrap_or(0);
+    &file[cmp::max(fs, bs)..]
+}
+
+#[cfg(test)]
+#[test]
+fn test_file_last_component() {
+    assert_eq!("ab.proto", file_last_component("ab.proto"));
+    assert_eq!("ab.proto", file_last_component("xx/ab.proto"));
+    assert_eq!("ab.proto", file_last_component("xx\\ab.proto"));
+    assert_eq!("ab.proto", file_last_component("yy\\xx\\ab.proto"));
+}
+
+fn is_descriptor_proto(file: &FileDescriptor) -> bool {
+    file.package() == "google.protobuf" && file_last_component(file.name()) == "descriptor.proto"
+}
+
+fn make_path_to_path(source: &RustRelativePath, dest: &RustPath) -> RustPath {
+    if dest.is_absolute() {
+        return dest.clone();
+    }
+
+    let mut source = source.clone();
+    let mut dest = dest.clone();
+    while !source.is_empty() && source.first() == dest.first() {
+        source.remove_first().unwrap();
+        dest.remove_first().unwrap();
+    }
+    source.to_reverse().into_path().append(dest)
+}
+
+pub(crate) fn make_path(source: &RustRelativePath, dest: &RustIdentWithPath) -> RustIdentWithPath {
+    make_path_to_path(source, &dest.path).with_ident(dest.ident.clone())
+}
+
+pub(crate) fn message_or_enum_to_rust_relative(
+    message_or_enum: &dyn WithScope,
+    current: &FileAndMod,
+) -> RustIdentWithPath {
+    let same_file = message_or_enum.file_descriptor().name() == current.file;
+    if same_file {
+        // field type is a message or enum declared in the same file
+        make_path(&current.relative_mod, &message_or_enum.rust_name_to_file())
+    } else if let Some(name) = is_well_known_type_full(&message_or_enum.name_absolute()) {
+        // Well-known types are included in rust-protobuf library
+        // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
+        let file_descriptor = message_or_enum.file_descriptor();
+        static REGEX: Lazy<Regex> =
+            Lazy::new(|| Regex::new(r"^google/protobuf/([^/]+\.proto)$").unwrap());
+        let captures = REGEX
+            .captures(file_descriptor.name())
+            .unwrap_or_else(|| panic!("`{}` does not match the regex", file_descriptor.name()));
+        let file_name = captures.get(1).unwrap().as_str();
+        let mod_name = proto_path_to_rust_mod(file_name);
+        RustIdentWithPath::from(format!(
+            "{protobuf_crate}::well_known_types::{mod_name}::{name}",
+            protobuf_crate = protobuf_crate_path(&current.customize),
+        ))
+    } else if is_descriptor_proto(&message_or_enum.file_descriptor()) {
+        // Messages defined in descriptor.proto
+        RustIdentWithPath::from(format!(
+            "{}::descriptor::{}",
+            protobuf_crate_path(&current.customize),
+            message_or_enum.rust_name_to_file()
+        ))
+    } else {
+        current
+            .relative_mod
+            .to_reverse()
+            .into_path()
+            .append_component(RustPathComponent::SUPER)
+            .append_with_ident(message_or_enum.rust_name_with_file())
+    }
+}
+
+pub(crate) fn type_name_to_rust_relative(
+    type_name: &ProtobufAbsPath,
+    current: &FileAndMod,
+    root_scope: &RootScope,
+) -> RustIdentWithPath {
+    assert!(!type_name.is_root());
+    let message_or_enum = root_scope.find_message_or_enum(type_name);
+    message_or_enum_to_rust_relative(&message_or_enum, current)
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum PrimitiveTypeVariant {
+    Default,
+    TokioBytes,
+}
+
+pub enum _TokioBytesType {
+    Bytes,
+    Chars,
+}
+
+// ProtobufType trait name
+pub(crate) enum ProtobufTypeGen {
+    Primitive(field_descriptor_proto::Type, PrimitiveTypeVariant),
+    Message(RustTypeMessage),
+    EnumOrUnknown(RustIdentWithPath),
+}
+
+impl ProtobufTypeGen {
+    pub(crate) fn protobuf_value(&self, customize: &Customize) -> String {
+        match self {
+            ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => {
+                t.rust_type().to_code(customize)
+            }
+            ProtobufTypeGen::Primitive(_, PrimitiveTypeVariant::TokioBytes) => unimplemented!(),
+            ProtobufTypeGen::Message(m) => m.0.to_string(),
+            ProtobufTypeGen::EnumOrUnknown(e) => format!(
+                "{protobuf_crate}::EnumOrUnknown<{e}>",
+                protobuf_crate = protobuf_crate_path(customize)
+            ),
+        }
+    }
+
+    pub(crate) fn _rust_type(&self, customize: &Customize) -> String {
+        match self {
+            &ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!(
+                "{}::reflect::types::ProtobufType{}",
+                protobuf_crate_path(customize),
+                capitalize(t.protobuf_name())
+            ),
+            &ProtobufTypeGen::Primitive(
+                field_descriptor_proto::Type::TYPE_BYTES,
+                PrimitiveTypeVariant::TokioBytes,
+            ) => format!(
+                "{}::reflect::types::ProtobufTypeTokioBytes",
+                protobuf_crate_path(customize)
+            ),
+            &ProtobufTypeGen::Primitive(
+                field_descriptor_proto::Type::TYPE_STRING,
+                PrimitiveTypeVariant::TokioBytes,
+            ) => format!(
+                "{}::reflect::types::ProtobufTypeTokioChars",
+                protobuf_crate_path(customize)
+            ),
+            &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(),
+            &ProtobufTypeGen::Message(ref name) => format!(
+                "{}::reflect::types::ProtobufTypeMessage<{}>",
+                protobuf_crate_path(customize),
+                name
+            ),
+            &ProtobufTypeGen::EnumOrUnknown(ref name) => format!(
+                "{}::reflect::types::ProtobufTypeEnumOrUnknown<{}>",
+                protobuf_crate_path(customize),
+                name
+            ),
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn into_target_ref_box_to_ref() {
+        let t1 = RustType::Ref(Box::new(RustType::Uniq(Box::new(RustType::Message(
+            RustTypeMessage::from("Ab"),
+        )))));
+        let t2 = RustType::Ref(Box::new(RustType::Message(RustTypeMessage::from("Ab"))));
+
+        assert_eq!("&**v", t1.into_target(&t2, "v", &Customize::default()));
+    }
+}
diff --git a/src/gen/scope.rs b/src/gen/scope.rs
new file mode 100644
index 0000000..f70d233
--- /dev/null
+++ b/src/gen/scope.rs
@@ -0,0 +1,536 @@
+use std::ops::Deref;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::EnumValueDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+use protobuf_parse::ProtobufAbsPathRef;
+use protobuf_parse::ProtobufIdentRef;
+use protobuf_parse::ProtobufRelPath;
+use protobuf_parse::ProtobufRelPathRef;
+
+use crate::customize::Customize;
+use crate::gen::field::rust_field_name_for_protobuf_field_name;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::map::map_entry;
+use crate::gen::message::message_name_to_nested_mod_name;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::strx::capitalize;
+
+pub(crate) struct RootScope<'a> {
+    pub file_descriptors: &'a [FileDescriptor],
+}
+
+impl<'a> RootScope<'a> {
+    fn packages(&'a self) -> Vec<FileScope<'a>> {
+        self.file_descriptors
+            .iter()
+            .map(|fd| FileScope {
+                file_descriptor: fd,
+            })
+            .collect()
+    }
+
+    // find enum by fully qualified name
+    pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
+        match self.find_message_or_enum(fqn) {
+            MessageOrEnumWithScope::Enum(e) => e,
+            _ => panic!("not an enum: {}", fqn),
+        }
+    }
+
+    // find message by fully qualified name
+    pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
+        match self.find_message_or_enum(fqn) {
+            MessageOrEnumWithScope::Message(m) => m,
+            _ => panic!("not a message: {}", fqn),
+        }
+    }
+
+    // find message or enum by fully qualified name
+    pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
+        assert!(!fqn.is_root());
+        self.packages()
+            .into_iter()
+            .flat_map(|p| p.find_message_or_enum_abs(fqn))
+            .next()
+            .expect(&format!("enum not found by name: {}", fqn))
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FileScope<'a> {
+    pub file_descriptor: &'a FileDescriptor,
+}
+
+impl<'a> Deref for FileScope<'a> {
+    type Target = FileDescriptor;
+
+    fn deref(&self) -> &Self::Target {
+        self.file_descriptor
+    }
+}
+
+impl<'a> FileScope<'a> {
+    fn package(&self) -> ProtobufAbsPath {
+        ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
+    }
+
+    pub fn to_scope(&self) -> Scope<'a> {
+        Scope {
+            file_scope: self.clone(),
+            path: Vec::new(),
+        }
+    }
+
+    fn find_message_or_enum(
+        &self,
+        name: &ProtobufRelPathRef,
+    ) -> Option<MessageOrEnumWithScope<'a>> {
+        self.find_messages_and_enums()
+            .into_iter()
+            .filter(|e| e.protobuf_name_to_package().as_ref() == name)
+            .next()
+    }
+
+    fn find_message_or_enum_abs(
+        &self,
+        name: &ProtobufAbsPathRef,
+    ) -> Option<MessageOrEnumWithScope<'a>> {
+        let name = name.to_owned();
+        match name.remove_prefix(&self.package()) {
+            Some(rem) => self.find_message_or_enum(&rem),
+            None => None,
+        }
+    }
+
+    // find all enums in given file descriptor
+    pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
+        let mut r = Vec::new();
+
+        self.to_scope().walk_scopes(|scope| {
+            r.extend(scope.enums());
+        });
+
+        r
+    }
+
+    /// Find all messages in given file descriptor
+    pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
+        let mut r = Vec::new();
+
+        self.to_scope().walk_scopes(|scope| {
+            r.extend(scope.messages());
+        });
+
+        r
+    }
+
+    /// Find all messages in given file descriptor, except map messages
+    pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
+        self.find_messages()
+            .into_iter()
+            .filter(|m| !m.is_map())
+            .collect()
+    }
+
+    /// find all messages and enums in given file descriptor
+    pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
+        let mut r = Vec::new();
+
+        self.to_scope().walk_scopes(|scope| {
+            r.extend(scope.messages_and_enums());
+        });
+
+        r
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct Scope<'a> {
+    pub file_scope: FileScope<'a>,
+    pub path: Vec<MessageDescriptor>,
+}
+
+impl<'a> Scope<'a> {
+    pub(crate) fn file_descriptor(&self) -> FileDescriptor {
+        self.file_scope.file_descriptor.clone()
+    }
+
+    // get message descriptors in this scope
+    fn message_descriptors(&self) -> Vec<MessageDescriptor> {
+        if self.path.is_empty() {
+            self.file_scope.file_descriptor.messages().collect()
+        } else {
+            self.path.last().unwrap().nested_messages().collect()
+        }
+    }
+
+    // get enum descriptors in this scope
+    fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
+        if self.path.is_empty() {
+            self.file_scope.file_descriptor.enums().collect()
+        } else {
+            self.path.last().unwrap().nested_enums().collect()
+        }
+    }
+
+    // get messages with attached scopes in this scope
+    pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
+        self.message_descriptors()
+            .into_iter()
+            .map(|message| MessageWithScope {
+                scope: self.clone(),
+                message,
+            })
+            .collect()
+    }
+
+    // get enums with attached scopes in this scope
+    pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
+        self.enum_descriptors()
+            .into_iter()
+            .map(|en| EnumWithScope {
+                scope: self.clone(),
+                en,
+            })
+            .collect()
+    }
+
+    // get messages and enums with attached scopes in this scope
+    pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
+        self.messages()
+            .into_iter()
+            .map(|m| MessageOrEnumWithScope::Message(m))
+            .chain(
+                self.enums()
+                    .into_iter()
+                    .map(|m| MessageOrEnumWithScope::Enum(m)),
+            )
+            .collect()
+    }
+
+    // nested scopes, i. e. scopes of nested messages
+    fn nested_scopes(&self) -> Vec<Scope<'a>> {
+        self.message_descriptors()
+            .into_iter()
+            .map(|m| {
+                let mut nested = self.clone();
+                nested.path.push(m);
+                nested
+            })
+            .collect()
+    }
+
+    fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
+        (*callback)(self);
+
+        for nested in self.nested_scopes() {
+            nested.walk_scopes_impl(callback);
+        }
+    }
+
+    // apply callback for this scope and all nested scopes
+    fn walk_scopes<F>(&self, mut callback: F)
+    where
+        F: FnMut(&Scope<'a>),
+    {
+        self.walk_scopes_impl(&mut callback);
+    }
+
+    pub fn rust_path_to_file(&self) -> RustRelativePath {
+        RustRelativePath::from_idents(
+            self.path
+                .iter()
+                .map(|m| message_name_to_nested_mod_name(m.name())),
+        )
+    }
+
+    pub fn path_str(&self) -> String {
+        let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
+        v.join(".")
+    }
+
+    pub fn prefix(&self) -> String {
+        let path_str = self.path_str();
+        if path_str.is_empty() {
+            path_str
+        } else {
+            format!("{}.", path_str)
+        }
+    }
+
+    pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
+        ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
+    }
+
+    pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
+        let mut r = self.file_scope.package();
+        r.push_relative(&self.protobuf_path_to_file());
+        r
+    }
+
+    pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
+        FileAndMod {
+            file: self.file_scope.file_descriptor.proto().name().to_owned(),
+            relative_mod: self.rust_path_to_file(),
+            customize,
+        }
+    }
+}
+
+pub(crate) trait WithScope<'a> {
+    fn scope(&self) -> &Scope<'a>;
+
+    fn file_descriptor(&self) -> FileDescriptor {
+        self.scope().file_descriptor()
+    }
+
+    // message or enum name
+    fn name(&self) -> &ProtobufIdentRef;
+
+    fn name_to_package(&self) -> String {
+        let mut r = self.scope().prefix();
+        r.push_str(&self.name());
+        r
+    }
+
+    fn protobuf_name_to_package(&self) -> ProtobufRelPath {
+        let r = self.scope().protobuf_path_to_file();
+        r.append_ident(ProtobufIdentRef::new(self.name()))
+    }
+
+    /// Return absolute name starting with dot
+    fn name_absolute(&self) -> ProtobufAbsPath {
+        let mut path = self.scope().protobuf_absolute_path();
+        path.push_simple(self.name());
+        path
+    }
+
+    // rust type name of this descriptor
+    fn rust_name(&self) -> RustIdent {
+        let rust_name = capitalize(&self.name());
+        RustIdent::new(&rust_name)
+    }
+
+    fn rust_name_to_file(&self) -> RustIdentWithPath {
+        self.scope()
+            .rust_path_to_file()
+            .into_path()
+            .with_ident(self.rust_name())
+    }
+
+    // fully-qualified name of this type
+    fn rust_name_with_file(&self) -> RustIdentWithPath {
+        let mut r = self.rust_name_to_file();
+        r.prepend_ident(proto_path_to_rust_mod(
+            self.scope().file_descriptor().name(),
+        ));
+        r
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct MessageWithScope<'a> {
+    pub scope: Scope<'a>,
+    pub message: MessageDescriptor,
+}
+
+impl<'a> WithScope<'a> for MessageWithScope<'a> {
+    fn scope(&self) -> &Scope<'a> {
+        &self.scope
+    }
+
+    fn name(&self) -> &ProtobufIdentRef {
+        ProtobufIdentRef::new(self.message.name())
+    }
+}
+
+impl<'a> MessageWithScope<'a> {
+    pub fn into_scope(mut self) -> Scope<'a> {
+        self.scope.path.push(self.message);
+        self.scope
+    }
+
+    pub fn to_scope(&self) -> Scope<'a> {
+        self.clone().into_scope()
+    }
+
+    pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
+        self.message
+            .fields()
+            .into_iter()
+            .map(|field| FieldWithContext {
+                field,
+                message: self.clone(),
+            })
+            .collect()
+    }
+
+    pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
+        self.message
+            .oneofs()
+            .into_iter()
+            .map(|oneof| OneofWithContext {
+                message: self.clone(),
+                oneof,
+            })
+            .collect()
+    }
+
+    pub fn mod_name(&self) -> RustIdent {
+        message_name_to_nested_mod_name(self.message.name())
+    }
+
+    /// This message is a special message which is a map.
+    pub fn is_map(&self) -> bool {
+        map_entry(self).is_some()
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct EnumWithScope<'a> {
+    pub scope: Scope<'a>,
+    pub en: EnumDescriptor,
+}
+
+impl<'a> EnumWithScope<'a> {
+    pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
+        self.en
+            .values()
+            .into_iter()
+            .map(|v| EnumValueWithContext {
+                en: self.clone(),
+                proto: v,
+            })
+            .collect()
+    }
+
+    // find enum value by protobuf name
+    pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
+        self.values()
+            .into_iter()
+            .find(|v| v.proto.proto().name() == name)
+            .unwrap()
+    }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct EnumValueWithContext<'a> {
+    pub en: EnumWithScope<'a>,
+    pub proto: EnumValueDescriptor,
+}
+
+impl<'a> EnumValueWithContext<'a> {
+    pub fn rust_name(&self) -> RustIdent {
+        // TODO: camel case or something.
+        RustIdent::new(self.proto.name())
+    }
+}
+
+impl<'a> WithScope<'a> for EnumWithScope<'a> {
+    fn scope(&self) -> &Scope<'a> {
+        &self.scope
+    }
+
+    fn name(&self) -> &ProtobufIdentRef {
+        ProtobufIdentRef::new(self.en.name())
+    }
+}
+
+pub(crate) enum MessageOrEnumWithScope<'a> {
+    Message(MessageWithScope<'a>),
+    Enum(EnumWithScope<'a>),
+}
+
+impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
+    fn scope(&self) -> &Scope<'a> {
+        match self {
+            MessageOrEnumWithScope::Message(m) => m.scope(),
+            MessageOrEnumWithScope::Enum(e) => e.scope(),
+        }
+    }
+
+    fn name(&self) -> &ProtobufIdentRef {
+        match self {
+            MessageOrEnumWithScope::Message(m) => m.name(),
+            MessageOrEnumWithScope::Enum(e) => e.name(),
+        }
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct FieldWithContext<'a> {
+    pub field: FieldDescriptor,
+    pub message: MessageWithScope<'a>,
+}
+
+impl<'a> Deref for FieldWithContext<'a> {
+    type Target = FieldDescriptor;
+
+    fn deref(&self) -> &Self::Target {
+        &self.field
+    }
+}
+
+impl<'a> FieldWithContext<'a> {
+    pub fn is_oneof(&self) -> bool {
+        self.field.containing_oneof().is_some()
+    }
+
+    pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
+        match self.field.containing_oneof() {
+            Some(oneof) => Some(OneofWithContext {
+                message: self.message.clone(),
+                oneof,
+            }),
+            None => None,
+        }
+    }
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofVariantWithContext<'a> {
+    pub oneof: &'a OneofWithContext<'a>,
+    pub field: FieldDescriptor,
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofWithContext<'a> {
+    pub oneof: OneofDescriptor,
+    pub message: MessageWithScope<'a>,
+}
+
+impl<'a> OneofWithContext<'a> {
+    pub fn field_name(&'a self) -> RustIdent {
+        return rust_field_name_for_protobuf_field_name(self.oneof.name());
+    }
+
+    // rust type name of enum
+    pub fn rust_name(&self) -> RustIdentWithPath {
+        let type_name = RustIdent::from(capitalize(self.oneof.name()));
+        self.message
+            .to_scope()
+            .rust_path_to_file()
+            .into_path()
+            .with_ident(type_name)
+    }
+
+    pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
+        self.message
+            .fields()
+            .into_iter()
+            .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
+            .map(|f| OneofVariantWithContext {
+                oneof: self,
+                field: f.field,
+            })
+            .collect()
+    }
+}
diff --git a/src/strx.rs b/src/gen/strx.rs
similarity index 100%
rename from src/strx.rs
rename to src/gen/strx.rs
diff --git a/src/gen/well_known_types.rs b/src/gen/well_known_types.rs
new file mode 100644
index 0000000..6b0b389
--- /dev/null
+++ b/src/gen/well_known_types.rs
@@ -0,0 +1,123 @@
+use protobuf_parse::ProtobufAbsPath;
+use protobuf_parse::ProtobufRelPath;
+use protobuf_parse::ProtobufRelPathRef;
+
+use crate::compiler_plugin;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::paths::proto_path_to_rust_mod;
+
+pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_NAMES: &[&str] = &[
+    "any.proto",
+    "api.proto",
+    "duration.proto",
+    "empty.proto",
+    "field_mask.proto",
+    "source_context.proto",
+    "struct.proto",
+    "timestamp.proto",
+    "type.proto",
+    "wrappers.proto",
+];
+
+pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES: &[&str] = &[
+    "google/protobuf/any.proto",
+    "google/protobuf/api.proto",
+    "google/protobuf/duration.proto",
+    "google/protobuf/empty.proto",
+    "google/protobuf/field_mask.proto",
+    "google/protobuf/source_context.proto",
+    "google/protobuf/struct.proto",
+    "google/protobuf/timestamp.proto",
+    "google/protobuf/type.proto",
+    "google/protobuf/wrappers.proto",
+];
+
+static NAMES: &'static [&'static str] = &[
+    "Any",
+    "Api",
+    "BoolValue",
+    "BytesValue",
+    "DoubleValue",
+    "Duration",
+    "Empty",
+    "Enum",
+    "EnumValue",
+    "Field",
+    "Field.Cardinality",
+    "Field.Kind",
+    "FieldMask",
+    "FloatValue",
+    "Int32Value",
+    "Int64Value",
+    "ListValue",
+    "Method",
+    "Mixin",
+    "NullValue",
+    "Option",
+    "SourceContext",
+    "StringValue",
+    "Struct",
+    "Syntax",
+    "Timestamp",
+    "Type",
+    "UInt32Value",
+    "UInt64Value",
+    "Value",
+];
+
+fn is_well_known_type(name: &ProtobufRelPathRef) -> bool {
+    NAMES.iter().any(|&n| n == format!("{}", name))
+}
+
+pub(crate) fn is_well_known_type_full(name: &ProtobufAbsPath) -> Option<ProtobufRelPath> {
+    if let Some(rem) = name.remove_prefix(&ProtobufAbsPath::from(".google.protobuf")) {
+        if is_well_known_type(rem) {
+            Some(rem.to_owned())
+        } else {
+            None
+        }
+    } else {
+        None
+    }
+}
+
+pub(crate) fn gen_well_known_types_mod() -> compiler_plugin::GenResult {
+    let v = CodeWriter::with_no_error(|w| {
+        w.comment("This file is generated. Do not edit");
+        w.comment("@generated");
+        w.mod_doc("Generated code for \"well known types\"");
+        w.mod_doc("");
+        w.mod_doc("[This document](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) describes these types.");
+
+        w.write_line("");
+        w.write_line("#![allow(unused_attributes)]");
+        w.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
+
+        w.write_line("");
+        for m in WELL_KNOWN_TYPES_PROTO_FILE_NAMES {
+            w.write_line(&format!("pub mod {};", proto_path_to_rust_mod(m)));
+        }
+    });
+
+    compiler_plugin::GenResult {
+        name: "well_known_types_mod.rs".to_string(),
+        content: v.into_bytes(),
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_is_well_known_type_full() {
+        assert_eq!(
+            Some(ProtobufRelPath::from("BoolValue")),
+            is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.BoolValue"))
+        );
+        assert_eq!(
+            None,
+            is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.Fgfg"))
+        );
+    }
+}
diff --git a/src/gen_and_write.rs b/src/gen_and_write.rs
new file mode 100644
index 0000000..95d621e
--- /dev/null
+++ b/src/gen_and_write.rs
@@ -0,0 +1,62 @@
+#![doc(hidden)]
+
+use std::fs;
+use std::io;
+use std::path::Path;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf_parse::ProtoPathBuf;
+
+use crate::customize::CustomizeCallback;
+use crate::gen::all::gen_all;
+use crate::Customize;
+
+#[derive(Debug, thiserror::Error)]
+enum Error {
+    #[error("output path `{0}` is not a directory")]
+    OutputIsNotDirectory(String),
+    #[error("output path `{0}` does not exist or not accessible")]
+    OutputDoesNotExistOrNotAccssible(String, #[source] io::Error),
+    #[error("failed to create file `{0}`: {1}")]
+    FailedToWriteFile(String, #[source] io::Error),
+}
+
+#[doc(hidden)]
+pub fn gen_and_write(
+    file_descriptors: &[FileDescriptorProto],
+    parser: &str,
+    files_to_generate: &[ProtoPathBuf],
+    out_dir: &Path,
+    customize: &Customize,
+    customize_callback: &dyn CustomizeCallback,
+) -> anyhow::Result<()> {
+    match out_dir.metadata() {
+        Ok(m) => {
+            if !m.is_dir() {
+                return Err(Error::OutputIsNotDirectory(out_dir.display().to_string()).into());
+            }
+        }
+        Err(e) => {
+            return Err(
+                Error::OutputDoesNotExistOrNotAccssible(out_dir.display().to_string(), e).into(),
+            );
+        }
+    }
+
+    let results = gen_all(
+        file_descriptors,
+        parser,
+        files_to_generate,
+        customize,
+        customize_callback,
+    )?;
+
+    for r in &results {
+        let mut file_path = out_dir.to_owned();
+        file_path.push(&r.name);
+        fs::write(&file_path, r.content.as_slice())
+            .map_err(|e| Error::FailedToWriteFile(file_path.display().to_string(), e))?;
+    }
+
+    Ok(())
+}
diff --git a/src/inside.rs b/src/inside.rs
deleted file mode 100644
index bbdcfdb..0000000
--- a/src/inside.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use Customize;
-
-/// Path to `protobuf` crate, different when `.proto` file is
-/// used inside or outside of protobuf crate.
-pub(crate) fn protobuf_crate_path(customize: &Customize) -> &str {
-    match customize.inside_protobuf {
-        Some(true) => "crate",
-        _ => "::protobuf",
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 826e18b..0471d7c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,35 +1,68 @@
-//! # Protobuf code generator
+//! # Protobuf code generator for `protobuf` crate
 //!
-//! This crate contains protobuf code generator implementation
-//! and a `protoc-gen-rust` `protoc` plugin.
+//! This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build.
 //!
-//! This crate:
-//! * provides `protoc-gen-rust` plugin for `protoc` command
-//! * implement protobuf codegen
+//! # How to generate code
 //!
-//! This crate is not meant to be used directly, in fact, it does not provide any public API
-//! (except for `protoc-gen-rust` binary).
+//! There are three main ways to generate `.rs` files from `.proto` files:
+//! * using `protoc` command line tool and `protoc-gen-rust` plugin
+//! * using this crate `Codegen` with pure rust parser
+//! * using this crate `Codegen` with `protoc` parser
 //!
-//! Code can be generated with either:
-//! * `protoc-gen-rust` plugin for `protoc` or
-//! * [`protoc-rust`](https://docs.rs/protoc) crate
-//!   (code generator which depends on `protoc` binary for parsing of `.proto` files)
-//! * [`protobuf-codegen-pure`](https://docs.rs/protobuf-codegen-pure) crate,
-//!   similar API to `protoc-rust`, but uses pure rust parser of `.proto` files.
+//! Which one should you use depends on your needs.
 //!
-//! # `protoc-gen-rust` plugin for `protoc`
+//! If you are using non-cargo build system (like Bazel), you might prefer
+//! using `protoc-gen-rust` plugin for `protoc`.
 //!
-//! When non-cargo build system is used, consider using standard protobuf code generation pattern:
-//! `protoc` command does all the work of handling paths and parsing `.proto` files.
-//! When `protoc` is invoked with `--rust_out=` option, it invokes `protoc-gen-rust` plugin.
-//! provided by this crate.
+//! If you build with `cargo`, you probably want to use `Codegen` from this crate.
 //!
-//! When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+//! # Protoc parser vs pure rust parser
 //!
-//! ## How to use `protoc-gen-rust` if you have to
+//! There are two protobuf parsers which can be plugged into this crate:
+//! * `protoc`-based parser (`protoc` is a command like utility from Google protobuf)
+//! * pure rust parser (`protobuf-parse` crate)
+//!
+//! `protoc`-based parser is expected to parse `.proto` files very correctly:
+//! all Google's protobuf implementations rely on it.
+//!
+//! While there are no known bugs in `protobuf-parse`, it is not tested very well.
+//! Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser,
+//! mostly complex message options specified in `.proto` files.
+//! I never saw anyone using them, but you have been warned.
+//!
+//! Note `protoc` command can be obtained from
+//! [`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate.
+//!
+//! # Example
+//!
+//! ```no_run
+//! # mod protoc_bin_vendored {
+//! #   pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
+//! #       unimplemented!()
+//! #   }
+//! # }
+//! // Use this in build.rs
+//! protobuf_codegen::Codegen::new()
+//!     // Use `protoc` parser, optional.
+//!     .protoc()
+//!     // Use `protoc-bin-vendored` bundled protoc command, optional.
+//!     .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+//!     // All inputs and imports from the inputs must reside in `includes` directories.
+//!     .includes(&["src/protos"])
+//!     // Inputs must reside in some of include paths.
+//!     .input("src/protos/apple.proto")
+//!     .input("src/protos/banana.proto")
+//!     // Specify output directory relative to Cargo output directory.
+//!     .cargo_out_dir("protos")
+//!     .run_from_script();
+//! ```
+//!
+//! ## How to use `protoc-gen-rust`
+//!
+//! If you have to.
 //!
 //! (Note `protoc` can be invoked programmatically with
-//! [protoc crate](https://docs.rs/protoc))
+//! [protoc crate](https://docs.rs/protoc/%3E=3.0.0-alpha))
 //!
 //! 0) Install protobuf for `protoc` binary.
 //!
@@ -46,11 +79,11 @@
 //! ```
 //!
 //! Protobuf is needed only for code generation, `rust-protobuf` runtime
-//! does not use `protobuf` library.
+//! does not use C++ protobuf library.
 //!
 //! 1) Install `protoc-gen-rust` program (which is `protoc` plugin)
 //!
-//! It can be installed either from source or with `cargo install protobuf` command.
+//! It can be installed either from source or with `cargo install protobuf-codegen` command.
 //!
 //! 2) Add `protoc-gen-rust` to $PATH
 //!
@@ -68,320 +101,45 @@
 //!
 //! This will generate .rs files in current directory.
 //!
-//! # Version 2
+//! # Customize generate code
 //!
-//! This is documentation for version 2 of the crate.
+//! Sometimes generated code need to be adjusted, e. g. to have custom derives.
 //!
-//! [Version 3 of the crate](https://docs.rs/protobuf-codegen/%3E=3.0.0-alpha)
-//! (currently in development) encapsulates both `protoc` and pure codegens in this crate.
+//! rust-protobuf provides two options to do that:
+//! * generated `.rs` files contain `@@protoc_insertion_point(...)` markers
+//!   (similar markers inserts Google's protobuf generator for C++ or Java).
+//!   Simple script `sed` one-liners can be used to replace these markers with custom annotations.
+//! * `Codegen::customize_callback` can be used to patch generated code
+//!   when invoked from `build.rs` script.
+//!
+//! # Serde
+//!
+//! rust-protobuf since version 3 no longer directly supports serde.
+//!
+//! Rust-protobuf 3 fully supports:
+//! * runtime reflection
+//! * JSON parsing and printing via
+//!  [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping)
+//!
+//! Which covers the most of serde use cases.
+//!
+//! If you still need serde, generic customization callback (see above) can be used
+//! to insert `#[serde(...)]` annotations.
+//!
+//! [Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)
+//! in the rust-protobuf repository demonstrates how to do it.
 
 #![deny(rustdoc::broken_intra_doc_links)]
-#![deny(missing_docs)]
 
-extern crate protobuf;
-
-use std::collections::hash_map::HashMap;
-use std::fmt::Write as FmtWrite;
-use std::fs::File;
-use std::io;
-use std::io::Write;
-use std::path::Path;
-
-use protobuf::compiler_plugin;
-use protobuf::descriptor::*;
-use protobuf::Message;
-
+mod codegen;
+mod compiler_plugin;
 mod customize;
-mod enums;
-mod extensions;
-mod field;
-mod file;
-mod file_and_mod;
-mod file_descriptor;
-#[doc(hidden)]
-pub mod float;
-mod inside;
-mod message;
-mod oneof;
-mod protobuf_name;
-mod rust_name;
-mod rust_types_values;
-mod serde;
-mod well_known_types;
+mod gen;
+pub mod gen_and_write;
+pub mod protoc_gen_rust;
 
-pub(crate) mod rust;
-pub(crate) mod scope;
-pub(crate) mod strx;
-pub(crate) mod syntax;
-
-use customize::customize_from_rustproto_for_file;
-#[doc(hidden)]
+pub use codegen::Codegen;
 pub use customize::Customize;
-
-pub mod code_writer;
-
-use inside::protobuf_crate_path;
+pub use customize::CustomizeCallback;
 #[doc(hidden)]
-pub use protobuf_name::ProtobufAbsolutePath;
-#[doc(hidden)]
-pub use protobuf_name::ProtobufIdent;
-#[doc(hidden)]
-pub use protobuf_name::ProtobufRelativePath;
-use scope::FileScope;
-use scope::RootScope;
-
-use self::code_writer::CodeWriter;
-use self::enums::*;
-use self::extensions::*;
-use self::message::*;
-use crate::file::proto_path_to_rust_mod;
-
-fn escape_byte(s: &mut String, b: u8) {
-    if b == b'\n' {
-        write!(s, "\\n").unwrap();
-    } else if b == b'\r' {
-        write!(s, "\\r").unwrap();
-    } else if b == b'\t' {
-        write!(s, "\\t").unwrap();
-    } else if b == b'\\' || b == b'"' {
-        write!(s, "\\{}", b as char).unwrap();
-    } else if b == b'\0' {
-        write!(s, "\\0").unwrap();
-    // ASCII printable except space
-    } else if b > 0x20 && b < 0x7f {
-        write!(s, "{}", b as char).unwrap();
-    } else {
-        write!(s, "\\x{:02x}", b).unwrap();
-    }
-}
-
-fn write_file_descriptor_data(
-    file: &FileDescriptorProto,
-    customize: &Customize,
-    w: &mut CodeWriter,
-) {
-    let fdp_bytes = file.write_to_bytes().unwrap();
-    w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
-    w.indented(|w| {
-        const MAX_LINE_LEN: usize = 72;
-
-        let mut s = String::new();
-        for &b in &fdp_bytes {
-            let prev_len = s.len();
-            escape_byte(&mut s, b);
-            let truncate = s.len() > MAX_LINE_LEN;
-            if truncate {
-                s.truncate(prev_len);
-            }
-            if truncate || s.len() == MAX_LINE_LEN {
-                write!(s, "\\").unwrap();
-                w.write_line(&s);
-                s.clear();
-            }
-            if truncate {
-                escape_byte(&mut s, b);
-            }
-        }
-        if !s.is_empty() {
-            write!(s, "\\").unwrap();
-            w.write_line(&s);
-            s.clear();
-        }
-    });
-    w.write_line("\";");
-    w.write_line("");
-    w.lazy_static(
-        "file_descriptor_proto_lazy",
-        &format!(
-            "{}::descriptor::FileDescriptorProto",
-            protobuf_crate_path(customize)
-        ),
-        customize,
-    );
-    w.write_line("");
-    w.def_fn(
-        &format!(
-            "parse_descriptor_proto() -> {}::descriptor::FileDescriptorProto",
-            protobuf_crate_path(customize)
-        ),
-        |w| {
-            w.write_line(&format!(
-                "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
-                protobuf_crate_path(customize)
-            ));
-        },
-    );
-    w.write_line("");
-    w.pub_fn(
-        &format!(
-            "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
-            protobuf_crate_path(customize)
-        ),
-        |w| {
-            w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
-                w.write_line("parse_descriptor_proto()");
-            });
-        },
-    );
-}
-
-struct GenFileResult {
-    compiler_plugin_result: compiler_plugin::GenResult,
-    mod_name: String,
-}
-
-fn gen_file(
-    file: &FileDescriptorProto,
-    _files_map: &HashMap<&str, &FileDescriptorProto>,
-    root_scope: &RootScope,
-    customize: &Customize,
-) -> GenFileResult {
-    // TODO: use it
-    let mut customize = customize.clone();
-    // options specified in invocation have precedence over options specified in file
-    customize.update_with(&customize_from_rustproto_for_file(file.get_options()));
-
-    let scope = FileScope {
-        file_descriptor: file,
-    }
-    .to_scope();
-    let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
-        file.get_options().get_optimize_for() == FileOptions_OptimizeMode::LITE_RUNTIME
-    });
-
-    let mut v = Vec::new();
-
-    {
-        let mut w = CodeWriter::new(&mut v);
-
-        w.write_generated_by("rust-protobuf", "2.27.1");
-        w.write_line(&format!("//! Generated file from `{}`", file.get_name()));
-        if customize.inside_protobuf != Some(true) {
-            w.write_line("");
-            w.write_line("/// Generated files are compatible only with the same version");
-            w.write_line("/// of protobuf runtime.");
-            w.commented(|w| {
-                w.write_line(&format!(
-                    "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
-                    protobuf_crate_path(&customize),
-                    protobuf::VERSION_IDENT
-                ));
-            })
-        }
-
-        for message in &scope.get_messages() {
-            // ignore map entries, because they are not used in map fields
-            if message.map_entry().is_none() {
-                w.write_line("");
-                MessageGen::new(message, &root_scope, &customize).write(&mut w);
-            }
-        }
-        for enum_type in &scope.get_enums() {
-            w.write_line("");
-            EnumGen::new(enum_type, file, &customize, root_scope).write(&mut w);
-        }
-
-        write_extensions(file, &root_scope, &mut w, &customize);
-
-        if !lite_runtime {
-            w.write_line("");
-            write_file_descriptor_data(file, &customize, &mut w);
-        }
-    }
-
-    GenFileResult {
-        compiler_plugin_result: compiler_plugin::GenResult {
-            name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
-            content: v,
-        },
-        mod_name: proto_path_to_rust_mod(file.get_name()).into_string(),
-    }
-}
-
-fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
-    let mut v = Vec::new();
-    let mut w = CodeWriter::new(&mut v);
-    w.comment("@generated");
-    w.write_line("");
-    for m in mods {
-        w.write_line(&format!("pub mod {};", m));
-    }
-    drop(w);
-    compiler_plugin::GenResult {
-        name: "mod.rs".to_owned(),
-        content: v,
-    }
-}
-
-// This function is also used externally by cargo plugin
-// https://github.com/plietar/rust-protobuf-build
-// So be careful changing its signature.
-#[doc(hidden)]
-pub fn gen(
-    file_descriptors: &[FileDescriptorProto],
-    files_to_generate: &[String],
-    customize: &Customize,
-) -> Vec<compiler_plugin::GenResult> {
-    let root_scope = RootScope {
-        file_descriptors: file_descriptors,
-    };
-
-    let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
-    let files_map: HashMap<&str, &FileDescriptorProto> =
-        file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
-
-    let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
-
-    let mut mods = Vec::new();
-
-    for file_name in files_to_generate {
-        let file = files_map.get(&file_name[..]).expect(&format!(
-            "file not found in file descriptors: {:?}, files: {:?}",
-            file_name, all_file_names
-        ));
-
-        let gen_file_result = gen_file(file, &files_map, &root_scope, customize);
-        results.push(gen_file_result.compiler_plugin_result);
-        mods.push(gen_file_result.mod_name);
-    }
-
-    if customize.gen_mod_rs.unwrap_or(false) {
-        results.push(gen_mod_rs(&mods));
-    }
-
-    results
-}
-
-#[doc(hidden)]
-pub fn gen_and_write(
-    file_descriptors: &[FileDescriptorProto],
-    files_to_generate: &[String],
-    out_dir: &Path,
-    customize: &Customize,
-) -> io::Result<()> {
-    let results = gen(file_descriptors, files_to_generate, customize);
-
-    for r in &results {
-        let mut file_path = out_dir.to_owned();
-        file_path.push(&r.name);
-        let mut file_writer = File::create(&file_path)?;
-        file_writer.write_all(&r.content)?;
-        file_writer.flush()?;
-    }
-
-    Ok(())
-}
-
-#[doc(hidden)]
-pub fn protoc_gen_rust_main() {
-    compiler_plugin::plugin_main_2(|r| {
-        let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
-        gen(r.file_descriptors, r.files_to_generate, &customize)
-    });
-}
-
-/// Used in protobuf-codegen-identical-test
-#[doc(hidden)]
-pub fn proto_name_to_rs(name: &str) -> String {
-    format!("{}.rs", proto_path_to_rust_mod(name))
-}
+pub use gen::paths::proto_name_to_rs;
diff --git a/src/message.rs b/src/message.rs
deleted file mode 100644
index fbe9ed4..0000000
--- a/src/message.rs
+++ /dev/null
@@ -1,626 +0,0 @@
-use std::fmt;
-
-use file_descriptor::file_descriptor_proto_expr;
-use inside::protobuf_crate_path;
-use oneof::OneofGen;
-use oneof::OneofVariantGen;
-use protobuf::descriptor::*;
-use rust_name::RustIdentWithPath;
-use scope::MessageWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-
-use super::code_writer::*;
-use super::customize::customize_from_rustproto_for_message;
-use super::customize::Customize;
-use super::enums::*;
-use super::field::*;
-use super::rust_types_values::*;
-
-/// Protobuf message Rust type name
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct RustTypeMessage(pub RustIdentWithPath);
-
-impl fmt::Display for RustTypeMessage {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.0, f)
-    }
-}
-
-impl RustTypeMessage {
-    /// Code which emits default instance.
-    pub fn default_instance(&self, customize: &Customize) -> String {
-        format!(
-            "<{} as {}::Message>::default_instance()",
-            self.0,
-            protobuf_crate_path(customize)
-        )
-    }
-}
-
-/// Message info for codegen
-pub(crate) struct MessageGen<'a> {
-    pub message: &'a MessageWithScope<'a>,
-    pub root_scope: &'a RootScope<'a>,
-    type_name: RustIdentWithPath,
-    pub fields: Vec<FieldGen<'a>>,
-    pub lite_runtime: bool,
-    customize: Customize,
-}
-
-impl<'a> MessageGen<'a> {
-    pub fn new(
-        message: &'a MessageWithScope<'a>,
-        root_scope: &'a RootScope<'a>,
-        customize: &Customize,
-    ) -> MessageGen<'a> {
-        let mut customize = customize.clone();
-        customize.update_with(&customize_from_rustproto_for_message(
-            message.message.get_options(),
-        ));
-
-        let fields: Vec<_> = message
-            .fields()
-            .into_iter()
-            .map(|field| FieldGen::parse(field, root_scope, &customize))
-            .collect();
-        let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
-            message
-                .get_file_descriptor()
-                .get_options()
-                .get_optimize_for()
-                == FileOptions_OptimizeMode::LITE_RUNTIME
-        });
-        MessageGen {
-            message,
-            root_scope,
-            type_name: message.rust_name().into(),
-            fields,
-            lite_runtime,
-            customize,
-        }
-    }
-
-    fn expose_oneof(&self) -> bool {
-        self.customize.expose_oneof.unwrap_or(true)
-    }
-
-    fn oneofs(&'a self) -> Vec<OneofGen<'a>> {
-        self.message
-            .oneofs()
-            .into_iter()
-            .map(|oneof| OneofGen::parse(self, oneof, &self.customize))
-            .collect()
-    }
-
-    fn required_fields(&'a self) -> Vec<&'a FieldGen> {
-        self.fields
-            .iter()
-            .filter(|f| match f.kind {
-                FieldKind::Singular(ref singular) => singular.flag.is_required(),
-                _ => false,
-            })
-            .collect()
-    }
-
-    fn message_fields(&'a self) -> Vec<&'a FieldGen> {
-        self.fields
-            .iter()
-            .filter(|f| f.proto_type == FieldDescriptorProto_Type::TYPE_MESSAGE)
-            .collect()
-    }
-
-    fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen> {
-        self.fields.iter().filter(|f| !f.is_oneof()).collect()
-    }
-
-    fn fields_except_group(&'a self) -> Vec<&'a FieldGen> {
-        self.fields
-            .iter()
-            .filter(|f| f.proto_type != FieldDescriptorProto_Type::TYPE_GROUP)
-            .collect()
-    }
-
-    fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen> {
-        self.fields
-            .iter()
-            .filter(|f| !f.is_oneof() && f.proto_type != FieldDescriptorProto_Type::TYPE_GROUP)
-            .collect()
-    }
-
-    fn write_match_each_oneof_variant<F>(&self, w: &mut CodeWriter, cb: F)
-    where
-        F: Fn(&mut CodeWriter, &OneofVariantGen, &str, &RustType),
-    {
-        for oneof in self.oneofs() {
-            w.if_let_stmt(
-                "::std::option::Option::Some(ref v)",
-                &format!("self.{}", oneof.oneof.field_name())[..],
-                |w| {
-                    w.match_block("v", |w| {
-                        for variant in oneof.variants_except_group() {
-                            let ref field = variant.field;
-                            let (refv, vtype) = if !field.elem_type_is_copy() {
-                                ("ref v", field.elem().rust_storage_type().ref_type())
-                            } else {
-                                ("v", field.elem().rust_storage_type())
-                            };
-                            w.case_block(format!("&{}({})", variant.path(), refv), |w| {
-                                cb(w, &variant, "v", &vtype);
-                            });
-                        }
-                    });
-                },
-            );
-        }
-    }
-
-    fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) {
-        let sig = format!(
-            "write_to_with_cached_sizes(&self, os: &mut {}::CodedOutputStream<'_>) -> {}::ProtobufResult<()>",
-            protobuf_crate_path(&self.customize),
-            protobuf_crate_path(&self.customize),
-        );
-        w.def_fn(&sig, |w| {
-            // To have access to its methods but not polute the name space.
-            for f in self.fields_except_oneof_and_group() {
-                f.write_message_write_field(w);
-            }
-            self.write_match_each_oneof_variant(w, |w, variant, v, v_type| {
-                variant.field.write_write_element(w, "os", v, v_type);
-            });
-            w.write_line("os.write_unknown_fields(self.get_unknown_fields())?;");
-            w.write_line("::std::result::Result::Ok(())");
-        });
-    }
-
-    fn write_get_cached_size(&self, w: &mut CodeWriter) {
-        w.def_fn("get_cached_size(&self) -> u32", |w| {
-            w.write_line("self.cached_size.get()");
-        });
-    }
-
-    fn write_default_instance(&self, w: &mut CodeWriter) {
-        w.def_fn(
-            &format!("default_instance() -> &'static {}", self.type_name),
-            |w| {
-                w.lazy_static_decl_get_simple(
-                    "instance",
-                    &self.type_name.to_string(),
-                    &format!("{}::new", self.type_name),
-                    &self.customize,
-                );
-            },
-        );
-    }
-
-    fn write_compute_size(&self, w: &mut CodeWriter) {
-        // Append sizes of messages in the tree to the specified vector.
-        // First appended element is size of self, and then nested message sizes.
-        // in serialization order are appended recursively.");
-        w.comment("Compute sizes of nested messages");
-        // there are unused variables in oneof
-        w.allow(&["unused_variables"]);
-        w.def_fn("compute_size(&self) -> u32", |w| {
-            // To have access to its methods but not polute the name space.
-            w.write_line("let mut my_size = 0;");
-            for field in self.fields_except_oneof_and_group() {
-                field.write_message_compute_field_size("my_size", w);
-            }
-            self.write_match_each_oneof_variant(w, |w, variant, v, vtype| {
-                variant.field.write_element_size(w, v, vtype, "my_size");
-            });
-            w.write_line(&format!(
-                "my_size += {}::rt::unknown_fields_size(self.get_unknown_fields());",
-                protobuf_crate_path(&self.customize)
-            ));
-            w.write_line("self.cached_size.set(my_size);");
-            w.write_line("my_size");
-        });
-    }
-
-    fn write_field_accessors(&self, w: &mut CodeWriter) {
-        for f in self.fields_except_group() {
-            w.write_line("");
-            let reconstruct_def = f.reconstruct_def();
-            w.comment(&(reconstruct_def + ";"));
-            w.write_line("");
-            f.write_message_single_field_accessors(w);
-        }
-    }
-
-    fn write_impl_self(&self, w: &mut CodeWriter) {
-        w.impl_self_block(&self.type_name.to_string(), |w| {
-            // TODO: new should probably be a part of Message trait
-            w.pub_fn(&format!("new() -> {}", self.type_name), |w| {
-                w.write_line("::std::default::Default::default()");
-            });
-
-            self.write_field_accessors(w);
-        });
-    }
-
-    fn write_unknown_fields(&self, w: &mut CodeWriter) {
-        w.def_fn(
-            &format!(
-                "get_unknown_fields(&self) -> &{}::UnknownFields",
-                protobuf_crate_path(&self.customize)
-            ),
-            |w| {
-                w.write_line("&self.unknown_fields");
-            },
-        );
-        w.write_line("");
-        w.def_fn(
-            &format!(
-                "mut_unknown_fields(&mut self) -> &mut {}::UnknownFields",
-                protobuf_crate_path(&self.customize)
-            ),
-            |w| {
-                w.write_line("&mut self.unknown_fields");
-            },
-        );
-    }
-
-    fn write_merge_from(&self, w: &mut CodeWriter) {
-        let sig = format!(
-            "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::ProtobufResult<()>",
-            protobuf_crate_path(&self.customize),
-            protobuf_crate_path(&self.customize),
-        );
-        w.def_fn(&sig, |w| {
-            w.while_block("!is.eof()?", |w| {
-                w.write_line(&format!("let (field_number, wire_type) = is.read_tag_unpack()?;"));
-                w.match_block("field_number", |w| {
-                    for f in &self.fields_except_group() {
-                        let number = f.proto_field.number();
-                        w.case_block(number.to_string(), |w| {
-                            f.write_merge_from_field("wire_type", w);
-                        });
-                    }
-                    w.case_block("_", |w| {
-                        w.write_line(&format!("{}::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;", protobuf_crate_path(&self.customize)));
-                    });
-                });
-            });
-            w.write_line("::std::result::Result::Ok(())");
-        });
-    }
-
-    fn write_descriptor_field(&self, fields_var: &str, field: &FieldGen, w: &mut CodeWriter) {
-        let accessor_fn = field.accessor_fn();
-        w.write_line(&format!(
-            "{}.push({}::reflect::accessor::{}(",
-            fields_var,
-            protobuf_crate_path(&self.customize),
-            accessor_fn.sig()
-        ));
-        w.indented(|w| {
-            w.write_line(&format!("\"{}\",", field.proto_field.name()));
-            match accessor_fn.style {
-                AccessorStyle::Lambda => {
-                    w.write_line(&format!(
-                        "|m: &{}| {{ &m.{} }},",
-                        self.type_name, field.rust_name
-                    ));
-                    w.write_line(&format!(
-                        "|m: &mut {}| {{ &mut m.{} }},",
-                        self.type_name, field.rust_name
-                    ));
-                }
-                AccessorStyle::HasGet => {
-                    w.write_line(&format!("{}::has_{},", self.type_name, field.rust_name));
-                    w.write_line(&format!("{}::get_{},", self.type_name, field.rust_name));
-                }
-            }
-        });
-        w.write_line("));");
-    }
-
-    fn write_descriptor_static(&self, w: &mut CodeWriter) {
-        w.def_fn(
-            &format!(
-                "descriptor_static() -> &'static {}::reflect::MessageDescriptor",
-                protobuf_crate_path(&self.customize)
-            ),
-            |w| {
-                w.lazy_static_decl_get(
-                    "descriptor",
-                    &format!(
-                        "{}::reflect::MessageDescriptor",
-                        protobuf_crate_path(&self.customize)
-                    ),
-                    &self.customize,
-                    |w| {
-                        let fields = self.fields_except_group();
-                        if fields.is_empty() {
-                            w.write_line(&format!("let fields = ::std::vec::Vec::new();"));
-                        } else {
-                            w.write_line(&format!("let mut fields = ::std::vec::Vec::new();"));
-                        }
-                        for field in fields {
-                            self.write_descriptor_field("fields", field, w);
-                        }
-                        w.write_line(&format!(
-                            "{}::reflect::MessageDescriptor::new_pb_name::<{}>(",
-                            protobuf_crate_path(&self.customize),
-                            self.type_name
-                        ));
-                        w.indented(|w| {
-                            w.write_line(&format!("\"{}\",", self.message.name_to_package()));
-                            w.write_line("fields,");
-                            w.write_line(&file_descriptor_proto_expr(&self.message.scope));
-                        });
-                        w.write_line(")");
-                    },
-                );
-            },
-        );
-    }
-
-    fn write_is_initialized(&self, w: &mut CodeWriter) {
-        w.def_fn(&format!("is_initialized(&self) -> bool"), |w| {
-            // TODO: use single loop
-
-            for f in self.required_fields() {
-                f.write_if_self_field_is_none(w, |w| {
-                    w.write_line("return false;");
-                });
-            }
-
-            for f in self.message_fields() {
-                if let FieldKind::Map(..) = f.kind {
-                    // TODO: check values
-                    continue;
-                }
-
-                // TODO:
-                // if message is declared in this file and has no message fields,
-                // we could skip the check here
-                f.write_for_self_field(w, "v", |w, _t| {
-                    w.if_stmt("!v.is_initialized()", |w| {
-                        w.write_line("return false;");
-                    });
-                });
-            }
-            w.write_line("true");
-        });
-    }
-
-    fn write_impl_message(&self, w: &mut CodeWriter) {
-        w.impl_for_block(
-            &format!("{}::Message", protobuf_crate_path(&self.customize)),
-            &self.type_name.to_string(), |w| {
-            self.write_is_initialized(w);
-            w.write_line("");
-            self.write_merge_from(w);
-            w.write_line("");
-            self.write_compute_size(w);
-            w.write_line("");
-            self.write_write_to_with_cached_sizes(w);
-            w.write_line("");
-            self.write_get_cached_size(w);
-            w.write_line("");
-            self.write_unknown_fields(w);
-            w.write_line("");
-            w.def_fn("as_any(&self) -> &dyn (::std::any::Any)", |w| {
-                w.write_line("self as &dyn (::std::any::Any)");
-            });
-            w.def_fn("as_any_mut(&mut self) -> &mut dyn (::std::any::Any)", |w| {
-                w.write_line("self as &mut dyn (::std::any::Any)");
-            });
-            w.def_fn(
-                "into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)>",
-                |w| {
-                    w.write_line("self");
-                },
-            );
-            w.write_line("");
-            w.def_fn(
-                &format!("descriptor(&self) -> &'static {}::reflect::MessageDescriptor", protobuf_crate_path(&self.customize)),
-                |w| {
-                    w.write_line("Self::descriptor_static()");
-                },
-            );
-            w.write_line("");
-            w.def_fn(&format!("new() -> {}", self.type_name), |w| {
-                w.write_line(&format!("{}::new()", self.type_name));
-            });
-            if !self.lite_runtime {
-                w.write_line("");
-                self.write_descriptor_static(w);
-            }
-            w.write_line("");
-            self.write_default_instance(w);
-        });
-    }
-
-    fn write_impl_value(&self, w: &mut CodeWriter) {
-        w.impl_for_block(
-            &format!(
-                "{}::reflect::ProtobufValue",
-                protobuf_crate_path(&self.customize)
-            ),
-            &self.type_name.to_string(),
-            |w| {
-                w.def_fn(
-                    &format!(
-                        "as_ref(&self) -> {}::reflect::ReflectValueRef",
-                        protobuf_crate_path(&self.customize)
-                    ),
-                    |w| {
-                        w.write_line(&format!(
-                            "{}::reflect::ReflectValueRef::Message(self)",
-                            protobuf_crate_path(&self.customize)
-                        ))
-                    },
-                )
-            },
-        )
-    }
-
-    fn write_impl_show(&self, w: &mut CodeWriter) {
-        w.impl_for_block("::std::fmt::Debug", &self.type_name.to_string(), |w| {
-            w.def_fn(
-                "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result",
-                |w| {
-                    w.write_line(&format!(
-                        "{}::text_format::fmt(self, f)",
-                        protobuf_crate_path(&self.customize)
-                    ));
-                },
-            );
-        });
-    }
-
-    fn write_impl_clear(&self, w: &mut CodeWriter) {
-        w.impl_for_block(
-            &format!("{}::Clear", protobuf_crate_path(&self.customize)),
-            &format!("{}", self.type_name),
-            |w| {
-                w.def_fn("clear(&mut self)", |w| {
-                    for f in self.fields_except_group() {
-                        f.write_clear(w);
-                    }
-                    w.write_line("self.unknown_fields.clear();");
-                });
-            },
-        );
-    }
-
-    #[allow(dead_code)]
-    fn supports_derive_partial_eq(&self) -> bool {
-        // There's stack overflow in the compiler when struct has too many fields
-        // https://github.com/rust-lang/rust/issues/40119
-        self.fields.len() <= 500
-    }
-
-    fn write_struct(&self, w: &mut CodeWriter) {
-        let mut derive = vec!["PartialEq", "Clone", "Default"];
-        if self.lite_runtime {
-            derive.push("Debug");
-        }
-        w.derive(&derive);
-        serde::write_serde_attr(
-            w,
-            &self.customize,
-            "derive(::serde::Serialize, ::serde::Deserialize)",
-        );
-        w.pub_struct(&self.type_name.to_string(), |w| {
-            if !self.fields_except_oneof().is_empty() {
-                w.comment("message fields");
-                for field in self.fields_except_oneof() {
-                    if field.proto_type == FieldDescriptorProto_Type::TYPE_GROUP {
-                        w.comment(&format!("{}: <group>", &field.rust_name));
-                    } else {
-                        let vis = if field.expose_field {
-                            Visibility::Public
-                        } else {
-                            match field.kind {
-                                FieldKind::Repeated(..) => Visibility::Default,
-                                FieldKind::Singular(SingularField { ref flag, .. }) => {
-                                    match *flag {
-                                        SingularFieldFlag::WithFlag { .. } => Visibility::Default,
-                                        SingularFieldFlag::WithoutFlag => Visibility::Public,
-                                    }
-                                }
-                                FieldKind::Map(..) => Visibility::Public,
-                                FieldKind::Oneof(..) => unreachable!(),
-                            }
-                        };
-                        w.field_decl_vis(
-                            vis,
-                            &field.rust_name.get(),
-                            &field.full_storage_type().to_code(&self.customize),
-                        );
-                    }
-                }
-            }
-            if !self.oneofs().is_empty() {
-                w.comment("message oneof groups");
-                for oneof in self.oneofs() {
-                    let vis = match self.expose_oneof() {
-                        true => Visibility::Public,
-                        false => Visibility::Default,
-                    };
-                    w.field_decl_vis(
-                        vis,
-                        oneof.oneof.field_name().get(),
-                        &oneof.full_storage_type().to_code(&self.customize),
-                    );
-                }
-            }
-            w.comment("special fields");
-            serde::write_serde_attr(w, &self.customize, "serde(skip)");
-            w.pub_field_decl(
-                "unknown_fields",
-                &format!("{}::UnknownFields", protobuf_crate_path(&self.customize)),
-            );
-            serde::write_serde_attr(w, &self.customize, "serde(skip)");
-            w.pub_field_decl(
-                "cached_size",
-                &format!("{}::CachedSize", protobuf_crate_path(&self.customize)),
-            );
-        });
-    }
-
-    fn write_impl_default_for_amp(&self, w: &mut CodeWriter) {
-        w.impl_args_for_block(
-            &["'a"],
-            "::std::default::Default",
-            &format!("&'a {}", self.type_name),
-            |w| {
-                w.def_fn(&format!("default() -> &'a {}", self.type_name), |w| {
-                    w.write_line(&format!(
-                        "<{} as {}::Message>::default_instance()",
-                        self.type_name,
-                        protobuf_crate_path(&self.customize),
-                    ));
-                });
-            },
-        );
-    }
-
-    pub fn write(&self, w: &mut CodeWriter) {
-        self.write_struct(w);
-
-        w.write_line("");
-        self.write_impl_default_for_amp(w);
-
-        for oneof in self.oneofs() {
-            w.write_line("");
-            oneof.write_enum(w);
-        }
-
-        w.write_line("");
-        self.write_impl_self(w);
-        w.write_line("");
-        self.write_impl_message(w);
-        w.write_line("");
-        self.write_impl_clear(w);
-        if !self.lite_runtime {
-            w.write_line("");
-            self.write_impl_show(w);
-        }
-        w.write_line("");
-        self.write_impl_value(w);
-
-        let mut nested_prefix = self.type_name.to_string();
-        nested_prefix.push_str("_");
-
-        for nested in &self.message.to_scope().get_messages() {
-            // ignore map entries, because they are not used in map fields
-            if nested.map_entry().is_none() {
-                w.write_line("");
-                MessageGen::new(nested, self.root_scope, &self.customize).write(w);
-            }
-        }
-
-        for enum_type in &self.message.to_scope().get_enums() {
-            w.write_line("");
-            let current_file = self.message.get_scope().get_file_descriptor();
-            EnumGen::new(enum_type, current_file, &self.customize, self.root_scope).write(w);
-        }
-    }
-}
diff --git a/src/oneof.rs b/src/oneof.rs
deleted file mode 100644
index 44201ea..0000000
--- a/src/oneof.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-//! Oneof-related codegen functions.
-
-use std::collections::HashSet;
-
-use code_writer::CodeWriter;
-use field::FieldElem;
-use field::FieldGen;
-use message::MessageGen;
-use protobuf::descriptor::FieldDescriptorProto_Type;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use rust_types_values::RustType;
-use scope::FieldWithContext;
-use scope::OneofVariantWithContext;
-use scope::OneofWithContext;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-use Customize;
-
-// oneof one { ... }
-#[derive(Clone)]
-pub(crate) struct OneofField<'a> {
-    pub elem: FieldElem<'a>,
-    pub oneof_rust_field_name: RustIdent,
-    pub oneof_type_name: RustType,
-    pub boxed: bool,
-}
-
-impl<'a> OneofField<'a> {
-    // Detecting recursion: if oneof fields contains a self-reference
-    // or another message which has a reference to self,
-    // put oneof variant into a box.
-    fn need_boxed(field: &FieldWithContext, root_scope: &RootScope, owner_name: &str) -> bool {
-        let mut visited_messages = HashSet::new();
-        let mut fields = vec![field.clone()];
-        while let Some(field) = fields.pop() {
-            if field.field.get_field_type() == FieldDescriptorProto_Type::TYPE_MESSAGE {
-                let message_name = ProtobufAbsolutePath::from(field.field.get_type_name());
-                if !visited_messages.insert(message_name.clone()) {
-                    continue;
-                }
-                if message_name.path == owner_name {
-                    return true;
-                }
-                let message = root_scope.find_message(&message_name);
-                fields.extend(message.fields().into_iter().filter(|f| f.is_oneof()));
-            }
-        }
-        false
-    }
-
-    pub fn parse(
-        oneof: &OneofWithContext<'a>,
-        field: &FieldWithContext<'a>,
-        elem: FieldElem<'a>,
-        root_scope: &RootScope,
-    ) -> OneofField<'a> {
-        let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute().path);
-
-        OneofField {
-            elem,
-            boxed,
-            oneof_rust_field_name: oneof.field_name().into(),
-            oneof_type_name: RustType::Oneof(oneof.rust_name().to_string()),
-        }
-    }
-
-    pub fn rust_type(&self) -> RustType {
-        let t = self.elem.rust_storage_type();
-
-        if self.boxed {
-            RustType::Uniq(Box::new(t))
-        } else {
-            t
-        }
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofVariantGen<'a> {
-    _oneof: &'a OneofGen<'a>,
-    _variant: OneofVariantWithContext<'a>,
-    oneof_field: OneofField<'a>,
-    pub field: FieldGen<'a>,
-    path: String,
-    _customize: Customize,
-}
-
-impl<'a> OneofVariantGen<'a> {
-    fn parse(
-        oneof: &'a OneofGen<'a>,
-        variant: OneofVariantWithContext<'a>,
-        field: &'a FieldGen,
-        _root_scope: &RootScope,
-        customize: Customize,
-    ) -> OneofVariantGen<'a> {
-        OneofVariantGen {
-            _oneof: oneof,
-            _variant: variant.clone(),
-            field: field.clone(),
-            path: format!(
-                "{}::{}",
-                oneof.type_name.to_code(&field.customize),
-                field.rust_name
-            ),
-            oneof_field: OneofField::parse(
-                variant.oneof,
-                &field.proto_field,
-                field.oneof().elem.clone(),
-                oneof.message.root_scope,
-            ),
-            _customize: customize,
-        }
-    }
-
-    fn rust_type(&self) -> RustType {
-        self.oneof_field.rust_type()
-    }
-
-    pub fn path(&self) -> String {
-        self.path.clone()
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofGen<'a> {
-    // Message containing this oneof
-    message: &'a MessageGen<'a>,
-    pub oneof: OneofWithContext<'a>,
-    type_name: RustType,
-    customize: Customize,
-}
-
-impl<'a> OneofGen<'a> {
-    pub fn parse(
-        message: &'a MessageGen,
-        oneof: OneofWithContext<'a>,
-        customize: &Customize,
-    ) -> OneofGen<'a> {
-        let rust_name = oneof.rust_name();
-        OneofGen {
-            message,
-            oneof,
-            type_name: RustType::Oneof(rust_name.to_string()),
-            customize: customize.clone(),
-        }
-    }
-
-    pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
-        self.oneof
-            .variants()
-            .into_iter()
-            .filter_map(|v| {
-                let field = self
-                    .message
-                    .fields
-                    .iter()
-                    .filter(|f| f.proto_field.name() == v.field.get_name())
-                    .next()
-                    .expect(&format!("field not found by name: {}", v.field.get_name()));
-                match field.proto_type {
-                    FieldDescriptorProto_Type::TYPE_GROUP => None,
-                    _ => Some(OneofVariantGen::parse(
-                        self,
-                        v,
-                        field,
-                        self.message.root_scope,
-                        self.customize.clone(),
-                    )),
-                }
-            })
-            .collect()
-    }
-
-    pub fn full_storage_type(&self) -> RustType {
-        RustType::Option(Box::new(self.type_name.clone()))
-    }
-
-    pub fn write_enum(&self, w: &mut CodeWriter) {
-        let derive = vec!["Clone", "PartialEq", "Debug"];
-        w.derive(&derive);
-        serde::write_serde_attr(
-            w,
-            &self.customize,
-            "derive(::serde::Serialize, ::serde::Deserialize)",
-        );
-        w.pub_enum(&self.type_name.to_code(&self.customize), |w| {
-            for variant in self.variants_except_group() {
-                w.write_line(&format!(
-                    "{}({}),",
-                    variant.field.rust_name,
-                    &variant.rust_type().to_code(&self.customize)
-                ));
-            }
-        });
-    }
-}
diff --git a/src/protobuf_name.rs b/src/protobuf_name.rs
deleted file mode 100644
index c40a69c..0000000
--- a/src/protobuf_name.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-use std::fmt;
-
-/// Identifier in `.proto` file
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub struct ProtobufIdent(String);
-
-impl ProtobufIdent {
-    /// New ident from a string.
-    #[allow(dead_code)]
-    pub fn new(s: &str) -> ProtobufIdent {
-        assert!(!s.is_empty());
-        assert!(!s.contains("/"));
-        assert!(!s.contains("."));
-        assert!(!s.contains(":"));
-        ProtobufIdent(s.to_owned())
-    }
-
-    /// Get as a string.
-    pub fn get(&self) -> &str {
-        &self.0
-    }
-}
-
-impl From<&'_ str> for ProtobufIdent {
-    fn from(s: &str) -> Self {
-        ProtobufIdent::new(s)
-    }
-}
-
-impl From<String> for ProtobufIdent {
-    fn from(s: String) -> Self {
-        ProtobufIdent::new(&s)
-    }
-}
-
-impl fmt::Display for ProtobufIdent {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.get(), f)
-    }
-}
-
-/// Relative protobuf identifier path.
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub struct ProtobufRelativePath {
-    /// The path
-    pub path: String,
-}
-
-#[allow(dead_code)]
-impl ProtobufRelativePath {
-    /// Empty relative path.
-    pub fn empty() -> ProtobufRelativePath {
-        ProtobufRelativePath::new(String::new())
-    }
-
-    /// New path from a string.
-    pub fn new(path: String) -> ProtobufRelativePath {
-        assert!(!path.starts_with("."));
-
-        ProtobufRelativePath { path }
-    }
-
-    /// From path components.
-    pub fn from_components<I: IntoIterator<Item = ProtobufIdent>>(i: I) -> ProtobufRelativePath {
-        let v: Vec<String> = i.into_iter().map(|c| c.get().to_owned()).collect();
-        ProtobufRelativePath::from(v.join("."))
-    }
-
-    /// Get the string.
-    pub fn get(&self) -> &str {
-        &self.path
-    }
-
-    /// The path is empty.
-    pub fn is_empty(&self) -> bool {
-        self.path.is_empty()
-    }
-
-    /// As absolute path from root namespace.
-    pub fn into_absolute(self) -> ProtobufAbsolutePath {
-        if self.is_empty() {
-            ProtobufAbsolutePath::root()
-        } else {
-            ProtobufAbsolutePath::from(format!(".{}", self))
-        }
-    }
-
-    fn _last_part(&self) -> Option<&str> {
-        match self.path.rfind('.') {
-            Some(pos) => Some(&self.path[pos + 1..]),
-            None => {
-                if self.path.is_empty() {
-                    None
-                } else {
-                    Some(&self.path)
-                }
-            }
-        }
-    }
-
-    fn parent(&self) -> Option<ProtobufRelativePath> {
-        match self.path.rfind('.') {
-            Some(pos) => Some(ProtobufRelativePath::new(self.path[..pos].to_owned())),
-            None => {
-                if self.path.is_empty() {
-                    None
-                } else {
-                    Some(ProtobufRelativePath::empty())
-                }
-            }
-        }
-    }
-
-    /// Self path and parent paths.
-    pub fn self_and_parents(&self) -> Vec<ProtobufRelativePath> {
-        let mut tmp = self.clone();
-
-        let mut r = Vec::new();
-
-        r.push(self.clone());
-
-        while let Some(parent) = tmp.parent() {
-            r.push(parent.clone());
-            tmp = parent;
-        }
-
-        r
-    }
-
-    /// Append path component.
-    pub fn append(&self, simple: &ProtobufRelativePath) -> ProtobufRelativePath {
-        if self.path.is_empty() {
-            ProtobufRelativePath::from(simple.get())
-        } else {
-            ProtobufRelativePath::new(format!("{}.{}", self.path, simple))
-        }
-    }
-
-    /// Append identifier to the path.
-    pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath {
-        self.append(&ProtobufRelativePath::from(simple.clone()))
-    }
-
-    /// Get first component path and remaining.
-    pub fn split_first_rem(&self) -> Option<(ProtobufIdent, ProtobufRelativePath)> {
-        if self.is_empty() {
-            None
-        } else {
-            Some(match self.path.find('.') {
-                Some(dot) => (
-                    ProtobufIdent::from(&self.path[..dot]),
-                    ProtobufRelativePath::new(self.path[dot + 1..].to_owned()),
-                ),
-                None => (
-                    ProtobufIdent::from(self.path.clone()),
-                    ProtobufRelativePath::empty(),
-                ),
-            })
-        }
-    }
-}
-
-impl From<&'_ str> for ProtobufRelativePath {
-    fn from(s: &str) -> ProtobufRelativePath {
-        ProtobufRelativePath::from(s.to_owned())
-    }
-}
-
-impl From<String> for ProtobufRelativePath {
-    fn from(s: String) -> ProtobufRelativePath {
-        ProtobufRelativePath::new(s)
-    }
-}
-
-impl From<ProtobufIdent> for ProtobufRelativePath {
-    fn from(s: ProtobufIdent) -> ProtobufRelativePath {
-        ProtobufRelativePath::from(s.get())
-    }
-}
-
-impl From<Vec<ProtobufIdent>> for ProtobufRelativePath {
-    fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath {
-        ProtobufRelativePath::from_components(s.into_iter())
-    }
-}
-
-impl fmt::Display for ProtobufRelativePath {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.path, f)
-    }
-}
-
-#[cfg(test)]
-mod relative_path_test {
-    use super::*;
-
-    #[test]
-    fn parent() {
-        assert_eq!(None, ProtobufRelativePath::empty().parent());
-        assert_eq!(
-            Some(ProtobufRelativePath::empty()),
-            ProtobufRelativePath::new("aaa".to_owned()).parent()
-        );
-        assert_eq!(
-            Some(ProtobufRelativePath::new("abc".to_owned())),
-            ProtobufRelativePath::new("abc.def".to_owned()).parent()
-        );
-        assert_eq!(
-            Some(ProtobufRelativePath::new("abc.def".to_owned())),
-            ProtobufRelativePath::new("abc.def.gh".to_owned()).parent()
-        );
-    }
-
-    #[test]
-    fn last_part() {
-        assert_eq!(None, ProtobufRelativePath::empty()._last_part());
-        assert_eq!(
-            Some("aaa"),
-            ProtobufRelativePath::new("aaa".to_owned())._last_part()
-        );
-        assert_eq!(
-            Some("def"),
-            ProtobufRelativePath::new("abc.def".to_owned())._last_part()
-        );
-        assert_eq!(
-            Some("gh"),
-            ProtobufRelativePath::new("abc.def.gh".to_owned())._last_part()
-        );
-    }
-}
-
-/// Absolute protobuf path (e. g. package).
-///
-/// This is not filesystem path.
-#[derive(Clone, Eq, PartialEq, Debug, Hash)]
-pub struct ProtobufAbsolutePath {
-    /// The path.
-    pub path: String,
-}
-
-impl ProtobufAbsolutePath {
-    fn root() -> ProtobufAbsolutePath {
-        ProtobufAbsolutePath::new(String::new())
-    }
-
-    /// From string.
-    pub fn new(path: String) -> ProtobufAbsolutePath {
-        assert!(path.is_empty() || path.starts_with("."), "{}", path);
-        assert!(!path.ends_with("."), "{}", path);
-        ProtobufAbsolutePath { path }
-    }
-
-    /// The path is empty.
-    pub fn is_empty(&self) -> bool {
-        self.path.is_empty()
-    }
-
-    /// From a path without leading dot.
-    ///
-    /// (Protobuf paths start with dot).
-    pub fn from_path_without_dot(path: &str) -> ProtobufAbsolutePath {
-        if path.is_empty() {
-            ProtobufAbsolutePath::root()
-        } else {
-            assert!(!path.starts_with("."));
-            assert!(!path.ends_with("."));
-            ProtobufAbsolutePath::new(format!(".{}", path))
-        }
-    }
-
-    /// Parse absolute path.
-    #[allow(dead_code)]
-    pub fn from_package_path(path: Option<&str>) -> ProtobufAbsolutePath {
-        match path {
-            None => ProtobufAbsolutePath::root(),
-            Some(path) => ProtobufAbsolutePath::from_path_without_dot(path),
-        }
-    }
-
-    /// Construct abs path from a string which may start with a dot.
-    pub fn from_path_maybe_dot(path: &str) -> ProtobufAbsolutePath {
-        if path.starts_with(".") {
-            ProtobufAbsolutePath::new(path.to_owned())
-        } else {
-            ProtobufAbsolutePath::from_path_without_dot(path)
-        }
-    }
-
-    /// Push identifier to the path.
-    pub fn push_simple(&mut self, simple: ProtobufIdent) {
-        self.path.push('.');
-        self.path.push_str(simple.get());
-    }
-
-    /// Push relative path.
-    pub fn push_relative(&mut self, relative: &ProtobufRelativePath) {
-        if !relative.is_empty() {
-            self.path.push('.');
-            self.path.push_str(&relative.path);
-        }
-    }
-
-    /// Try remove a prefix.
-    pub fn remove_prefix(&self, prefix: &ProtobufAbsolutePath) -> Option<ProtobufRelativePath> {
-        if self.path.starts_with(&prefix.path) {
-            let rem = &self.path[prefix.path.len()..];
-            if rem.is_empty() {
-                return Some(ProtobufRelativePath::empty());
-            }
-            if rem.starts_with('.') {
-                return Some(ProtobufRelativePath::new(rem[1..].to_owned()));
-            }
-        }
-        None
-    }
-}
-
-impl From<&'_ str> for ProtobufAbsolutePath {
-    fn from(s: &str) -> Self {
-        ProtobufAbsolutePath::new(s.to_owned())
-    }
-}
-
-impl From<String> for ProtobufAbsolutePath {
-    fn from(s: String) -> Self {
-        ProtobufAbsolutePath::new(s)
-    }
-}
-
-impl fmt::Display for ProtobufAbsolutePath {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.path, f)
-    }
-}
-
-#[cfg(test)]
-mod absolute_path_test {
-    use super::*;
-
-    #[test]
-    fn absolute_path_push_simple() {
-        let mut foo = ProtobufAbsolutePath::new(".foo".to_owned());
-        foo.push_simple(ProtobufIdent::from("bar"));
-        assert_eq!(ProtobufAbsolutePath::new(".foo.bar".to_owned()), foo);
-
-        let mut foo = ProtobufAbsolutePath::root();
-        foo.push_simple(ProtobufIdent::from("bar"));
-        assert_eq!(ProtobufAbsolutePath::new(".bar".to_owned()), foo);
-    }
-
-    #[test]
-    fn absolute_path_remove_prefix() {
-        assert_eq!(
-            Some(ProtobufRelativePath::empty()),
-            ProtobufAbsolutePath::new(".foo".to_owned())
-                .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
-        );
-        assert_eq!(
-            Some(ProtobufRelativePath::new("bar".to_owned())),
-            ProtobufAbsolutePath::new(".foo.bar".to_owned())
-                .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
-        );
-        assert_eq!(
-            Some(ProtobufRelativePath::new("baz.qux".to_owned())),
-            ProtobufAbsolutePath::new(".foo.bar.baz.qux".to_owned())
-                .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
-        );
-        assert_eq!(
-            None,
-            ProtobufAbsolutePath::new(".foo.barbaz".to_owned())
-                .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
-        );
-    }
-}
diff --git a/src/protoc_gen_rust.rs b/src/protoc_gen_rust.rs
new file mode 100644
index 0000000..014db6a
--- /dev/null
+++ b/src/protoc_gen_rust.rs
@@ -0,0 +1,21 @@
+#![doc(hidden)]
+
+use crate::compiler_plugin;
+use crate::customize::CustomizeCallbackDefault;
+use crate::gen::all::gen_all;
+use crate::Customize;
+
+#[doc(hidden)]
+pub fn protoc_gen_rust_main() {
+    compiler_plugin::plugin_main(|r| {
+        let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
+        gen_all(
+            r.file_descriptors,
+            "protoc --rust-out=...",
+            r.files_to_generate,
+            &customize,
+            &CustomizeCallbackDefault,
+        )
+    })
+    .expect("plugin failed");
+}
diff --git a/src/rust.rs b/src/rust.rs
deleted file mode 100644
index 263cba0..0000000
--- a/src/rust.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-#[cfg_attr(rustfmt, rustfmt_skip)]
-static RUST_KEYWORDS: &'static [&'static str] = &[
-    "as",
-    "async",
-    "await",
-    "break",
-    "crate",
-    "dyn",
-    "else",
-    "enum",
-    "extern",
-    "false",
-    "fn",
-    "for",
-    "if",
-    "impl",
-    "in",
-    "let",
-    "loop",
-    "match",
-    "mod",
-    "move",
-    "mut",
-    "pub",
-    "ref",
-    "return",
-    "static",
-    "self",
-    "Self",
-    "struct",
-    "super",
-    "true",
-    "trait",
-    "type",
-    "unsafe",
-    "use",
-    "while",
-    "continue",
-    "box",
-    "const",
-    "where",
-    "virtual",
-    "proc",
-    "alignof",
-    "become",
-    "offsetof",
-    "priv",
-    "pure",
-    "sizeof",
-    "typeof",
-    "unsized",
-    "yield",
-    "do",
-    "abstract",
-    "final",
-    "override",
-    "macro",
-];
-
-pub fn is_rust_keyword(ident: &str) -> bool {
-    RUST_KEYWORDS.contains(&ident)
-}
diff --git a/src/rust_name.rs b/src/rust_name.rs
deleted file mode 100644
index 234925b..0000000
--- a/src/rust_name.rs
+++ /dev/null
@@ -1,273 +0,0 @@
-use std::fmt;
-use std::iter;
-
-/// Valid Rust identifier
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustIdent(String);
-
-#[allow(dead_code)]
-impl RustIdent {
-    pub fn new(s: &str) -> RustIdent {
-        assert!(!s.is_empty());
-        assert!(!s.contains("/"), "{}", s);
-        assert!(!s.contains("."), "{}", s);
-        assert!(!s.contains(":"), "{}", s);
-        RustIdent(s.to_owned())
-    }
-
-    pub fn super_ident() -> RustIdent {
-        RustIdent::new("super")
-    }
-
-    pub fn get(&self) -> &str {
-        &self.0
-    }
-
-    pub fn into_string(self) -> String {
-        self.0
-    }
-
-    pub fn to_path(&self) -> RustIdentWithPath {
-        RustIdentWithPath::from(&self.0)
-    }
-}
-
-impl fmt::Display for RustIdent {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.get(), f)
-    }
-}
-
-impl From<&'_ str> for RustIdent {
-    fn from(s: &str) -> Self {
-        RustIdent::new(s)
-    }
-}
-
-impl From<String> for RustIdent {
-    fn from(s: String) -> Self {
-        RustIdent::new(&s)
-    }
-}
-
-impl Into<String> for RustIdent {
-    fn into(self) -> String {
-        self.0
-    }
-}
-
-#[derive(Default, Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustRelativePath {
-    path: Vec<RustIdent>,
-}
-
-#[allow(dead_code)]
-impl RustRelativePath {
-    pub fn into_path(self) -> RustPath {
-        RustPath {
-            absolute: false,
-            path: self,
-        }
-    }
-
-    pub fn empty() -> RustRelativePath {
-        RustRelativePath { path: Vec::new() }
-    }
-
-    pub fn from_components<I: IntoIterator<Item = RustIdent>>(i: I) -> RustRelativePath {
-        RustRelativePath {
-            path: i.into_iter().collect(),
-        }
-    }
-
-    pub fn is_empty(&self) -> bool {
-        self.path.is_empty()
-    }
-
-    pub fn first(&self) -> Option<RustIdent> {
-        self.path.iter().cloned().next()
-    }
-
-    pub fn remove_first(&mut self) -> Option<RustIdent> {
-        if self.path.is_empty() {
-            None
-        } else {
-            Some(self.path.remove(0))
-        }
-    }
-
-    pub fn prepend_ident(&mut self, ident: RustIdent) {
-        self.path.insert(0, ident);
-    }
-
-    pub fn append(mut self, path: RustRelativePath) -> RustRelativePath {
-        for c in path.path {
-            self.path.push(c);
-        }
-        self
-    }
-
-    pub fn push_ident(&mut self, ident: RustIdent) {
-        self.path.push(ident);
-    }
-
-    pub fn _append_ident(mut self, ident: RustIdent) -> RustRelativePath {
-        self.push_ident(ident);
-        self
-    }
-
-    pub fn to_reverse(&self) -> RustRelativePath {
-        RustRelativePath::from_components(
-            iter::repeat(RustIdent::super_ident()).take(self.path.len()),
-        )
-    }
-}
-
-#[derive(Default, Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustPath {
-    absolute: bool,
-    path: RustRelativePath,
-}
-
-impl fmt::Display for RustRelativePath {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        for (i, c) in self.path.iter().enumerate() {
-            if i != 0 {
-                write!(f, "::")?;
-            }
-            write!(f, "{}", c)?;
-        }
-        Ok(())
-    }
-}
-
-impl From<&'_ str> for RustRelativePath {
-    fn from(s: &str) -> Self {
-        RustRelativePath {
-            path: s.split("::").map(RustIdent::from).collect(),
-        }
-    }
-}
-
-#[allow(dead_code)]
-impl RustPath {
-    pub fn is_absolute(&self) -> bool {
-        self.absolute
-    }
-
-    pub fn is_empty(&self) -> bool {
-        assert!(!self.absolute);
-        self.path.is_empty()
-    }
-
-    pub fn with_ident(self, ident: RustIdent) -> RustIdentWithPath {
-        RustIdentWithPath { path: self, ident }
-    }
-
-    pub fn first(&self) -> Option<RustIdent> {
-        assert!(!self.absolute);
-        self.path.first()
-    }
-
-    pub fn remove_first(&mut self) -> Option<RustIdent> {
-        assert!(!self.absolute);
-        self.path.remove_first()
-    }
-
-    pub fn to_reverse(&self) -> RustPath {
-        assert!(!self.absolute);
-        RustPath {
-            absolute: false,
-            path: self.path.to_reverse(),
-        }
-    }
-
-    pub fn prepend_ident(&mut self, ident: RustIdent) {
-        assert!(!self.absolute);
-        self.path.prepend_ident(ident);
-    }
-
-    pub fn append(self, path: RustPath) -> RustPath {
-        if path.absolute {
-            path
-        } else {
-            RustPath {
-                absolute: self.absolute,
-                path: self.path.append(path.path),
-            }
-        }
-    }
-
-    pub fn append_ident(mut self, ident: RustIdent) -> RustPath {
-        self.path.path.push(ident);
-        self
-    }
-
-    pub fn append_with_ident(self, path: RustIdentWithPath) -> RustIdentWithPath {
-        self.append(path.path).with_ident(path.ident)
-    }
-}
-
-impl From<&'_ str> for RustPath {
-    fn from(s: &str) -> Self {
-        let (s, absolute) = if s.starts_with("::") {
-            (&s[2..], true)
-        } else {
-            (s, false)
-        };
-        RustPath {
-            absolute,
-            path: RustRelativePath::from(s),
-        }
-    }
-}
-
-impl From<String> for RustPath {
-    fn from(s: String) -> Self {
-        RustPath::from(&s[..])
-    }
-}
-
-impl fmt::Display for RustPath {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        if self.absolute {
-            write!(f, "::")?;
-        }
-        write!(f, "{}", self.path)
-    }
-}
-
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustIdentWithPath {
-    pub path: RustPath,
-    pub ident: RustIdent,
-}
-
-#[allow(dead_code)]
-impl RustIdentWithPath {
-    pub fn new(s: String) -> RustIdentWithPath {
-        let mut path = RustPath::from(s);
-        let ident = path.path.path.pop().unwrap();
-        RustIdentWithPath { path, ident }
-    }
-
-    pub fn prepend_ident(&mut self, ident: RustIdent) {
-        self.path.prepend_ident(ident)
-    }
-
-    pub fn to_path(&self) -> RustPath {
-        self.path.clone().append_ident(self.ident.clone())
-    }
-}
-
-impl<S: Into<String>> From<S> for RustIdentWithPath {
-    fn from(s: S) -> Self {
-        RustIdentWithPath::new(s.into())
-    }
-}
-
-impl fmt::Display for RustIdentWithPath {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.to_path(), f)
-    }
-}
diff --git a/src/rust_types_values.rs b/src/rust_types_values.rs
deleted file mode 100644
index e9b017d..0000000
--- a/src/rust_types_values.rs
+++ /dev/null
@@ -1,593 +0,0 @@
-use std::cmp;
-
-use inside::protobuf_crate_path;
-use message::RustTypeMessage;
-use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use scope::RootScope;
-use scope::WithScope;
-use strx::capitalize;
-use Customize;
-
-use super::well_known_types::is_well_known_type_full;
-
-// Represent subset of rust types used in generated code
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) enum RustType {
-    // integer: signed?, size in bits
-    Int(bool, u32),
-    // param is size in bits
-    Float(u32),
-    Bool,
-    Vec(Box<RustType>),
-    HashMap(Box<RustType>, Box<RustType>),
-    String,
-    // [T], not &[T]
-    Slice(Box<RustType>),
-    // str, not &str
-    Str,
-    Option(Box<RustType>),
-    SingularField(Box<RustType>),
-    SingularPtrField(Box<RustType>),
-    RepeatedField(Box<RustType>),
-    // Box<T>
-    Uniq(Box<RustType>),
-    // &T
-    Ref(Box<RustType>),
-    // protobuf message
-    Message(RustTypeMessage),
-    // protobuf enum, not any enum
-    Enum(String, RustIdent),
-    // oneof enum
-    Oneof(String),
-    // bytes::Bytes
-    Bytes,
-    // chars::Chars
-    Chars,
-    // group
-    Group,
-}
-
-impl RustType {
-    #[inline]
-    pub(crate) fn to_code(&self, customize: &Customize) -> String {
-        match *self {
-            RustType::Int(true, bits) => format!("i{}", bits),
-            RustType::Int(false, bits) => format!("u{}", bits),
-            RustType::Float(bits) => format!("f{}", bits),
-            RustType::Bool => format!("bool"),
-            RustType::Vec(ref param) => format!("::std::vec::Vec<{}>", param.to_code(customize)),
-            RustType::HashMap(ref key, ref value) => format!(
-                "::std::collections::HashMap<{}, {}>",
-                key.to_code(customize),
-                value.to_code(customize)
-            ),
-            RustType::String => format!("::std::string::String"),
-            RustType::Slice(ref param) => format!("[{}]", param.to_code(customize)),
-            RustType::Str => format!("str"),
-            RustType::Option(ref param) => {
-                format!("::std::option::Option<{}>", param.to_code(customize))
-            }
-            RustType::SingularField(ref param) => format!(
-                "{}::SingularField<{}>",
-                protobuf_crate_path(customize),
-                param.to_code(customize)
-            ),
-            RustType::SingularPtrField(ref param) => format!(
-                "{}::SingularPtrField<{}>",
-                protobuf_crate_path(customize),
-                param.to_code(customize)
-            ),
-            RustType::RepeatedField(ref param) => format!(
-                "{}::RepeatedField<{}>",
-                protobuf_crate_path(customize),
-                param.to_code(customize)
-            ),
-            RustType::Uniq(ref param) => format!("::std::boxed::Box<{}>", param.to_code(customize)),
-            RustType::Ref(ref param) => format!("&{}", param.to_code(customize)),
-            RustType::Message(ref name) => format!("{}", name),
-            RustType::Enum(ref name, _) | RustType::Oneof(ref name) => format!("{}", name),
-            RustType::Group => format!("<group>"),
-            RustType::Bytes => format!("::bytes::Bytes"),
-            RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)),
-        }
-    }
-}
-
-impl RustType {
-    pub fn u8() -> RustType {
-        RustType::Int(false, 8)
-    }
-
-    /// Type is rust primitive?
-    pub fn is_primitive(&self) -> bool {
-        match *self {
-            RustType::Int(..) | RustType::Float(..) | RustType::Bool => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_u8(&self) -> bool {
-        match *self {
-            RustType::Int(false, 8) => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_copy(&self) -> bool {
-        if self.is_primitive() {
-            true
-        } else if let RustType::Enum(..) = *self {
-            true
-        } else {
-            false
-        }
-    }
-
-    fn is_str(&self) -> bool {
-        match *self {
-            RustType::Str => true,
-            _ => false,
-        }
-    }
-
-    fn is_string(&self) -> bool {
-        match *self {
-            RustType::String => true,
-            _ => false,
-        }
-    }
-
-    fn is_slice(&self) -> Option<&RustType> {
-        match *self {
-            RustType::Slice(ref v) => Some(&**v),
-            _ => None,
-        }
-    }
-
-    fn is_slice_u8(&self) -> bool {
-        match self.is_slice() {
-            Some(t) => t.is_u8(),
-            None => false,
-        }
-    }
-
-    fn is_message(&self) -> bool {
-        match *self {
-            RustType::Message(..) => true,
-            _ => false,
-        }
-    }
-
-    fn is_enum(&self) -> bool {
-        match *self {
-            RustType::Enum(..) => true,
-            _ => false,
-        }
-    }
-
-    pub fn is_ref(&self) -> bool {
-        match *self {
-            RustType::Ref(..) => true,
-            _ => false,
-        }
-    }
-
-    // default value for type
-    pub fn default_value(&self, customize: &Customize) -> String {
-        match *self {
-            RustType::Ref(ref t) if t.is_str() => "\"\"".to_string(),
-            RustType::Ref(ref t) if t.is_slice().is_some() => "&[]".to_string(),
-            RustType::Int(..) => "0".to_string(),
-            RustType::Float(..) => "0.".to_string(),
-            RustType::Bool => "false".to_string(),
-            RustType::Vec(..) => "::std::vec::Vec::new()".to_string(),
-            RustType::HashMap(..) => "::std::collections::HashMap::new()".to_string(),
-            RustType::String => "::std::string::String::new()".to_string(),
-            RustType::Bytes => "::bytes::Bytes::new()".to_string(),
-            RustType::Chars => format!("{}::Chars::new()", protobuf_crate_path(customize)),
-            RustType::Option(..) => "::std::option::Option::None".to_string(),
-            RustType::SingularField(..) => {
-                format!("{}::SingularField::none()", protobuf_crate_path(customize))
-            }
-            RustType::SingularPtrField(..) => format!(
-                "{}::SingularPtrField::none()",
-                protobuf_crate_path(customize)
-            ),
-            RustType::RepeatedField(..) => {
-                format!("{}::RepeatedField::new()", protobuf_crate_path(customize))
-            }
-            RustType::Message(ref name) => format!("{}::new()", name),
-            RustType::Ref(ref m) if m.is_message() => match **m {
-                RustType::Message(ref name) => name.default_instance(customize),
-                _ => unreachable!(),
-            },
-            // Note: default value of enum type may not be equal to default value of field
-            RustType::Enum(ref name, ref default) => format!("{}::{}", name, default),
-            _ => panic!("cannot create default value for: {:?}", *self),
-        }
-    }
-
-    pub fn default_value_typed(self, customize: &Customize) -> RustValueTyped {
-        RustValueTyped {
-            value: self.default_value(customize),
-            rust_type: self,
-        }
-    }
-
-    /// Emit a code to clear a variable `v`
-    pub fn clear(&self, v: &str, customize: &Customize) -> String {
-        match *self {
-            RustType::Option(..) => format!("{} = ::std::option::Option::None", v),
-            RustType::Vec(..)
-            | RustType::Bytes
-            | RustType::String
-            | RustType::RepeatedField(..)
-            | RustType::SingularField(..)
-            | RustType::SingularPtrField(..)
-            | RustType::HashMap(..) => format!("{}.clear()", v),
-            RustType::Chars => format!(
-                "{}::Clear::clear(&mut {})",
-                protobuf_crate_path(customize),
-                v
-            ),
-            RustType::Bool | RustType::Float(..) | RustType::Int(..) | RustType::Enum(..) => {
-                format!("{} = {}", v, self.default_value(customize))
-            }
-            ref ty => panic!("cannot clear type: {:?}", ty),
-        }
-    }
-
-    // wrap value in storage type
-    pub fn wrap_value(&self, value: &str, customize: &Customize) -> String {
-        match *self {
-            RustType::Option(..) => format!("::std::option::Option::Some({})", value),
-            RustType::SingularField(..) => format!(
-                "{}::SingularField::some({})",
-                protobuf_crate_path(customize),
-                value
-            ),
-            RustType::SingularPtrField(..) => format!(
-                "{}::SingularPtrField::some({})",
-                protobuf_crate_path(customize),
-                value
-            ),
-            _ => panic!("not a wrapper type: {:?}", *self),
-        }
-    }
-
-    // expression to convert `v` of type `self` to type `target`
-    pub fn into_target(&self, target: &RustType, v: &str, customize: &Customize) -> String {
-        self.try_into_target(target, v, customize)
-            .expect(&format!("failed to convert {:?} into {:?}", self, target))
-    }
-
-    fn try_into_target(
-        &self,
-        target: &RustType,
-        v: &str,
-        customize: &Customize,
-    ) -> Result<String, ()> {
-        match (self, target) {
-            (x, y) if x == y => return Ok(format!("{}", v)),
-            (&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
-            (x, &RustType::Uniq(ref y)) if *x == **y => {
-                return Ok(format!("::std::boxed::Box::new({})", v))
-            }
-            (&RustType::Uniq(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
-            (&RustType::String, &RustType::Ref(ref t)) if **t == RustType::Str => {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Chars, &RustType::Ref(ref t)) if **t == RustType::Str => {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Ref(ref t1), &RustType::Ref(ref t2)) if t1.is_string() && t2.is_str() => {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Ref(ref t1), &RustType::String)
-                if match **t1 {
-                    RustType::Str => true,
-                    _ => false,
-                } =>
-            {
-                return Ok(format!("{}.to_owned()", v))
-            }
-            (&RustType::Ref(ref t1), &RustType::Chars)
-                if match **t1 {
-                    RustType::Str => true,
-                    _ => false,
-                    // TODO: from_static
-                } =>
-            {
-                return Ok(format!(
-                    "<{}::Chars as ::std::convert::From<_>>::from({}.to_owned())",
-                    protobuf_crate_path(customize),
-                    v
-                ))
-            }
-            (&RustType::Ref(ref t1), &RustType::Vec(ref t2))
-                if match (&**t1, &**t2) {
-                    (&RustType::Slice(ref x), ref y) => **x == **y,
-                    _ => false,
-                } =>
-            {
-                return Ok(format!("{}.to_vec()", v))
-            }
-            (&RustType::Ref(ref t1), &RustType::Bytes) if t1.is_slice_u8() => {
-                return Ok(format!(
-                    "<::bytes::Bytes as ::std::convert::From<_>>::from({}.to_vec())",
-                    v
-                ))
-            }
-            (&RustType::Vec(ref x), &RustType::Ref(ref t))
-                if match **t {
-                    RustType::Slice(ref y) => x == y,
-                    _ => false,
-                } =>
-            {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Bytes, &RustType::Ref(ref t))
-                if match **t {
-                    RustType::Slice(ref y) => **y == RustType::u8(),
-                    _ => false,
-                } =>
-            {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Ref(ref t1), &RustType::Ref(ref t2))
-                if match (&**t1, &**t2) {
-                    (&RustType::Vec(ref x), &RustType::Slice(ref y)) => x == y,
-                    _ => false,
-                } =>
-            {
-                return Ok(format!("&{}", v))
-            }
-            (&RustType::Enum(..), &RustType::Int(true, 32)) => {
-                return Ok(format!(
-                    "{}::ProtobufEnum::value(&{})",
-                    protobuf_crate_path(customize),
-                    v
-                ))
-            }
-            (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum() => {
-                return Ok(format!(
-                    "{}::ProtobufEnum::value({})",
-                    protobuf_crate_path(customize),
-                    v
-                ))
-            }
-            _ => (),
-        };
-
-        if let &RustType::Ref(ref s) = self {
-            if let Ok(conv) = s.try_into_target(target, v, customize) {
-                return Ok(conv);
-            }
-        }
-
-        Err(())
-    }
-
-    /// Type to view data of this type
-    pub fn ref_type(&self) -> RustType {
-        RustType::Ref(Box::new(match self {
-            &RustType::String | &RustType::Chars => RustType::Str,
-            &RustType::Vec(ref p) | &RustType::RepeatedField(ref p) => RustType::Slice(p.clone()),
-            &RustType::Bytes => RustType::Slice(Box::new(RustType::u8())),
-            &RustType::Message(ref p) => RustType::Message(p.clone()),
-            x => panic!("no ref type for {:?}", x),
-        }))
-    }
-
-    pub fn elem_type(&self) -> RustType {
-        match self {
-            &RustType::Option(ref ty) => (**ty).clone(),
-            x => panic!("cannot get elem type of {:?}", x),
-        }
-    }
-
-    // type of `v` in `for v in xxx`
-    pub fn iter_elem_type(&self) -> RustType {
-        match self {
-            &RustType::Vec(ref ty)
-            | &RustType::Option(ref ty)
-            | &RustType::RepeatedField(ref ty)
-            | &RustType::SingularField(ref ty)
-            | &RustType::SingularPtrField(ref ty) => RustType::Ref(ty.clone()),
-            x => panic!("cannot iterate {:?}", x),
-        }
-    }
-
-    pub fn value(self, value: String) -> RustValueTyped {
-        RustValueTyped {
-            value: value,
-            rust_type: self,
-        }
-    }
-}
-
-/// Representation of an expression in code generator: text and type
-pub(crate) struct RustValueTyped {
-    pub value: String,
-    pub rust_type: RustType,
-}
-
-impl RustValueTyped {
-    pub fn into_type(&self, target: RustType, customize: &Customize) -> RustValueTyped {
-        let target_value = self.rust_type.into_target(&target, &self.value, customize);
-        RustValueTyped {
-            value: target_value,
-            rust_type: target,
-        }
-    }
-
-    pub fn boxed(self, customize: &Customize) -> RustValueTyped {
-        self.into_type(RustType::Uniq(Box::new(self.rust_type.clone())), customize)
-    }
-}
-
-// protobuf type name for protobuf base type
-pub fn protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str {
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_DOUBLE => "double",
-        FieldDescriptorProto_Type::TYPE_FLOAT => "float",
-        FieldDescriptorProto_Type::TYPE_INT32 => "int32",
-        FieldDescriptorProto_Type::TYPE_INT64 => "int64",
-        FieldDescriptorProto_Type::TYPE_UINT32 => "uint32",
-        FieldDescriptorProto_Type::TYPE_UINT64 => "uint64",
-        FieldDescriptorProto_Type::TYPE_SINT32 => "sint32",
-        FieldDescriptorProto_Type::TYPE_SINT64 => "sint64",
-        FieldDescriptorProto_Type::TYPE_FIXED32 => "fixed32",
-        FieldDescriptorProto_Type::TYPE_FIXED64 => "fixed64",
-        FieldDescriptorProto_Type::TYPE_SFIXED32 => "sfixed32",
-        FieldDescriptorProto_Type::TYPE_SFIXED64 => "sfixed64",
-        FieldDescriptorProto_Type::TYPE_BOOL => "bool",
-        FieldDescriptorProto_Type::TYPE_STRING => "string",
-        FieldDescriptorProto_Type::TYPE_BYTES => "bytes",
-        FieldDescriptorProto_Type::TYPE_ENUM => "enum",
-        FieldDescriptorProto_Type::TYPE_MESSAGE => "message",
-        FieldDescriptorProto_Type::TYPE_GROUP => "group",
-    }
-}
-
-// rust type for protobuf base type
-pub(crate) fn rust_name(field_type: FieldDescriptorProto_Type) -> RustType {
-    match field_type {
-        FieldDescriptorProto_Type::TYPE_DOUBLE => RustType::Float(64),
-        FieldDescriptorProto_Type::TYPE_FLOAT => RustType::Float(32),
-        FieldDescriptorProto_Type::TYPE_INT32 => RustType::Int(true, 32),
-        FieldDescriptorProto_Type::TYPE_INT64 => RustType::Int(true, 64),
-        FieldDescriptorProto_Type::TYPE_UINT32 => RustType::Int(false, 32),
-        FieldDescriptorProto_Type::TYPE_UINT64 => RustType::Int(false, 64),
-        FieldDescriptorProto_Type::TYPE_SINT32 => RustType::Int(true, 32),
-        FieldDescriptorProto_Type::TYPE_SINT64 => RustType::Int(true, 64),
-        FieldDescriptorProto_Type::TYPE_FIXED32 => RustType::Int(false, 32),
-        FieldDescriptorProto_Type::TYPE_FIXED64 => RustType::Int(false, 64),
-        FieldDescriptorProto_Type::TYPE_SFIXED32 => RustType::Int(true, 32),
-        FieldDescriptorProto_Type::TYPE_SFIXED64 => RustType::Int(true, 64),
-        FieldDescriptorProto_Type::TYPE_BOOL => RustType::Bool,
-        FieldDescriptorProto_Type::TYPE_STRING => RustType::String,
-        FieldDescriptorProto_Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::Int(false, 8))),
-        FieldDescriptorProto_Type::TYPE_ENUM
-        | FieldDescriptorProto_Type::TYPE_GROUP
-        | FieldDescriptorProto_Type::TYPE_MESSAGE => {
-            panic!("there is no rust name for {:?}", field_type)
-        }
-    }
-}
-
-fn file_last_component(file: &str) -> &str {
-    let bs = file.rfind('\\').map(|i| i + 1).unwrap_or(0);
-    let fs = file.rfind('/').map(|i| i + 1).unwrap_or(0);
-    &file[cmp::max(fs, bs)..]
-}
-
-#[cfg(test)]
-#[test]
-fn test_file_last_component() {
-    assert_eq!("ab.proto", file_last_component("ab.proto"));
-    assert_eq!("ab.proto", file_last_component("xx/ab.proto"));
-    assert_eq!("ab.proto", file_last_component("xx\\ab.proto"));
-    assert_eq!("ab.proto", file_last_component("yy\\xx\\ab.proto"));
-}
-
-fn is_descriptor_proto(file: &FileDescriptorProto) -> bool {
-    file.get_package() == "google.protobuf"
-        && file_last_component(file.get_name()) == "descriptor.proto"
-}
-
-pub(crate) fn type_name_to_rust_relative(
-    type_name: &ProtobufAbsolutePath,
-    file: &FileDescriptorProto,
-    subm: bool,
-    root_scope: &RootScope,
-    customize: &Customize,
-) -> String {
-    let message_or_enum = root_scope.find_message_or_enum(type_name);
-    if message_or_enum.get_scope().get_file_descriptor().get_name() == file.get_name() {
-        // field type is a message or enum declared in the same file
-        if subm {
-            format!("super::{}", message_or_enum.rust_name())
-        } else {
-            format!("{}", message_or_enum.rust_name())
-        }
-    } else if let Some(name) = is_well_known_type_full(&type_name.path) {
-        // Well-known types are included in rust-protobuf library
-        // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
-        format!(
-            "{}::well_known_types::{}",
-            protobuf_crate_path(customize),
-            name
-        )
-    } else if is_descriptor_proto(message_or_enum.get_file_descriptor()) {
-        // Messages defined in descriptor.proto
-        format!(
-            "{}::descriptor::{}",
-            protobuf_crate_path(customize),
-            message_or_enum.name_to_package()
-        )
-    } else {
-        if subm {
-            format!("super::super::{}", message_or_enum.rust_fq_name())
-        } else {
-            format!("super::{}", message_or_enum.rust_fq_name())
-        }
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum PrimitiveTypeVariant {
-    Default,
-    Carllerche,
-}
-
-pub enum _CarllercheBytesType {
-    Bytes,
-    Chars,
-}
-
-// ProtobufType trait name
-pub enum ProtobufTypeGen {
-    Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant),
-    Message(String),
-    Enum(String),
-}
-
-impl ProtobufTypeGen {
-    pub fn rust_type(&self, customize: &Customize) -> String {
-        match self {
-            &ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!(
-                "{}::types::ProtobufType{}",
-                protobuf_crate_path(customize),
-                capitalize(protobuf_name(t))
-            ),
-            &ProtobufTypeGen::Primitive(
-                FieldDescriptorProto_Type::TYPE_BYTES,
-                PrimitiveTypeVariant::Carllerche,
-            ) => format!(
-                "{}::types::ProtobufTypeCarllercheBytes",
-                protobuf_crate_path(customize)
-            ),
-            &ProtobufTypeGen::Primitive(
-                FieldDescriptorProto_Type::TYPE_STRING,
-                PrimitiveTypeVariant::Carllerche,
-            ) => format!(
-                "{}::types::ProtobufTypeCarllercheChars",
-                protobuf_crate_path(customize)
-            ),
-            &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(),
-            &ProtobufTypeGen::Message(ref name) => format!(
-                "{}::types::ProtobufTypeMessage<{}>",
-                protobuf_crate_path(customize),
-                name
-            ),
-            &ProtobufTypeGen::Enum(ref name) => format!(
-                "{}::types::ProtobufTypeEnum<{}>",
-                protobuf_crate_path(customize),
-                name
-            ),
-        }
-    }
-}
diff --git a/src/scope.rs b/src/scope.rs
deleted file mode 100644
index 5f92d08..0000000
--- a/src/scope.rs
+++ /dev/null
@@ -1,555 +0,0 @@
-use protobuf::descriptor::DescriptorProto;
-use protobuf::descriptor::EnumDescriptorProto;
-use protobuf::descriptor::EnumValueDescriptorProto;
-use protobuf::descriptor::FieldDescriptorProto;
-use protobuf::descriptor::FileDescriptorProto;
-use protobuf::descriptor::OneofDescriptorProto;
-
-use crate::field::rust_field_name_for_protobuf_field_name;
-use crate::file::proto_path_to_rust_mod;
-use crate::protobuf_name::ProtobufAbsolutePath;
-use crate::protobuf_name::ProtobufIdent;
-use crate::protobuf_name::ProtobufRelativePath;
-use crate::rust;
-use crate::rust_name::RustIdent;
-use crate::rust_name::RustIdentWithPath;
-use crate::syntax::Syntax;
-
-pub(crate) struct RootScope<'a> {
-    pub file_descriptors: &'a [FileDescriptorProto],
-}
-
-impl<'a> RootScope<'a> {
-    fn packages(&'a self) -> Vec<FileScope<'a>> {
-        self.file_descriptors
-            .iter()
-            .map(|fd| FileScope {
-                file_descriptor: fd,
-            })
-            .collect()
-    }
-
-    // find enum by fully qualified name
-    pub fn _find_enum(&'a self, fqn: &ProtobufAbsolutePath) -> EnumWithScope<'a> {
-        match self.find_message_or_enum(fqn) {
-            MessageOrEnumWithScope::Enum(e) => e,
-            _ => panic!("not an enum: {}", fqn),
-        }
-    }
-
-    // find message by fully qualified name
-    pub fn find_message(&'a self, fqn: &ProtobufAbsolutePath) -> MessageWithScope<'a> {
-        match self.find_message_or_enum(fqn) {
-            MessageOrEnumWithScope::Message(m) => m,
-            _ => panic!("not a message: {}", fqn),
-        }
-    }
-
-    // find message or enum by fully qualified name
-    pub fn find_message_or_enum(
-        &'a self,
-        fqn: &ProtobufAbsolutePath,
-    ) -> MessageOrEnumWithScope<'a> {
-        assert!(!fqn.is_empty());
-        self.packages()
-            .into_iter()
-            .flat_map(|p| p.find_message_or_enum_abs(fqn))
-            .next()
-            .expect(&format!("enum not found by name: {}", fqn))
-    }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct FileScope<'a> {
-    pub file_descriptor: &'a FileDescriptorProto,
-}
-
-impl<'a> FileScope<'a> {
-    fn get_package(&self) -> ProtobufAbsolutePath {
-        ProtobufRelativePath::from(self.file_descriptor.get_package()).into_absolute()
-    }
-
-    pub fn syntax(&self) -> Syntax {
-        Syntax::parse(self.file_descriptor.get_syntax())
-    }
-
-    pub fn to_scope(&self) -> Scope<'a> {
-        Scope {
-            file_scope: self.clone(),
-            path: Vec::new(),
-        }
-    }
-
-    fn find_message_or_enum(
-        &self,
-        name: &ProtobufRelativePath,
-    ) -> Option<MessageOrEnumWithScope<'a>> {
-        self.find_messages_and_enums()
-            .into_iter()
-            .filter(|e| e.protobuf_name_to_package() == *name)
-            .next()
-    }
-
-    fn find_message_or_enum_abs(
-        &self,
-        name: &ProtobufAbsolutePath,
-    ) -> Option<MessageOrEnumWithScope<'a>> {
-        match name.remove_prefix(&self.get_package()) {
-            Some(ref rem) => self.find_message_or_enum(rem),
-            None => None,
-        }
-    }
-
-    // find all enums in given file descriptor
-    pub fn _find_enums(&self) -> Vec<EnumWithScope<'a>> {
-        let mut r = Vec::new();
-
-        self.to_scope().walk_scopes(|scope| {
-            r.extend(scope.get_enums());
-        });
-
-        r
-    }
-
-    // find all messages in given file descriptor
-    pub fn _find_messages(&self) -> Vec<MessageWithScope<'a>> {
-        let mut r = Vec::new();
-
-        self.to_scope().walk_scopes(|scope| {
-            r.extend(scope.get_messages());
-        });
-
-        r
-    }
-
-    // find all messages and enums in given file descriptor
-    pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
-        let mut r = Vec::new();
-
-        self.to_scope().walk_scopes(|scope| {
-            r.extend(scope.get_messages_and_enums());
-        });
-
-        r
-    }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct Scope<'a> {
-    pub file_scope: FileScope<'a>,
-    pub path: Vec<&'a DescriptorProto>,
-}
-
-impl<'a> Scope<'a> {
-    pub fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
-        self.file_scope.file_descriptor
-    }
-
-    // get message descriptors in this scope
-    fn get_message_descriptors(&self) -> &'a [DescriptorProto] {
-        if self.path.is_empty() {
-            &self.file_scope.file_descriptor.get_message_type()
-        } else {
-            &self.path.last().unwrap().get_nested_type()
-        }
-    }
-
-    // get enum descriptors in this scope
-    fn get_enum_descriptors(&self) -> &'a [EnumDescriptorProto] {
-        if self.path.is_empty() {
-            &self.file_scope.file_descriptor.get_enum_type()
-        } else {
-            &self.path.last().unwrap().get_enum_type()
-        }
-    }
-
-    // get messages with attached scopes in this scope
-    pub fn get_messages(&self) -> Vec<MessageWithScope<'a>> {
-        self.get_message_descriptors()
-            .iter()
-            .map(|m| MessageWithScope {
-                scope: self.clone(),
-                message: m,
-            })
-            .collect()
-    }
-
-    // get enums with attached scopes in this scope
-    pub fn get_enums(&self) -> Vec<EnumWithScope<'a>> {
-        self.get_enum_descriptors()
-            .iter()
-            .map(|e| EnumWithScope {
-                scope: self.clone(),
-                en: e,
-            })
-            .collect()
-    }
-
-    // get messages and enums with attached scopes in this scope
-    pub fn get_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
-        self.get_messages()
-            .into_iter()
-            .map(|m| MessageOrEnumWithScope::Message(m))
-            .chain(
-                self.get_enums()
-                    .into_iter()
-                    .map(|m| MessageOrEnumWithScope::Enum(m)),
-            )
-            .collect()
-    }
-
-    // nested scopes, i. e. scopes of nested messages
-    fn nested_scopes(&self) -> Vec<Scope<'a>> {
-        self.get_message_descriptors()
-            .iter()
-            .map(|m| {
-                let mut nested = self.clone();
-                nested.path.push(m);
-                nested
-            })
-            .collect()
-    }
-
-    fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
-        (*callback)(self);
-
-        for nested in self.nested_scopes() {
-            nested.walk_scopes_impl(callback);
-        }
-    }
-
-    // apply callback for this scope and all nested scopes
-    fn walk_scopes<F>(&self, mut callback: F)
-    where
-        F: FnMut(&Scope<'a>),
-    {
-        self.walk_scopes_impl(&mut callback);
-    }
-
-    pub fn prefix(&self) -> String {
-        if self.path.is_empty() {
-            "".to_string()
-        } else {
-            let v: Vec<&'a str> = self.path.iter().map(|m| m.get_name()).collect();
-            let mut r = v.join(".");
-            r.push_str(".");
-            r
-        }
-    }
-
-    pub fn protobuf_path_to_file(&self) -> ProtobufRelativePath {
-        ProtobufRelativePath::from_components(
-            self.path.iter().map(|m| ProtobufIdent::from(m.get_name())),
-        )
-    }
-
-    pub fn protobuf_absolute_path(&self) -> ProtobufAbsolutePath {
-        let mut r = self.file_scope.get_package();
-        r.push_relative(&self.protobuf_path_to_file());
-        r
-    }
-
-    // rust type name prefix for this scope
-    pub fn rust_prefix(&self) -> String {
-        self.prefix().replace(".", "_")
-    }
-}
-
-pub(crate) trait WithScope<'a> {
-    fn get_scope(&self) -> &Scope<'a>;
-
-    fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
-        self.get_scope().get_file_descriptor()
-    }
-
-    // message or enum name
-    fn get_name(&self) -> ProtobufIdent;
-
-    fn escape_prefix(&self) -> &'static str;
-
-    fn name_to_package(&self) -> String {
-        let mut r = self.get_scope().prefix();
-        r.push_str(self.get_name().get());
-        r
-    }
-
-    fn protobuf_name_to_package(&self) -> ProtobufRelativePath {
-        let r = self.get_scope().protobuf_path_to_file();
-        r.append_ident(&ProtobufIdent::from(self.get_name()))
-    }
-
-    /// Return absolute name starting with dot
-    fn name_absolute(&self) -> ProtobufAbsolutePath {
-        let mut path = self.get_scope().protobuf_absolute_path();
-        path.push_simple(self.get_name());
-        path
-    }
-
-    // rust type name of this descriptor
-    fn rust_name(&self) -> RustIdent {
-        let mut r = self.get_scope().rust_prefix();
-        // Only escape if prefix is not empty
-        if r.is_empty() && rust::is_rust_keyword(self.get_name().get()) {
-            r.push_str(self.escape_prefix());
-        }
-        r.push_str(self.get_name().get());
-        RustIdent::from(r)
-    }
-
-    // fully-qualified name of this type
-    fn rust_fq_name(&self) -> String {
-        format!(
-            "{}::{}",
-            proto_path_to_rust_mod(self.get_scope().get_file_descriptor().get_name()),
-            self.rust_name()
-        )
-    }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct MessageWithScope<'a> {
-    pub scope: Scope<'a>,
-    pub message: &'a DescriptorProto,
-}
-
-impl<'a> WithScope<'a> for MessageWithScope<'a> {
-    fn get_scope(&self) -> &Scope<'a> {
-        &self.scope
-    }
-
-    fn escape_prefix(&self) -> &'static str {
-        "message_"
-    }
-
-    fn get_name(&self) -> ProtobufIdent {
-        ProtobufIdent::from(self.message.get_name())
-    }
-}
-
-impl<'a> MessageWithScope<'a> {
-    pub fn into_scope(mut self) -> Scope<'a> {
-        self.scope.path.push(self.message);
-        self.scope
-    }
-
-    pub fn to_scope(&self) -> Scope<'a> {
-        self.clone().into_scope()
-    }
-
-    pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
-        self.message
-            .get_field()
-            .iter()
-            .map(|f| FieldWithContext {
-                field: f,
-                message: self.clone(),
-            })
-            .collect()
-    }
-
-    pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
-        self.message
-            .get_oneof_decl()
-            .iter()
-            .enumerate()
-            .map(|(index, oneof)| OneofWithContext {
-                message: self.clone(),
-                oneof: oneof,
-                index: index as u32,
-            })
-            .collect()
-    }
-
-    pub fn oneof_by_index(&self, index: u32) -> OneofWithContext<'a> {
-        self.oneofs().swap_remove(index as usize)
-    }
-
-    /// Pair of (key, value) if this message is map entry
-    pub fn map_entry(&'a self) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> {
-        if self.message.get_options().get_map_entry() {
-            let key = self
-                .fields()
-                .into_iter()
-                .find(|f| f.field.get_number() == 1)
-                .unwrap();
-            let value = self
-                .fields()
-                .into_iter()
-                .find(|f| f.field.get_number() == 2)
-                .unwrap();
-            Some((key, value))
-        } else {
-            None
-        }
-    }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct EnumWithScope<'a> {
-    pub scope: Scope<'a>,
-    pub en: &'a EnumDescriptorProto,
-}
-
-impl<'a> EnumWithScope<'a> {
-    pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
-        self.en
-            .get_value()
-            .iter()
-            .map(|v| EnumValueWithContext {
-                _en: self.clone(),
-                proto: v,
-            })
-            .collect()
-    }
-
-    // find enum value by protobuf name
-    pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
-        self.values()
-            .into_iter()
-            .find(|v| v.proto.get_name() == name)
-            .unwrap()
-    }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct EnumValueWithContext<'a> {
-    _en: EnumWithScope<'a>,
-    pub proto: &'a EnumValueDescriptorProto,
-}
-
-impl<'a> EnumValueWithContext<'a> {
-    pub fn rust_name(&self) -> RustIdent {
-        let mut r = String::new();
-        if rust::is_rust_keyword(self.proto.get_name()) {
-            r.push_str("value_");
-        }
-        r.push_str(self.proto.get_name());
-        RustIdent::new(&r)
-    }
-}
-
-impl<'a> WithScope<'a> for EnumWithScope<'a> {
-    fn get_scope(&self) -> &Scope<'a> {
-        &self.scope
-    }
-
-    fn escape_prefix(&self) -> &'static str {
-        "enum_"
-    }
-
-    fn get_name(&self) -> ProtobufIdent {
-        ProtobufIdent::from(self.en.get_name())
-    }
-}
-
-pub(crate) enum MessageOrEnumWithScope<'a> {
-    Message(MessageWithScope<'a>),
-    Enum(EnumWithScope<'a>),
-}
-
-impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
-    fn get_scope(&self) -> &Scope<'a> {
-        match self {
-            &MessageOrEnumWithScope::Message(ref m) => m.get_scope(),
-            &MessageOrEnumWithScope::Enum(ref e) => e.get_scope(),
-        }
-    }
-
-    fn escape_prefix(&self) -> &'static str {
-        match self {
-            &MessageOrEnumWithScope::Message(ref m) => m.escape_prefix(),
-            &MessageOrEnumWithScope::Enum(ref e) => e.escape_prefix(),
-        }
-    }
-
-    fn get_name(&self) -> ProtobufIdent {
-        match self {
-            &MessageOrEnumWithScope::Message(ref m) => m.get_name(),
-            &MessageOrEnumWithScope::Enum(ref e) => e.get_name(),
-        }
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct FieldWithContext<'a> {
-    pub field: &'a FieldDescriptorProto,
-    pub message: MessageWithScope<'a>,
-}
-
-impl<'a> FieldWithContext<'a> {
-    pub fn is_oneof(&self) -> bool {
-        self.field.has_oneof_index()
-    }
-
-    pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
-        if self.is_oneof() {
-            Some(
-                self.message
-                    .oneof_by_index(self.field.get_oneof_index() as u32),
-            )
-        } else {
-            None
-        }
-    }
-
-    pub fn number(&self) -> u32 {
-        self.field.get_number() as u32
-    }
-
-    /// Shortcut
-    pub fn name(&self) -> &str {
-        self.field.get_name()
-    }
-
-    pub fn rust_name(&self) -> RustIdent {
-        rust_field_name_for_protobuf_field_name(self.name())
-    }
-
-    // From field to file root
-    pub fn _containing_messages(&self) -> Vec<&'a DescriptorProto> {
-        let mut r = Vec::new();
-        r.push(self.message.message);
-        r.extend(self.message.scope.path.iter().rev());
-        r
-    }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofVariantWithContext<'a> {
-    pub oneof: &'a OneofWithContext<'a>,
-    pub field: &'a FieldDescriptorProto,
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofWithContext<'a> {
-    pub oneof: &'a OneofDescriptorProto,
-    pub index: u32,
-    pub message: MessageWithScope<'a>,
-}
-
-impl<'a> OneofWithContext<'a> {
-    pub fn field_name(&'a self) -> RustIdent {
-        return rust_field_name_for_protobuf_field_name(self.oneof.get_name());
-    }
-
-    // rust type name of enum
-    pub fn rust_name(&self) -> RustIdentWithPath {
-        RustIdentWithPath::from(format!(
-            "{}_oneof_{}",
-            self.message.rust_name(),
-            self.oneof.get_name()
-        ))
-    }
-
-    pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
-        self.message
-            .fields()
-            .iter()
-            .filter(|f| f.field.has_oneof_index() && f.field.get_oneof_index() == self.index as i32)
-            .map(|f| OneofVariantWithContext {
-                oneof: self,
-                field: &f.field,
-            })
-            .collect()
-    }
-}
diff --git a/src/serde.rs b/src/serde.rs
deleted file mode 100644
index f799611..0000000
--- a/src/serde.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use code_writer::CodeWriter;
-use Customize;
-
-/// Write serde attr according to specified codegen option.
-pub fn write_serde_attr(w: &mut CodeWriter, customize: &Customize, attr: &str) {
-    if customize.serde_derive.unwrap_or(false) {
-        w.write_line(&format!("#[cfg_attr(feature = \"with-serde\", {})]", attr));
-    }
-}
diff --git a/src/syntax.rs b/src/syntax.rs
deleted file mode 100644
index d075063..0000000
--- a/src/syntax.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum Syntax {
-    PROTO2,
-    PROTO3,
-}
-
-impl Syntax {
-    pub fn parse(s: &str) -> Self {
-        match s {
-            "" | "proto2" => Syntax::PROTO2,
-            "proto3" => Syntax::PROTO3,
-            _ => panic!("unsupported syntax value: {:?}", s),
-        }
-    }
-}
diff --git a/src/well_known_types.rs b/src/well_known_types.rs
deleted file mode 100644
index 6264947..0000000
--- a/src/well_known_types.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-static NAMES: &'static [&'static str] = &[
-    "Any",
-    "Api",
-    "BoolValue",
-    "BytesValue",
-    "DoubleValue",
-    "Duration",
-    "Empty",
-    "Enum",
-    "EnumValue",
-    "Field",
-    // TODO: dotted names
-    "Field.Cardinality",
-    "Field.Kind",
-    "FieldMask",
-    "FloatValue",
-    "Int32Value",
-    "Int64Value",
-    "ListValue",
-    "Method",
-    "Mixin",
-    "NullValue",
-    "Option",
-    "SourceContext",
-    "StringValue",
-    "Struct",
-    "Syntax",
-    "Timestamp",
-    "Type",
-    "UInt32Value",
-    "UInt64Value",
-    "Value",
-];
-
-fn is_well_known_type(name: &str) -> bool {
-    NAMES.iter().any(|&n| n == name)
-}
-
-pub fn is_well_known_type_full(name: &str) -> Option<&str> {
-    if let Some(dot) = name.rfind('.') {
-        if &name[..dot] == ".google.protobuf" && is_well_known_type(&name[dot + 1..]) {
-            Some(&name[dot + 1..])
-        } else {
-            None
-        }
-    } else {
-        None
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_is_well_known_type_full() {
-        assert_eq!(
-            Some("BoolValue"),
-            is_well_known_type_full(".google.protobuf.BoolValue")
-        );
-        assert_eq!(None, is_well_known_type_full(".google.protobuf.Fgfg"));
-    }
-}