Snap for 10447354 from 69b7c53a2fe3e60b8cc390405fe3abc800b6f6ce to mainline-wifi-release

Change-Id: I1e6419c250b81782952a574756f98ac6df34f184
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/2.27.1/.cargo_vcs_info.json b/2.27.1/.cargo_vcs_info.json
new file mode 100644
index 0000000..8ec5394
--- /dev/null
+++ b/2.27.1/.cargo_vcs_info.json
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "ec31ce829473039ac598ca6fdcb245cbd6fa82ba"
+  },
+  "path_in_vcs": "protobuf-codegen"
+}
\ No newline at end of file
diff --git a/2.27.1/Android.bp b/2.27.1/Android.bp
new file mode 100644
index 0000000..e705500
--- /dev/null
+++ b/2.27.1/Android.bp
@@ -0,0 +1,67 @@
+// This file is generated by cargo2android.py --config cargo2android.json.
+// Do not modify this file as changes will be overridden on upgrade.
+
+package {
+    default_applicable_licenses: [
+        "external_rust_crates_protobuf-codegen_2.27.1_license",
+    ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "external_rust_crates_protobuf-codegen_2.27.1_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-MIT",
+    ],
+    license_text: [
+        "LICENSE.txt",
+    ],
+}
+
+rust_library_host {
+    name: "libprotobuf_codegen_deprecated",
+    crate_name: "protobuf_codegen",
+    cargo_env_compat: true,
+    cargo_pkg_version: "2.27.1",
+    srcs: ["src/lib.rs"],
+    edition: "2015",
+    rustlibs: [
+        "libprotobuf_deprecated",
+    ],
+    product_available: true,
+    vendor_available: true,
+}
+
+rust_test_host {
+    name: "protobuf-codegen_deprecated_test_src_lib",
+    crate_name: "protobuf_codegen",
+    cargo_env_compat: true,
+    cargo_pkg_version: "2.27.1",
+    srcs: ["src/lib.rs"],
+    test_suites: ["general-tests"],
+    auto_gen_config: true,
+    test_options: {
+        unit_test: true,
+    },
+    edition: "2015",
+    rustlibs: [
+        "libprotobuf_deprecated",
+    ],
+}
+
+rust_binary_host {
+    name: "protoc-gen-rust-deprecated",
+    crate_name: "protoc_gen_rust",
+    cargo_env_compat: true,
+    cargo_pkg_version: "2.27.1",
+    srcs: ["src/bin/protoc-gen-rust.rs"],
+    edition: "2015",
+    rustlibs: [
+        "libprotobuf_deprecated",
+        "libprotobuf_codegen_deprecated",
+    ],
+    product_available: true,
+    vendor_available: true,
+}
diff --git a/2.27.1/Cargo.lock b/2.27.1/Cargo.lock
new file mode 100644
index 0000000..e9b0703
--- /dev/null
+++ b/2.27.1/Cargo.lock
@@ -0,0 +1,16 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "protobuf"
+version = "2.27.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96"
+
+[[package]]
+name = "protobuf-codegen"
+version = "2.27.1"
+dependencies = [
+ "protobuf",
+]
diff --git a/2.27.1/Cargo.toml b/2.27.1/Cargo.toml
new file mode 100644
index 0000000..ca327e2
--- /dev/null
+++ b/2.27.1/Cargo.toml
@@ -0,0 +1,44 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+name = "protobuf-codegen"
+version = "2.27.1"
+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.
+"""
+homepage = "https://github.com/stepancheg/rust-protobuf/"
+license = "MIT"
+repository = "https://github.com/stepancheg/rust-protobuf/"
+
+[package.metadata.docs.rs]
+all-features = true
+
+[lib]
+bench = false
+
+[[bin]]
+name = "protoc-gen-rust"
+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.protobuf]
+version = "=2.27.1"
diff --git a/2.27.1/Cargo.toml.orig b/2.27.1/Cargo.toml.orig
new file mode 100644
index 0000000..3400e66
--- /dev/null
+++ b/2.27.1/Cargo.toml.orig
@@ -0,0 +1,35 @@
+[package]
+name = "protobuf-codegen"
+version = "2.27.1"
+authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"]
+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.
+"""
+
+[lib]
+bench = false
+
+[dependencies]
+protobuf = { path = "../protobuf", version = "=2.27.1" }
+
+[[bin]]
+
+name = "protoc-gen-rust"
+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/2.27.1/LICENSE b/2.27.1/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/2.27.1/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/2.27.1/LICENSE.txt b/2.27.1/LICENSE.txt
new file mode 100644
index 0000000..acce639
--- /dev/null
+++ b/2.27.1/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2019 Stepan Koltsov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/2.27.1/METADATA b/2.27.1/METADATA
new file mode 100644
index 0000000..4593845
--- /dev/null
+++ b/2.27.1/METADATA
@@ -0,0 +1,19 @@
+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 {
+  url {
+    type: HOMEPAGE
+    value: "https://crates.io/crates/protobuf-codegen"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-2.27.1.crate"
+  }
+  version: "2.27.1"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2022
+    month: 3
+    day: 1
+  }
+}
diff --git a/2.27.1/MODULE_LICENSE_MIT b/2.27.1/MODULE_LICENSE_MIT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/2.27.1/MODULE_LICENSE_MIT
diff --git a/2.27.1/NOTICE b/2.27.1/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/2.27.1/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/2.27.1/OWNERS b/2.27.1/OWNERS
new file mode 100644
index 0000000..46fc303
--- /dev/null
+++ b/2.27.1/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:/OWNERS
diff --git a/2.27.1/README.md b/2.27.1/README.md
new file mode 100644
index 0000000..2ab7e05
--- /dev/null
+++ b/2.27.1/README.md
@@ -0,0 +1,80 @@
+<!-- cargo-sync-readme start -->
+
+# Protobuf code generator
+
+This crate contains protobuf code generator implementation
+and a `protoc-gen-rust` `protoc` plugin.
+
+This crate:
+* provides `protoc-gen-rust` plugin for `protoc` command
+* implement protobuf codegen
+
+This crate is not meant to be used directly, in fact, it does not provide any public API
+(except for `protoc-gen-rust` binary).
+
+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.
+
+# `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.
+
+When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+
+## How to use `protoc-gen-rust` if you have to
+
+(Note `protoc` can be invoked programmatically with
+[protoc crate](https://docs.rs/protoc))
+
+0) Install protobuf for `protoc` binary.
+
+On OS X [Homebrew](https://github.com/Homebrew/brew) can be used:
+
+```sh
+brew install protobuf
+```
+
+On Ubuntu, `protobuf-compiler` package can be installed:
+
+```sh
+apt-get install protobuf-compiler
+```
+
+Protobuf is needed only for code generation, `rust-protobuf` runtime
+does not use `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.
+
+2) Add `protoc-gen-rust` to $PATH
+
+If you installed it with cargo, it should be
+
+```sh
+PATH="$HOME/.cargo/bin:$PATH"
+```
+
+3) Generate .rs files:
+
+```sh
+protoc --rust_out . foo.proto
+```
+
+This will generate .rs files in current directory.
+
+# Version 2
+
+This is documentation for version 2 of the crate.
+
+[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.
+
+<!-- cargo-sync-readme end -->
diff --git a/2.27.1/cargo2android.json b/2.27.1/cargo2android.json
new file mode 100644
index 0000000..341300b
--- /dev/null
+++ b/2.27.1/cargo2android.json
@@ -0,0 +1,4 @@
+{
+  "run": true,
+  "tests": true
+}
\ No newline at end of file
diff --git a/src/bin/protobuf-bin-gen-rust-do-not-use.rs b/2.27.1/src/bin/protobuf-bin-gen-rust-do-not-use.rs
similarity index 100%
rename from src/bin/protobuf-bin-gen-rust-do-not-use.rs
rename to 2.27.1/src/bin/protobuf-bin-gen-rust-do-not-use.rs
diff --git a/2.27.1/src/bin/protoc-gen-rust.rs b/2.27.1/src/bin/protoc-gen-rust.rs
new file mode 100644
index 0000000..97ac7d2
--- /dev/null
+++ b/2.27.1/src/bin/protoc-gen-rust.rs
@@ -0,0 +1,5 @@
+extern crate protobuf_codegen;
+
+fn main() {
+    protobuf_codegen::protoc_gen_rust_main();
+}
diff --git a/src/code_writer.rs b/2.27.1/src/code_writer.rs
similarity index 100%
rename from src/code_writer.rs
rename to 2.27.1/src/code_writer.rs
diff --git a/src/customize.rs b/2.27.1/src/customize.rs
similarity index 100%
rename from src/customize.rs
rename to 2.27.1/src/customize.rs
diff --git a/src/enums.rs b/2.27.1/src/enums.rs
similarity index 100%
rename from src/enums.rs
rename to 2.27.1/src/enums.rs
diff --git a/src/extensions.rs b/2.27.1/src/extensions.rs
similarity index 100%
rename from src/extensions.rs
rename to 2.27.1/src/extensions.rs
diff --git a/src/field/mod.rs b/2.27.1/src/field/mod.rs
similarity index 100%
rename from src/field/mod.rs
rename to 2.27.1/src/field/mod.rs
diff --git a/src/file.rs b/2.27.1/src/file.rs
similarity index 100%
rename from src/file.rs
rename to 2.27.1/src/file.rs
diff --git a/src/file_and_mod.rs b/2.27.1/src/file_and_mod.rs
similarity index 100%
rename from src/file_and_mod.rs
rename to 2.27.1/src/file_and_mod.rs
diff --git a/src/file_descriptor.rs b/2.27.1/src/file_descriptor.rs
similarity index 100%
rename from src/file_descriptor.rs
rename to 2.27.1/src/file_descriptor.rs
diff --git a/src/float.rs b/2.27.1/src/float.rs
similarity index 100%
rename from src/float.rs
rename to 2.27.1/src/float.rs
diff --git a/src/inside.rs b/2.27.1/src/inside.rs
similarity index 100%
rename from src/inside.rs
rename to 2.27.1/src/inside.rs
diff --git a/2.27.1/src/lib.rs b/2.27.1/src/lib.rs
new file mode 100644
index 0000000..826e18b
--- /dev/null
+++ b/2.27.1/src/lib.rs
@@ -0,0 +1,387 @@
+//! # Protobuf code generator
+//!
+//! This crate contains protobuf code generator implementation
+//! and a `protoc-gen-rust` `protoc` plugin.
+//!
+//! This crate:
+//! * provides `protoc-gen-rust` plugin for `protoc` command
+//! * implement protobuf codegen
+//!
+//! This crate is not meant to be used directly, in fact, it does not provide any public API
+//! (except for `protoc-gen-rust` binary).
+//!
+//! 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.
+//!
+//! # `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.
+//!
+//! When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+//!
+//! ## How to use `protoc-gen-rust` if you have to
+//!
+//! (Note `protoc` can be invoked programmatically with
+//! [protoc crate](https://docs.rs/protoc))
+//!
+//! 0) Install protobuf for `protoc` binary.
+//!
+//! On OS X [Homebrew](https://github.com/Homebrew/brew) can be used:
+//!
+//! ```sh
+//! brew install protobuf
+//! ```
+//!
+//! On Ubuntu, `protobuf-compiler` package can be installed:
+//!
+//! ```sh
+//! apt-get install protobuf-compiler
+//! ```
+//!
+//! Protobuf is needed only for code generation, `rust-protobuf` runtime
+//! does not use `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.
+//!
+//! 2) Add `protoc-gen-rust` to $PATH
+//!
+//! If you installed it with cargo, it should be
+//!
+//! ```sh
+//! PATH="$HOME/.cargo/bin:$PATH"
+//! ```
+//!
+//! 3) Generate .rs files:
+//!
+//! ```sh
+//! protoc --rust_out . foo.proto
+//! ```
+//!
+//! This will generate .rs files in current directory.
+//!
+//! # Version 2
+//!
+//! This is documentation for version 2 of the crate.
+//!
+//! [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.
+
+#![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 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;
+
+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 customize::Customize;
+
+pub mod code_writer;
+
+use inside::protobuf_crate_path;
+#[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))
+}
diff --git a/src/message.rs b/2.27.1/src/message.rs
similarity index 100%
rename from src/message.rs
rename to 2.27.1/src/message.rs
diff --git a/src/oneof.rs b/2.27.1/src/oneof.rs
similarity index 100%
rename from src/oneof.rs
rename to 2.27.1/src/oneof.rs
diff --git a/src/protobuf_name.rs b/2.27.1/src/protobuf_name.rs
similarity index 100%
rename from src/protobuf_name.rs
rename to 2.27.1/src/protobuf_name.rs
diff --git a/src/rust.rs b/2.27.1/src/rust.rs
similarity index 100%
rename from src/rust.rs
rename to 2.27.1/src/rust.rs
diff --git a/src/rust_name.rs b/2.27.1/src/rust_name.rs
similarity index 100%
rename from src/rust_name.rs
rename to 2.27.1/src/rust_name.rs
diff --git a/src/rust_types_values.rs b/2.27.1/src/rust_types_values.rs
similarity index 100%
rename from src/rust_types_values.rs
rename to 2.27.1/src/rust_types_values.rs
diff --git a/src/scope.rs b/2.27.1/src/scope.rs
similarity index 100%
rename from src/scope.rs
rename to 2.27.1/src/scope.rs
diff --git a/src/serde.rs b/2.27.1/src/serde.rs
similarity index 100%
rename from src/serde.rs
rename to 2.27.1/src/serde.rs
diff --git a/src/strx.rs b/2.27.1/src/strx.rs
similarity index 100%
copy from src/strx.rs
copy to 2.27.1/src/strx.rs
diff --git a/src/syntax.rs b/2.27.1/src/syntax.rs
similarity index 100%
rename from src/syntax.rs
rename to 2.27.1/src/syntax.rs
diff --git a/src/well_known_types.rs b/2.27.1/src/well_known_types.rs
similarity index 100%
rename from src/well_known_types.rs
rename to 2.27.1/src/well_known_types.rs
diff --git a/Android.bp b/Android.bp
index dd251e9..b0ae081 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,28 +24,42 @@
     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,
 }
 
 rust_test_host {
     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",
     ],
 }
 
@@ -53,11 +67,19 @@
     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/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/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/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/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/gen/file_and_mod.rs b/src/gen/file_and_mod.rs
new file mode 100644
index 0000000..8862c53
--- /dev/null
+++ b/src/gen/file_and_mod.rs
@@ -0,0 +1,8 @@
+use crate::customize::Customize;
+use crate::gen::rust::rel_path::RustRelativePath;
+
+pub(crate) struct FileAndMod {
+    pub file: String,
+    pub relative_mod: RustRelativePath,
+    pub customize: Customize,
+}
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/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/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");
+}