├── golang-parser ├── LICENSE-MIT ├── LICENSE-APACHE ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── golang-struct-tag ├── LICENSE-MIT ├── LICENSE-APACHE ├── src │ ├── convention_struct_tag_parser.rs │ ├── grammars │ │ └── convention_struct_tag.pest │ ├── json.rs │ └── lib.rs ├── tests │ ├── files │ │ ├── json.txt │ │ ├── json_gen.sh │ │ └── json.go │ └── json.rs ├── README.md └── Cargo.toml ├── golang-type ├── golang-type │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ └── tests │ │ └── gen_type.rs ├── golang-type-core │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── tests │ │ ├── files │ │ │ ├── slice_type.txt │ │ │ ├── struct_type │ │ │ │ ├── tag.txt │ │ │ │ ├── normal.txt │ │ │ │ ├── tag.go │ │ │ │ ├── normal.go │ │ │ │ ├── embedded_field.txt │ │ │ │ ├── embedded_field.go │ │ │ │ └── gen.sh │ │ │ ├── array_type.txt │ │ │ ├── map_type.txt │ │ │ ├── slice_type.go │ │ │ ├── array_type.go │ │ │ ├── map_type.go │ │ │ ├── map_type_gen.sh │ │ │ ├── array_type_gen.sh │ │ │ └── slice_type_gen.sh │ │ ├── parenthesized_type.rs │ │ ├── slice_type.rs │ │ ├── map_type.rs │ │ ├── array_type.rs │ │ └── struct_type.rs │ ├── src │ │ ├── channel_type.rs │ │ ├── function_type.rs │ │ ├── interface_type.rs │ │ ├── pointer_type.rs │ │ ├── parenthesized_type.rs │ │ ├── slice_type.rs │ │ ├── map_type.rs │ │ ├── array_type.rs │ │ ├── lib.rs │ │ └── struct_type.rs │ └── Cargo.toml ├── golang-type-macro │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── README.md ├── golang-var-decl ├── golang-var-decl │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml ├── golang-var-decl-core │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml ├── golang-var-decl-macro │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml └── README.md ├── golang-const-decl ├── golang-const-decl │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml ├── golang-const-decl-core │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml ├── golang-const-decl-macro │ ├── src │ │ └── lib.rs │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ └── Cargo.toml └── README.md ├── golang-type-decl ├── golang-type-decl │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── tests │ │ ├── files │ │ │ └── simple.go │ │ ├── gen_type_alias_from_file.rs │ │ ├── gen_type_alias.rs │ │ ├── gen_json_struct_from_file.rs │ │ └── gen_json_struct.rs │ ├── src │ │ ├── lib.rs │ │ ├── gen_type_alias.rs │ │ └── gen_json_struct.rs │ └── Cargo.toml ├── golang-type-decl-core │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── tests │ │ ├── files │ │ │ └── simple.go │ │ ├── alias_decl.rs │ │ └── type_def.rs │ ├── Cargo.toml │ └── src │ │ ├── alias_decl │ │ └── type_alias.rs │ │ ├── type_def.rs │ │ ├── alias_decl.rs │ │ ├── lib.rs │ │ └── type_def │ │ └── json_struct.rs ├── golang-type-decl-macro │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── src │ │ ├── lib.rs │ │ ├── gen_json_struct │ │ │ ├── field_types.rs │ │ │ ├── field_opts.rs │ │ │ ├── mod.rs │ │ │ └── input.rs │ │ ├── gen_type_alias │ │ │ ├── mod.rs │ │ │ └── input.rs │ │ └── utils.rs │ └── Cargo.toml └── README.md ├── golang-type-name ├── golang-type-name │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── src │ │ └── lib.rs │ ├── Cargo.toml │ └── tests │ │ └── gen_type_name.rs ├── golang-type-name-core │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── tests │ │ └── files │ │ │ ├── type_names.txt │ │ │ ├── type_names_gen.sh │ │ │ └── type_names.go │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── golang-type-name-macro │ ├── README.md │ ├── LICENSE-MIT │ ├── LICENSE-APACHE │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── README.md ├── .gitignore ├── README.md ├── Cargo.toml ├── LICENSE-MIT └── LICENSE-APACHE /golang-parser/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /golang-struct-tag/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /golang-parser/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type/golang-type/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-struct-tag/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type/golang-type-core/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type/golang-type-macro/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type/golang-type/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type/golang-type-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type/golang-type-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type/golang-type/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-core/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-core/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-macro/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-macro/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type/golang-type-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-macro/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/slice_type.txt: -------------------------------------------------------------------------------- 1 | []int 2 | -------------------------------------------------------------------------------- /golang-type/golang-type-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .vscode 4 | .idea 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-core/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../../LICENSE-MIT -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-core/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../../LICENSE-APACHE -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/tests/files/simple.go: -------------------------------------------------------------------------------- 1 | ../../../golang-type-decl-core/tests/files/simple.go -------------------------------------------------------------------------------- /golang-parser/README.md: -------------------------------------------------------------------------------- 1 | # golang-parser 2 | 3 | * [Cargo package](https://crates.io/crates/golang-parser) 4 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/tag.txt: -------------------------------------------------------------------------------- 1 | struct { 2 | microsec uint64 `protobuf:"1"` 3 | serverIP6 uint64 `protobuf:"2"` 4 | } 5 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/array_type.txt: -------------------------------------------------------------------------------- 1 | [32]byte 2 | [2 * N]struct{ x, y int32 } 3 | [1000]*float64 4 | [3][5]int 5 | [2][2][2]float64 6 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/map_type.txt: -------------------------------------------------------------------------------- 1 | map[string]int 2 | map[*T]struct{ x, y float64 } 3 | map[string]interface{} 4 | map[string][]string 5 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/slice_type.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // https://golang.org/ref/spec#Slice_types 5 | var _ []int 6 | } 7 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/normal.txt: -------------------------------------------------------------------------------- 1 | struct { 2 | x, y int 3 | u float32 4 | _ float32 // padding 5 | A *[]int 6 | F func() 7 | } 8 | -------------------------------------------------------------------------------- /golang-type/README.md: -------------------------------------------------------------------------------- 1 | # golang-type 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#Type) 4 | * [Cargo package](https://crates.io/crates/golang-type) 5 | -------------------------------------------------------------------------------- /golang-var-decl/README.md: -------------------------------------------------------------------------------- 1 | # golang-var-decl 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#VarDecl) 4 | * [Cargo package](https://crates.io/crates/golang-var-decl) 5 | -------------------------------------------------------------------------------- /golang-type-decl/README.md: -------------------------------------------------------------------------------- 1 | # golang-type-decl 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#TypeDecl) 4 | * [Cargo package](https://crates.io/crates/golang-type-decl) 5 | -------------------------------------------------------------------------------- /golang-type-name/README.md: -------------------------------------------------------------------------------- 1 | # golang-type-name 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#TypeName) 4 | * [Cargo package](https://crates.io/crates/golang-type-name) 5 | -------------------------------------------------------------------------------- /golang-const-decl/README.md: -------------------------------------------------------------------------------- 1 | # golang-const-decl 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#ConstDecl) 4 | * [Cargo package](https://crates.io/crates/golang-const-decl) 5 | -------------------------------------------------------------------------------- /golang-struct-tag/src/convention_struct_tag_parser.rs: -------------------------------------------------------------------------------- 1 | use pest_derive::Parser; 2 | 3 | #[derive(Parser)] 4 | #[grammar = "grammars/convention_struct_tag.pest"] 5 | pub(crate) struct ConventionStructTagParser; 6 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use golang_type_decl_core::*; 2 | pub use golang_type_decl_macro; 3 | 4 | mod gen_json_struct; 5 | mod gen_type_alias; 6 | 7 | pub use gen_json_struct::*; 8 | pub use gen_type_alias::*; 9 | -------------------------------------------------------------------------------- /golang-type/golang-type/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use golang_type_core::*; 2 | pub use golang_type_macro; 3 | 4 | #[macro_export] 5 | macro_rules! gen_type { 6 | ($type_lit:literal) => { 7 | golang_type_macro::gen_type!($type_lit) 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/tag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // https://golang.org/ref/spec#StructType 5 | var _ struct { 6 | microsec uint64 `protobuf:"1"` 7 | serverIP6 uint64 `protobuf:"2"` 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/normal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | // https://golang.org/ref/spec#StructType 5 | var _ struct { 6 | x, y int 7 | u float32 8 | _ float32 // padding 9 | A *[]int 10 | F func() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use golang_type_name_core::*; 2 | pub use golang_type_name_macro; 3 | 4 | #[macro_export] 5 | macro_rules! gen_type_name { 6 | ($type_name_lit:literal) => { 7 | golang_type_name_macro::gen_type_name!($type_name_lit) 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/embedded_field.txt: -------------------------------------------------------------------------------- 1 | struct { 2 | T1 // field name is T1 3 | *T2 // field name is T2 4 | P.Duration // field name is Duration 5 | *P.Month // field name is Month 6 | x, y int // field names are x and y 7 | } 8 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/tests/files/type_names.txt: -------------------------------------------------------------------------------- 1 | bool 2 | uint8 3 | uint16 4 | uint32 5 | uint64 6 | int8 7 | int16 8 | int32 9 | int64 10 | float32 11 | float64 12 | complex64 13 | complex128 14 | byte 15 | rune 16 | uint 17 | int 18 | uintptr 19 | string 20 | time.Duration 21 | foo 22 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/array_type.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const N = 1 4 | 5 | func main() { 6 | // https://golang.org/ref/spec#ArrayType 7 | var _ [32]byte 8 | var _ [2 * N]struct{ x, y int32 } 9 | var _ [1000]*float64 10 | var _ [3][5]int 11 | var _ [2][2][2]float64 12 | } 13 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/map_type.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type T int 4 | 5 | func main() { 6 | // https://golang.org/ref/spec#MapType 7 | var _ map[string]int 8 | var _ map[*T]struct{ x, y float64 } 9 | var _ map[string]interface{} 10 | 11 | // 12 | var _ map[string][]string 13 | } 14 | -------------------------------------------------------------------------------- /golang-struct-tag/tests/files/json.txt: -------------------------------------------------------------------------------- 1 | A `json:""` 2 | B `json:"-"` 3 | C `json:"-,"` 4 | D `json:"d"` 5 | E `json:",omitempty"` 6 | F `json:",string"` 7 | G `json:"g,omitempty"` 8 | H `json:"h,string"` 9 | I `json:"i,omitempty,string"` 10 | J `json:"j,string,omitempty"` 11 | K `json:"k,foo,bar"` 12 | L `json:"l" xml:""` 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Dev 2 | 3 | ``` 4 | cargo clippy --all-features -- -D clippy::all 5 | cargo +nightly clippy --all-features -- -D clippy::all 6 | 7 | cargo fmt -- --check 8 | 9 | cargo build-all-features 10 | cargo test-all-features -- --nocapture 11 | ``` 12 | 13 | ## Examples 14 | 15 | [bk-rs/consul-rs](https://github.com/bk-rs/consul-rs) 16 | -------------------------------------------------------------------------------- /golang-struct-tag/README.md: -------------------------------------------------------------------------------- 1 | # golang-struct-tag 2 | 3 | * [The Go Programming Language Specification](https://golang.org/ref/spec#Tag) 4 | * [Package reflect](https://golang.org/pkg/reflect/#StructTag) 5 | * [Well-known struct tags](https://github.com/golang/go/wiki/Well-known-struct-tags) 6 | * [Cargo package](https://crates.io/crates/golang-struct-tag) 7 | -------------------------------------------------------------------------------- /golang-struct-tag/tests/files/json_gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | # ./tests/files/json_gen.sh 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | go run "${script_path_root}json.go" 11 | 12 | cat "${script_path_root}json.go" | sed -n '/^type S struct {$/,/^}$/p' | sed '1d; $d; s/^[ \t]//g; s/[ \t]$//g; s/ int /\t/; /^$/d;' > "${script_path_root}json.txt" 13 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/parenthesized_type.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | 3 | use golang_type_core::{golang_type_name_core::TypeName, ParenthesizedType, Type}; 4 | 5 | #[test] 6 | fn test_parse() -> Result<(), Box> { 7 | assert_eq!( 8 | Type::ParenthesizedType(ParenthesizedType(Type::TypeName(TypeName::Int).into()).into()), 9 | "(int)".parse()? 10 | ); 11 | 12 | Ok(()) 13 | } 14 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/map_type_gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ./tests/files/map_type_gen.sh 4 | 5 | set -ex 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | go run "${script_path_root}map_type.go" 11 | 12 | cat "${script_path_root}map_type.go" | sed -n '/^[ \t]*var _ /p' | sed 's/^[ \t]//g; s/[ \t]$//g; s/^[ \t]*var _ //; /^$/d;' > "${script_path_root}map_type.txt" 13 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/array_type_gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ./tests/files/array_type_gen.sh 4 | 5 | set -ex 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | go run "${script_path_root}array_type.go" 11 | 12 | cat "${script_path_root}array_type.go" | sed -n '/^[ \t]*var _ /p' | sed 's/^[ \t]//g; s/[ \t]$//g; s/^[ \t]*var _ //; /^$/d;' > "${script_path_root}array_type.txt" 13 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/slice_type_gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ./tests/files/slice_type_gen.sh 4 | 5 | set -ex 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | go run "${script_path_root}slice_type.go" 11 | 12 | cat "${script_path_root}slice_type.go" | sed -n '/^[ \t]*var _ /p' | sed 's/^[ \t]//g; s/[ \t]$//g; s/^[ \t]*var _ //; /^$/d;' > "${script_path_root}slice_type.txt" 13 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/tests/files/type_names_gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ./tests/files/type_names_gen.sh 4 | 5 | set -ex 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | go run "${script_path_root}type_names.go" 11 | 12 | cat "${script_path_root}type_names.go" | sed -n '/^[ \t]*var _ /p' | sed 's/^[ \t]//g; s/[ \t]$//g; s/^[ \t]*var _ //; /^$/d;' > "${script_path_root}type_names.txt" 13 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/embedded_field.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | P "time" 5 | ) 6 | 7 | type T1 int 8 | type T2 int 9 | 10 | func main() { 11 | // https://golang.org/ref/spec#StructType 12 | var _ struct { 13 | T1 // field name is T1 14 | *T2 // field name is T2 15 | P.Duration // field name is Duration 16 | *P.Month // field name is Month 17 | x, y int // field names are x and y 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-var-decl" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Var Decl" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-var-decl" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-const-decl" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Const Decl" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-const-decl" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-var-decl-core" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Var Decl Core" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-var-decl-core" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/channel_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::TypeParseError; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct ChannelType {} 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum ChannelTypeParseError {} 10 | impl ChannelType { 11 | pub(crate) fn from_channel_type_node( 12 | _node: Node, 13 | _source: &[u8], 14 | ) -> Result { 15 | // TODO 16 | Ok(Self {}) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /golang-var-decl/golang-var-decl-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-var-decl-macro" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Var Decl Macro" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-var-decl-macro" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-const-decl-core" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Const Decl Core" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-const-decl-core" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/function_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::TypeParseError; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct FunctionType {} 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum FunctionTypeParseError {} 10 | impl FunctionType { 11 | pub(crate) fn from_function_type_node( 12 | _node: Node, 13 | _source: &[u8], 14 | ) -> Result { 15 | // TODO 16 | Ok(Self {}) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /golang-const-decl/golang-const-decl-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-const-decl-macro" 3 | version = "0.0.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Const Decl Macro" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-const-decl-macro" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/interface_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::TypeParseError; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct InterfaceType {} 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum InterfaceTypeParseError {} 10 | impl InterfaceType { 11 | pub(crate) fn from_interface_type_node( 12 | _node: Node, 13 | _source: &[u8], 14 | ) -> Result { 15 | // TODO 16 | Ok(Self {}) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /golang-struct-tag/src/grammars/convention_struct_tag.pest: -------------------------------------------------------------------------------- 1 | tag = { "`" ~ tag_pair ~ (" "+ ~ tag_pair)* ~ "`" } 2 | tag_pair = { json | other } 3 | 4 | json = { "json" ~ ":" ~ "\"" ~ json_name ~ ("," ~ json_option)* ~ "\"" } 5 | json_name = @{ ("\x21" | '\x23'..'\x2B' | '\x2D'..'\x39' | '\x3B'..'\x7E')* } 6 | json_option = @{ ('\x20'..'\x21' | '\x23'..'\x2B' | '\x2D'..'\x7E')* } 7 | 8 | other = { other_key ~ ":" ~ "\"" ~ other_value ~ "\"" } 9 | other_key = @{ ("\x21" | '\x23'..'\x39' | '\x3B'..'\x7E')+ } 10 | other_value = @{ ("\x21" | '\x23'..'\x7E')* } 11 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/files/struct_type/gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ./tests/files/struct_type/gen.sh 4 | 5 | set -ex 6 | 7 | script_path=$(cd $(dirname $0) ; pwd -P) 8 | script_path_root="${script_path}/" 9 | 10 | categories=('embedded_field' 'normal' 'tag') 11 | 12 | for category in "${categories[@]}" 13 | do 14 | go run "${script_path_root}${category}.go" 15 | 16 | cat "${script_path_root}${category}.go" | sed -n '/^[ \t]*var _ struct {/,/^[ \t]*}/p' | sed 's/^[ \t]//g; s/[ \t]$//g; s/^[ \t]*var _ //; /^$/d;' > "${script_path_root}${category}.txt" 17 | done 18 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/tests/files/type_names.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type foo int 8 | 9 | func main() { 10 | // 11 | var _ bool 12 | 13 | // 14 | var _ uint8 15 | var _ uint16 16 | var _ uint32 17 | var _ uint64 18 | 19 | var _ int8 20 | var _ int16 21 | var _ int32 22 | var _ int64 23 | 24 | var _ float32 25 | var _ float64 26 | 27 | var _ complex64 28 | var _ complex128 29 | 30 | var _ byte 31 | var _ rune 32 | 33 | var _ uint 34 | var _ int 35 | var _ uintptr 36 | 37 | // 38 | var _ string 39 | 40 | // 41 | var _ time.Duration 42 | 43 | // 44 | var _ foo 45 | } 46 | -------------------------------------------------------------------------------- /golang-type/golang-type/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | golang-type-core = { version = "=0.1.0", features = ["enable-quote-to_tokens"], path = "../golang-type-core" } 17 | golang-type-macro = { version = "=0.1.0", path = "../golang-type-macro" } 18 | -------------------------------------------------------------------------------- /golang-parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-parser" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Parser" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-parser" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | tree-sitter = { version = "~0.17", default-features = false } 17 | tree-sitter-go = { version = "~0.16", default-features = false } 18 | 19 | thiserror = { version = "1.0", default-features = false } 20 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/tests/files/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Node = int 4 | type Comparable = int 5 | 6 | type ( 7 | nodeList = []*Node // nodeList and []*Node are identical types 8 | Polar = polar // Polar and polar denote identical types 9 | ) 10 | 11 | type ( 12 | Point struct{ x, y float64 } // Point and struct{ x, y float64 } are different types 13 | polar Point // polar and Point denote different types 14 | ) 15 | 16 | type TreeNode struct { 17 | left, right *TreeNode 18 | value *Comparable 19 | } 20 | 21 | type Foo struct { 22 | bar uint 23 | } 24 | 25 | type ( 26 | Bar = int 27 | Foo struct { 28 | bar uint 29 | } 30 | ) 31 | 32 | func main() { 33 | } 34 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use syn::parse_macro_input; 4 | 5 | mod gen_json_struct; 6 | mod gen_type_alias; 7 | pub(crate) mod utils; 8 | 9 | #[proc_macro] 10 | pub fn gen_json_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 11 | let input = parse_macro_input!(input as gen_json_struct::Input); 12 | let output = gen_json_struct::get_output(input); 13 | output.into() 14 | } 15 | 16 | #[proc_macro] 17 | pub fn gen_type_alias(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 18 | let input = parse_macro_input!(input as gen_type_alias::Input); 19 | let output = gen_type_alias::get_output(input); 20 | output.into() 21 | } 22 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/slice_type.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_core::{golang_type_name_core::TypeName, SliceType, Type}; 4 | 5 | #[test] 6 | fn test_parse() -> Result<(), Box> { 7 | let content = fs::read_to_string(PathBuf::new().join("tests/files/slice_type.txt"))?; 8 | for (i, str) in content.lines().enumerate() { 9 | match i + 1 { 10 | 1 => assert_eq!( 11 | Type::SliceType(SliceType { 12 | element: Type::TypeName(TypeName::Int).into() 13 | }), 14 | str.parse()? 15 | ), 16 | 17 | _ => {} 18 | } 19 | } 20 | 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/tests/gen_type_alias_from_file.rs: -------------------------------------------------------------------------------- 1 | use golang_type_decl::gen_type_alias_from_file; 2 | 3 | #[test] 4 | fn simple() { 5 | gen_type_alias_from_file!("../golang-type-decl-core/tests/files/simple.go#L3"); 6 | let _: Node = 0_isize; 7 | } 8 | 9 | #[test] 10 | fn with_nth() { 11 | gen_type_alias_from_file!("tests/files/simple.go#L25-L30", nth = 0); 12 | let _: Bar = 0_isize; 13 | } 14 | 15 | #[test] 16 | fn with_type() { 17 | gen_type_alias_from_file!("tests/files/simple.go#L3", bool); 18 | let _: Node = true; 19 | } 20 | 21 | #[test] 22 | fn with_nth_and_type() { 23 | gen_type_alias_from_file!("tests/files/simple.go#L25-L30", bool, nth = 0); 24 | let _: Bar = true; 25 | } 26 | -------------------------------------------------------------------------------- /golang-struct-tag/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-struct-tag" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Struct Tag" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-struct-tag" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | golang-parser = { version = "~0.1", path = "../golang-parser" } 17 | 18 | pest = { version = "2.1", default-features = false } 19 | pest_derive = { version = "2.1", default-features = false } 20 | 21 | thiserror = { version = "1.0", default-features = false } 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "golang-const-decl/golang-const-decl", 4 | "golang-const-decl/golang-const-decl-core", 5 | "golang-const-decl/golang-const-decl-macro", 6 | "golang-parser", 7 | "golang-struct-tag", 8 | "golang-type/golang-type", 9 | "golang-type/golang-type-core", 10 | "golang-type/golang-type-macro", 11 | "golang-type-decl/golang-type-decl", 12 | "golang-type-decl/golang-type-decl-core", 13 | "golang-type-decl/golang-type-decl-macro", 14 | "golang-type-name/golang-type-name", 15 | "golang-type-name/golang-type-name-core", 16 | "golang-type-name/golang-type-name-macro", 17 | "golang-var-decl/golang-var-decl", 18 | "golang-var-decl/golang-var-decl-core", 19 | "golang-var-decl/golang-var-decl-macro", 20 | ] 21 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-name" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang TypeName" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-name" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | golang-type-name-core = { version = "=0.1.0", features = ["enable-quote-to_tokens"], path = "../golang-type-name-core" } 17 | golang-type-name-macro = { version = "=0.1.0", path = "../golang-type-name-macro" } 18 | 19 | [dev-dependencies] 20 | num-complex = { version = "0.4", default-features = false } 21 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-decl" 3 | version = "0.3.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type Decl" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-decl" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | golang-type-decl-core = { version = "=0.3.0", features = ["enable-quote-to_tokens"], path = "../golang-type-decl-core" } 17 | golang-type-decl-macro = { version = "=0.3.0", path = "../golang-type-decl-macro" } 18 | 19 | [dev-dependencies] 20 | serde = { version = "1.0", features = ["derive"] } 21 | serde_json = { version = "1.0" } 22 | serde-aux = { version = "2.2" } 23 | -------------------------------------------------------------------------------- /golang-type/golang-type-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-macro" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type Macro" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-macro" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | golang-type-core = { version = "=0.1.0", features = ["enable-quote-to_tokens"], path = "../golang-type-core" } 20 | 21 | proc-macro2 = { version = "1.0", default-features = false } 22 | quote = { version = "1.0", default-features = false } 23 | syn = { version = "1.0", default-features = false, features = ["parsing", "proc-macro"] } 24 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-name-macro" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang TypeName Macro" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-name-macro" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | golang-type-name-core = { version = "=0.1.0", features = ["enable-quote-to_tokens"], path = "../golang-type-name-core" } 20 | 21 | proc-macro2 = { version = "1.0", default-features = false } 22 | quote = { version = "1.0", default-features = false } 23 | syn = { version = "1.0", default-features = false, features = ["parsing", "proc-macro"] } 24 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-name-core" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang TypeName Core" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-name-core" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [package.metadata.docs.rs] 16 | features = ["enable-quote-to_tokens"] 17 | 18 | [features] 19 | default = [] 20 | enable-quote-to_tokens = ["proc-macro2", "quote"] 21 | 22 | [dependencies] 23 | golang-parser = { version = "~0.1", path = "../../golang-parser" } 24 | 25 | thiserror = { version = "1.0", default-features = false } 26 | 27 | proc-macro2 = { version = "1.0", default-features = false, optional = true } 28 | quote = { version = "1.0", default-features = false, optional = true } 29 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/tests/gen_type_alias.rs: -------------------------------------------------------------------------------- 1 | use golang_type_decl::gen_type_alias; 2 | 3 | #[test] 4 | fn simple() { 5 | gen_type_alias!( 6 | r#" 7 | type Bar = int 8 | "# 9 | ); 10 | } 11 | 12 | #[test] 13 | fn with_nth() { 14 | gen_type_alias!( 15 | r#" 16 | type ( 17 | Bar = int 18 | Foo struct { 19 | bar uint 20 | } 21 | ) 22 | "#, 23 | nth = 0 24 | ); 25 | } 26 | 27 | #[test] 28 | fn with_nth_and_type() { 29 | gen_type_alias!( 30 | r#" 31 | type ( 32 | Bar = int 33 | Foo struct { 34 | bar uint 35 | } 36 | ) 37 | "#, 38 | Option, 39 | nth = 0 40 | ); 41 | 42 | let _: Bar = None; 43 | } 44 | 45 | #[test] 46 | fn with_other_opts() { 47 | gen_type_alias!( 48 | r#" 49 | type Foo = int 50 | "#, 51 | alias_name = "Bar" 52 | ); 53 | 54 | let _: Bar = 1_isize; 55 | } 56 | -------------------------------------------------------------------------------- /golang-type/golang-type-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use golang_type_core::{Type, TypeParseError}; 4 | use quote::quote; 5 | use syn::{ 6 | parse::{Parse, ParseStream}, 7 | parse_macro_input, Error as SynError, LitStr, 8 | }; 9 | 10 | struct GenTypeInput { 11 | r#type: Type, 12 | } 13 | impl Parse for GenTypeInput { 14 | fn parse(input: ParseStream) -> Result { 15 | let r#type = input.parse::()?; 16 | 17 | let r#type: Type = r#type 18 | .value() 19 | .parse() 20 | .map_err(|err: TypeParseError| SynError::new_spanned(r#type, err.to_string()))?; 21 | 22 | Ok(Self { r#type }) 23 | } 24 | } 25 | 26 | #[proc_macro] 27 | pub fn gen_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 28 | let input = parse_macro_input!(input as GenTypeInput); 29 | 30 | let r#type = input.r#type; 31 | 32 | let output = quote!(#r#type); 33 | output.into() 34 | } 35 | -------------------------------------------------------------------------------- /golang-struct-tag/tests/files/json.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type S struct { 9 | A int `json:""` 10 | B int `json:"-"` 11 | C int `json:"-,"` 12 | D int `json:"d"` 13 | E int `json:",omitempty"` 14 | F int `json:",string"` 15 | G int `json:"g,omitempty"` 16 | H int `json:"h,string"` 17 | I int `json:"i,omitempty,string"` 18 | J int `json:"j,string,omitempty"` 19 | K int `json:"k,foo,bar"` 20 | L int `json:"l" xml:""` 21 | } 22 | 23 | func main() { 24 | s1 := &S{ 25 | A: 1, 26 | B: 1, 27 | C: 1, 28 | D: 1, 29 | E: 1, 30 | F: 1, 31 | G: 1, 32 | H: 1, 33 | I: 1, 34 | J: 1, 35 | K: 1, 36 | L: 1, 37 | } 38 | str1, _ := json.Marshal(s1) 39 | fmt.Println(string(str1)) 40 | 41 | s0 := &S{ 42 | A: 0, 43 | B: 0, 44 | C: 0, 45 | D: 0, 46 | E: 0, 47 | F: 0, 48 | G: 0, 49 | H: 0, 50 | I: 0, 51 | J: 0, 52 | K: 0, 53 | L: 0, 54 | } 55 | str0, _ := json.Marshal(s0) 56 | fmt.Println(string(str0)) 57 | } 58 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-decl-macro" 3 | version = "0.3.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type Decl Macro" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-decl-macro" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | golang-type-decl-core = { version = "=0.3.0", features = ["enable-quote-to_tokens"], path = "../golang-type-decl-core" } 20 | 21 | proc-macro2 = { version = "1.0", default-features = false } 22 | quote = { version = "1.0", default-features = false } 23 | syn = { version = "1.0", default-features = false, features = ["parsing", "proc-macro"] } 24 | 25 | url = { version = "2.2", default-features = false } 26 | regex = { version = "1.4", default-features = false } 27 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/tests/gen_json_struct_from_file.rs: -------------------------------------------------------------------------------- 1 | use golang_type_decl::gen_json_struct_from_file; 2 | 3 | #[test] 4 | fn simple() { 5 | gen_json_struct_from_file!("../golang-type-decl-core/tests/files/simple.go#L21-L23"); 6 | Foo { bar: 0 }; 7 | } 8 | 9 | #[test] 10 | fn with_nth() { 11 | gen_json_struct_from_file!("tests/files/simple.go#L25-L30", nth = 1); 12 | Foo { bar: 0 }; 13 | } 14 | 15 | #[test] 16 | fn with_field_types() { 17 | gen_json_struct_from_file!("tests/files/simple.go#L21-L23"; "bar" => bool); 18 | Foo { bar: true }; 19 | } 20 | 21 | #[test] 22 | fn with_nth_and_field_types() { 23 | gen_json_struct_from_file!("tests/files/simple.go#L25-L30", nth = 1; "bar" => bool); 24 | Foo { bar: true }; 25 | } 26 | 27 | #[test] 28 | fn with_box_type() { 29 | type Comparable = isize; 30 | 31 | gen_json_struct_from_file!( 32 | "tests/files/simple.go#L16-L19"; 33 | "left" => { "box_type": true }, 34 | "right" => { "box_type": true } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use golang_type_name_core::{TypeName, TypeNameParseError}; 4 | use quote::quote; 5 | use syn::{ 6 | parse::{Parse, ParseStream}, 7 | parse_macro_input, Error as SynError, LitStr, 8 | }; 9 | 10 | struct GenTypeNameInput { 11 | type_name: TypeName, 12 | } 13 | impl Parse for GenTypeNameInput { 14 | fn parse(input: ParseStream) -> Result { 15 | let type_name = input.parse::()?; 16 | 17 | let type_name: TypeName = type_name 18 | .value() 19 | .parse() 20 | .map_err(|err: TypeNameParseError| SynError::new_spanned(type_name, err.to_string()))?; 21 | 22 | Ok(Self { type_name }) 23 | } 24 | } 25 | 26 | #[proc_macro] 27 | pub fn gen_type_name(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 28 | let input = parse_macro_input!(input as GenTypeNameInput); 29 | 30 | let type_name = input.type_name; 31 | 32 | let output = quote!(#type_name); 33 | output.into() 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-core" 3 | version = "0.1.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type Core" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-core" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [package.metadata.docs.rs] 16 | features = ["enable-quote-to_tokens"] 17 | 18 | [features] 19 | default = [] 20 | enable-quote-to_tokens = ["golang-type-name-core/enable-quote-to_tokens", "proc-macro2", "quote"] 21 | 22 | [dependencies] 23 | golang-type-name-core = { version = "~0.1", path = "../../golang-type-name/golang-type-name-core" } 24 | golang-struct-tag = { version = "~0.1", path = "../../golang-struct-tag" } 25 | golang-parser = { version = "~0.1", path = "../../golang-parser" } 26 | 27 | thiserror = { version = "1.0", default-features = false } 28 | 29 | proc-macro2 = { version = "1.0", default-features = false, optional = true } 30 | quote = { version = "1.0", default-features = false, optional = true } 31 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "golang-type-decl-core" 3 | version = "0.3.0" 4 | authors = ["vkill "] 5 | edition = "2018" 6 | description = "Golang Type Decl core" 7 | license = "Apache-2.0 OR MIT" 8 | repository = "https://github.com/bk-rs/golang-rs" 9 | homepage = "https://github.com/bk-rs/golang-rs" 10 | documentation = "https://docs.rs/golang-type-decl-core" 11 | keywords = [] 12 | categories = [] 13 | readme = "README.md" 14 | 15 | [package.metadata.docs.rs] 16 | features = ["enable-quote-to_tokens"] 17 | 18 | [features] 19 | default = [] 20 | enable-quote-to_tokens = ["golang-type-core/enable-quote-to_tokens", "proc-macro2", "quote", "convert_case"] 21 | 22 | [dependencies] 23 | golang-type-core = { version = "~0.1", path = "../../golang-type/golang-type-core" } 24 | golang-parser = { version = "~0.1", path = "../../golang-parser" } 25 | 26 | thiserror = { version = "1.0", default-features = false } 27 | 28 | proc-macro2 = { version = "1.0", default-features = false, optional = true } 29 | quote = { version = "1.0", default-features = false, optional = true } 30 | 31 | convert_case = { version = "0.4", default-features = false, optional = true } 32 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_json_struct/field_types.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use golang_type_decl_core::type_def::json_struct::JsonStructFieldName; 4 | use syn::{ 5 | parse::{Parse, ParseStream}, 6 | Error as SynError, LitStr, Token, Type, 7 | }; 8 | 9 | #[derive(Default)] 10 | pub struct FieldTypes(pub HashMap); 11 | 12 | impl Parse for FieldTypes { 13 | fn parse(input: ParseStream) -> Result { 14 | let mut inner = HashMap::new(); 15 | 16 | loop { 17 | let field_name = input.parse::()?; 18 | input.parse::]>()?; 19 | let field_type = input.parse::()?; 20 | 21 | if inner.insert(field_name.value(), field_type).is_some() { 22 | let err = format!("duplicate field name: {}", &field_name.value()); 23 | return Err(SynError::new_spanned(field_name, err)); 24 | } 25 | 26 | input.parse::()?; 27 | 28 | if !(input.peek(LitStr) && input.peek2(Token![=>])) { 29 | break; 30 | } 31 | } 32 | 33 | Ok(Self(inner)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/map_type.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_core::{golang_type_name_core::TypeName, MapType, SliceType, Type}; 4 | 5 | #[test] 6 | fn test_parse() -> Result<(), Box> { 7 | let content = fs::read_to_string(PathBuf::new().join("tests/files/map_type.txt"))?; 8 | for (i, str) in content.lines().enumerate() { 9 | match i + 1 { 10 | 1 => assert_eq!( 11 | Type::MapType(MapType { 12 | key: Type::TypeName(TypeName::String).into(), 13 | value: Type::TypeName(TypeName::Int).into() 14 | }), 15 | str.parse()? 16 | ), 17 | 2 => {} 18 | 3 => {} 19 | 4 => assert_eq!( 20 | Type::MapType(MapType { 21 | key: Type::TypeName(TypeName::String).into(), 22 | value: Type::SliceType(SliceType { 23 | element: Type::TypeName(TypeName::String).into() 24 | }) 25 | .into() 26 | }), 27 | str.parse()? 28 | ), 29 | _ => {} 30 | } 31 | } 32 | 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/src/alias_decl/type_alias.rs: -------------------------------------------------------------------------------- 1 | use convert_case::{Case, Casing as _}; 2 | use golang_type_core::Type; 3 | use proc_macro2::TokenStream; 4 | use quote::{format_ident, quote, ToTokens, TokenStreamExt as _}; 5 | 6 | pub struct TypeAlias { 7 | pub name: String, 8 | pub r#type: Type, 9 | pub opt: TypeAliasOption, 10 | } 11 | 12 | #[derive(Default, Debug)] 13 | pub struct TypeAliasOption { 14 | pub alias_name: Option, 15 | pub special_type: Option, 16 | } 17 | 18 | impl ToTokens for TypeAlias { 19 | fn to_tokens(&self, tokens: &mut TokenStream) { 20 | let name = format_ident!( 21 | "{}", 22 | self.opt 23 | .alias_name 24 | .to_owned() 25 | .unwrap_or_else(|| self.name.to_case(Case::Pascal)) 26 | ); 27 | 28 | let type_token = if let Some(special_type) = &self.opt.special_type { 29 | special_type.to_owned() 30 | } else { 31 | let r#type = &self.r#type; 32 | quote!(#r#type) 33 | }; 34 | 35 | let token = quote! { 36 | pub type #name = #type_token; 37 | }; 38 | 39 | tokens.append_all(token); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/pointer_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::{Type, TypeParseError}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct PointerType(pub Box); 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum PointerTypeParseError { 10 | #[error("NodeMissing {0}")] 11 | NodeMissing(String), 12 | } 13 | impl PointerType { 14 | pub(crate) fn from_pointer_type_node( 15 | node: Node, 16 | source: &[u8], 17 | ) -> Result { 18 | let node_pointer_type_element = node.named_child(0).ok_or_else(|| { 19 | PointerTypeParseError::NodeMissing("pointer_type element".to_string()) 20 | })?; 21 | 22 | let element = Type::from_node(node_pointer_type_element, source)?; 23 | 24 | Ok(Self(element.into())) 25 | } 26 | } 27 | 28 | #[cfg(feature = "enable-quote-to_tokens")] 29 | mod enable_quote_to_tokens { 30 | use super::PointerType; 31 | 32 | use proc_macro2::TokenStream; 33 | use quote::{quote, ToTokens, TokenStreamExt as _}; 34 | 35 | impl ToTokens for PointerType { 36 | fn to_tokens(&self, tokens: &mut TokenStream) { 37 | let element = &self.0; 38 | tokens.append_all(quote!(#element)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_type_alias/mod.rs: -------------------------------------------------------------------------------- 1 | use golang_type_decl_core::{ 2 | alias_decl::type_alias::{TypeAlias, TypeAliasOption}, 3 | TypeDecl, TypeSpec, 4 | }; 5 | use proc_macro2::TokenStream; 6 | use quote::quote; 7 | 8 | mod input; 9 | 10 | pub use self::input::Input; 11 | 12 | pub fn get_output(input: Input) -> TokenStream { 13 | let type_decl = match input.code.parse::() { 14 | Ok(type_decl) => type_decl, 15 | Err(err) => { 16 | let err = err.to_string(); 17 | return quote!(compile_error!(#err)); 18 | } 19 | }; 20 | 21 | let (name, r#type) = match type_decl.type_specs.into_iter().nth(input.nth) { 22 | Some(TypeSpec::TypeDef(type_def)) => (type_def.name, type_def.r#type), 23 | Some(TypeSpec::AliasDecl(alias_decl)) => (alias_decl.name, alias_decl.r#type), 24 | None => { 25 | let err = "Require [Alias declarations](https://golang.org/ref/spec#AliasDecl)"; 26 | return quote!(compile_error!(#err)); 27 | } 28 | }; 29 | 30 | let type_alias = TypeAlias { 31 | name, 32 | r#type, 33 | opt: TypeAliasOption { 34 | alias_name: input.alias_name, 35 | special_type: input.r#type.map(|ty| quote!(#ty)), 36 | }, 37 | }; 38 | 39 | quote!(#type_alias) 40 | } 41 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/parenthesized_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::{Type, TypeParseError}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct ParenthesizedType(pub Box); 7 | 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum ParenthesizedTypeParseError { 10 | #[error("NodeMissing {0}")] 11 | NodeMissing(String), 12 | } 13 | impl ParenthesizedType { 14 | pub(crate) fn from_parenthesized_type_node( 15 | node: Node, 16 | source: &[u8], 17 | ) -> Result { 18 | let node_parenthesized_type_element = node.named_child(0).ok_or_else(|| { 19 | ParenthesizedTypeParseError::NodeMissing("parenthesized_type element".to_string()) 20 | })?; 21 | 22 | let element = Type::from_node(node_parenthesized_type_element, source)?; 23 | 24 | Ok(Self(element.into())) 25 | } 26 | } 27 | 28 | #[cfg(feature = "enable-quote-to_tokens")] 29 | mod enable_quote_to_tokens { 30 | use super::ParenthesizedType; 31 | 32 | use proc_macro2::TokenStream; 33 | use quote::{quote, ToTokens, TokenStreamExt as _}; 34 | 35 | impl ToTokens for ParenthesizedType { 36 | fn to_tokens(&self, tokens: &mut TokenStream) { 37 | let element = &self.0; 38 | tokens.append_all(quote!(#element)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/src/type_def.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | use golang_parser::tree_sitter::Node; 4 | use golang_type_core::{Type, TypeParseError}; 5 | 6 | #[cfg(feature = "enable-quote-to_tokens")] 7 | pub mod json_struct; 8 | 9 | #[derive(PartialEq, Eq, Debug, Clone)] 10 | pub struct TypeDef { 11 | pub name: String, 12 | pub r#type: Type, 13 | } 14 | 15 | #[derive(thiserror::Error, Debug)] 16 | pub enum TypeDefParseError { 17 | #[error("NodeMissing {0}")] 18 | NodeMissing(&'static str), 19 | #[error("Utf8Error {0:?}")] 20 | Utf8Error(#[from] str::Utf8Error), 21 | #[error("TypeParseError {0:?}")] 22 | TypeParseError(#[from] TypeParseError), 23 | } 24 | 25 | impl TypeDef { 26 | pub(crate) fn from_type_spec_node( 27 | node: Node, 28 | source: &[u8], 29 | ) -> Result { 30 | debug_assert!(node.kind() == "type_spec"); 31 | 32 | let node_name = node 33 | .named_child(0) 34 | .ok_or(TypeDefParseError::NodeMissing("name"))?; 35 | let name = node_name.utf8_text(source)?; 36 | 37 | let node_type = node 38 | .named_child(1) 39 | .ok_or(TypeDefParseError::NodeMissing("type"))?; 40 | let r#type = Type::from_node(node_type, source)?; 41 | 42 | Ok(Self { 43 | name: name.to_owned(), 44 | r#type, 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/src/alias_decl.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | use golang_parser::tree_sitter::Node; 4 | use golang_type_core::{Type, TypeParseError}; 5 | 6 | #[cfg(feature = "enable-quote-to_tokens")] 7 | pub mod type_alias; 8 | 9 | #[derive(PartialEq, Eq, Debug, Clone)] 10 | pub struct AliasDecl { 11 | pub name: String, 12 | pub r#type: Type, 13 | } 14 | 15 | #[derive(thiserror::Error, Debug)] 16 | pub enum AliasDeclParseError { 17 | #[error("NodeMissing {0}")] 18 | NodeMissing(&'static str), 19 | #[error("Utf8Error {0:?}")] 20 | Utf8Error(#[from] str::Utf8Error), 21 | #[error("TypeParseError {0:?}")] 22 | TypeParseError(#[from] TypeParseError), 23 | } 24 | 25 | impl AliasDecl { 26 | pub(crate) fn from_type_alias_node( 27 | node: Node, 28 | source: &[u8], 29 | ) -> Result { 30 | debug_assert!(node.kind() == "type_alias"); 31 | 32 | let node_name = node 33 | .named_child(0) 34 | .ok_or(AliasDeclParseError::NodeMissing("name"))?; 35 | let name = node_name.utf8_text(source)?; 36 | 37 | let node_type = node 38 | .named_child(1) 39 | .ok_or(AliasDeclParseError::NodeMissing("type"))?; 40 | let r#type = Type::from_node(node_type, source)?; 41 | 42 | Ok(Self { 43 | name: name.to_owned(), 44 | r#type, 45 | }) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /golang-type/golang-type/tests/gen_type.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any as _, TypeId}, 3 | collections::HashMap, 4 | }; 5 | 6 | macro_rules! assert_gen_type { 7 | ($type_lit:literal, $value:expr, $type:ty) => { 8 | let v: golang_type::gen_type!($type_lit); 9 | v = $value; 10 | assert_eq!(v.type_id(), TypeId::of::<$type>()); 11 | }; 12 | } 13 | 14 | #[test] 15 | fn test_array_type() { 16 | assert_gen_type!("[32]byte", vec![8_u8], Vec); 17 | assert_gen_type!("[1000]*float64", vec![0.0_f64], Vec); 18 | assert_gen_type!("[3][5]int", vec![vec![-1_isize]], Vec>); 19 | assert_gen_type!( 20 | "[2][2][2]float64", 21 | vec![vec![vec![0.0_f64]]], 22 | Vec>> 23 | ); 24 | } 25 | 26 | #[test] 27 | fn test_slice_type() { 28 | assert_gen_type!("[]int", vec![-1_isize], Vec); 29 | assert_gen_type!("[][]uint", vec![vec![1_usize]], Vec>); 30 | } 31 | 32 | #[test] 33 | fn test_map_type() { 34 | assert_gen_type!("map[string]int", vec![("".to_owned(), -1_isize)].into_iter().collect(), HashMap); 35 | assert_gen_type!( 36 | "map[string][]string", 37 | vec![("".to_owned(), vec!["".to_owned()])] 38 | .into_iter() 39 | .collect(), 40 | HashMap> 41 | ); 42 | } 43 | 44 | #[test] 45 | fn test_parenthesized_type() { 46 | assert_gen_type!("(int)", -1_isize, isize); 47 | } 48 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/slice_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::{Type, TypeParseError}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct SliceType { 7 | pub element: Box, 8 | } 9 | 10 | #[derive(thiserror::Error, Debug)] 11 | pub enum SliceTypeParseError { 12 | #[error("NodeMissing {0}")] 13 | NodeMissing(String), 14 | } 15 | impl SliceType { 16 | pub(crate) fn from_slice_type_node(node: Node, source: &[u8]) -> Result { 17 | let node_slice_type_element = node 18 | .named_child(0) 19 | .ok_or_else(|| SliceTypeParseError::NodeMissing("slice_type element".to_string()))?; 20 | 21 | let element = Type::from_node(node_slice_type_element, source)?; 22 | 23 | Ok(Self { 24 | element: element.into(), 25 | }) 26 | } 27 | } 28 | 29 | #[cfg(feature = "enable-quote-to_tokens")] 30 | mod enable_quote_to_tokens { 31 | use super::SliceType; 32 | 33 | use proc_macro2::{Punct, Spacing, TokenStream}; 34 | use quote::{format_ident, quote, ToTokens, TokenStreamExt as _}; 35 | 36 | impl ToTokens for SliceType { 37 | fn to_tokens(&self, tokens: &mut TokenStream) { 38 | let vec_ident = format_ident!("{}", "Vec"); 39 | tokens.append_all(quote!(#vec_ident)); 40 | tokens.append(Punct::new('<', Spacing::Alone)); 41 | let element = &self.element; 42 | tokens.append_all(quote!(#element)); 43 | tokens.append(Punct::new('>', Spacing::Alone)); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name/tests/gen_type_name.rs: -------------------------------------------------------------------------------- 1 | use std::any::{Any as _, TypeId}; 2 | 3 | use num_complex::{Complex32, Complex64}; 4 | 5 | macro_rules! assert_gen_type_name { 6 | ($type_name_lit:literal, $value:expr, $type:ty) => { 7 | let v: golang_type_name::gen_type_name!($type_name_lit); 8 | v = $value; 9 | assert_eq!(v.type_id(), TypeId::of::<$type>()); 10 | }; 11 | } 12 | 13 | #[test] 14 | fn test_builtin() { 15 | assert_gen_type_name!("bool", true, bool); 16 | 17 | assert_gen_type_name!("uint8", 8_u8, u8); 18 | assert_gen_type_name!("uint16", 16_u16, u16); 19 | assert_gen_type_name!("uint32", 32_u32, u32); 20 | assert_gen_type_name!("uint64", 64_u64, u64); 21 | assert_gen_type_name!("int8", -8_i8, i8); 22 | assert_gen_type_name!("int16", -16_i16, i16); 23 | assert_gen_type_name!("int32", -32_i32, i32); 24 | assert_gen_type_name!("int64", -64_i64, i64); 25 | assert_gen_type_name!("float32", 0.0_f32, f32); 26 | assert_gen_type_name!("float64", 0.0_f64, f64); 27 | assert_gen_type_name!("complex64", Complex32::new(0.0, 0.0), Complex32); 28 | assert_gen_type_name!("complex128", Complex64::new(0.0, 0.0), Complex64); 29 | assert_gen_type_name!("byte", 8_u8, u8); 30 | assert_gen_type_name!("rune", -32_i32, i32); 31 | assert_gen_type_name!("uint", 1_usize, usize); 32 | assert_gen_type_name!("int", -1_isize, isize); 33 | assert_gen_type_name!("uintptr", 1_usize, usize); 34 | 35 | assert_gen_type_name!("string", "".to_string(), String); 36 | } 37 | 38 | #[test] 39 | fn test_qualified_identifier() { 40 | assert_gen_type_name!( 41 | "num_complex.Complex32", 42 | Complex32::new(0.0, 0.0), 43 | num_complex::Complex32 44 | ); 45 | } 46 | 47 | #[test] 48 | fn test_identifier() { 49 | type Foo = (); 50 | 51 | assert_gen_type_name!("Foo", (), Foo); 52 | } 53 | -------------------------------------------------------------------------------- /golang-struct-tag/src/json.rs: -------------------------------------------------------------------------------- 1 | use pest::iterators::Pairs; 2 | 3 | use crate::{convention_struct_tag_parser::Rule, StructTagParseError}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub enum JsonStructTag { 7 | // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1259 8 | Ignored, 9 | // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1262 10 | Normal(JsonStructTagName, Vec), 11 | } 12 | 13 | pub type JsonStructTagName = Option; 14 | 15 | #[derive(PartialEq, Eq, Debug, Clone)] 16 | pub enum JsonStructTagOption { 17 | // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1278 18 | String, 19 | // https://github.com/golang/go/blob/go1.16.3/src/encoding/json/encode.go#L1300 20 | Omitempty, 21 | // 22 | Unknown(String), 23 | } 24 | impl From<&str> for JsonStructTagOption { 25 | fn from(s: &str) -> Self { 26 | match s { 27 | "string" => Self::String, 28 | "omitempty" => Self::Omitempty, 29 | _ => Self::Unknown(s.to_owned()), 30 | } 31 | } 32 | } 33 | 34 | impl JsonStructTag { 35 | pub(crate) fn from_json_pairs(mut pairs: Pairs<'_, Rule>) -> Result { 36 | let name_pair = pairs.next().ok_or(StructTagParseError::Unknown)?; 37 | let name = name_pair.as_str(); 38 | 39 | if name == "-" && pairs.peek().is_none() { 40 | return Ok(Self::Ignored); 41 | } 42 | 43 | let name = if name.is_empty() { 44 | None 45 | } else { 46 | Some(name.to_owned()) 47 | }; 48 | 49 | let options = pairs 50 | .filter(|pair| !pair.as_str().is_empty()) 51 | .map(|pair| JsonStructTagOption::from(pair.as_str())) 52 | .collect(); 53 | 54 | Ok(Self::Normal(name, options)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/map_type.rs: -------------------------------------------------------------------------------- 1 | use golang_parser::tree_sitter::Node; 2 | 3 | use crate::{Type, TypeParseError}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub struct MapType { 7 | pub key: Box, 8 | pub value: Box, 9 | } 10 | 11 | #[derive(thiserror::Error, Debug)] 12 | pub enum MapTypeParseError { 13 | #[error("NodeMissing {0}")] 14 | NodeMissing(String), 15 | } 16 | impl MapType { 17 | pub(crate) fn from_map_type_node(node: Node, source: &[u8]) -> Result { 18 | let node_map_type_key = node 19 | .named_child(0) 20 | .ok_or_else(|| MapTypeParseError::NodeMissing("map_type key".to_string()))?; 21 | let node_map_type_value = node 22 | .named_child(1) 23 | .ok_or_else(|| MapTypeParseError::NodeMissing("map_type value".to_string()))?; 24 | 25 | let key = Type::from_node(node_map_type_key, source)?; 26 | let value = Type::from_node(node_map_type_value, source)?; 27 | 28 | Ok(Self { 29 | key: key.into(), 30 | value: value.into(), 31 | }) 32 | } 33 | } 34 | 35 | #[cfg(feature = "enable-quote-to_tokens")] 36 | mod enable_quote_to_tokens { 37 | use super::MapType; 38 | 39 | use proc_macro2::{Punct, Spacing, TokenStream}; 40 | use quote::{quote, ToTokens, TokenStreamExt as _}; 41 | 42 | impl ToTokens for MapType { 43 | fn to_tokens(&self, tokens: &mut TokenStream) { 44 | tokens.append_all(quote!(::std::collections::HashMap)); 45 | tokens.append(Punct::new('<', Spacing::Alone)); 46 | let key = &self.key; 47 | tokens.append_all(quote!(#key)); 48 | tokens.append(Punct::new(',', Spacing::Alone)); 49 | let value = &self.value; 50 | tokens.append_all(quote!(#value)); 51 | tokens.append(Punct::new('>', Spacing::Alone)); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/tests/alias_decl.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_decl_core::{ 4 | golang_type_core::{PointerType, SliceType, Type, TypeName}, 5 | AliasDecl, TypeDecl, TypeSpec, 6 | }; 7 | 8 | #[test] 9 | fn test_parse_single() -> Result<(), Box> { 10 | let content = fs::read_to_string(PathBuf::new().join("tests/files/simple.go"))?; 11 | let str = content.lines().skip(2).next().unwrap(); 12 | 13 | assert_eq!( 14 | TypeDecl { 15 | type_specs: vec![TypeSpec::AliasDecl(AliasDecl { 16 | name: "Node".to_owned(), 17 | r#type: Type::TypeName(TypeName::Int) 18 | })] 19 | }, 20 | str.parse()? 21 | ); 22 | 23 | Ok(()) 24 | } 25 | 26 | #[test] 27 | fn test_parse_multi() -> Result<(), Box> { 28 | let content = fs::read_to_string(PathBuf::new().join("tests/files/simple.go"))?; 29 | let str = content 30 | .lines() 31 | .skip(5) 32 | .take(4) 33 | .collect::>() 34 | .join("\r\n"); 35 | 36 | assert_eq!( 37 | TypeDecl { 38 | type_specs: vec![ 39 | TypeSpec::AliasDecl(AliasDecl { 40 | name: "nodeList".to_owned(), 41 | r#type: Type::SliceType(SliceType { 42 | element: Type::PointerType(PointerType( 43 | Type::TypeName(TypeName::Identifier("Node".to_owned())).into() 44 | )) 45 | .into() 46 | }) 47 | }), 48 | TypeSpec::AliasDecl(AliasDecl { 49 | name: "Polar".to_owned(), 50 | r#type: Type::TypeName(TypeName::Identifier("polar".to_owned())) 51 | }), 52 | ] 53 | }, 54 | str.parse()? 55 | ); 56 | 57 | Ok(()) 58 | } 59 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/src/gen_type_alias.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! gen_type_alias { 3 | // 4 | ($code:literal) => { 5 | golang_type_decl_macro::gen_type_alias!(code = $code,); 6 | }; 7 | // 8 | ( 9 | $code:literal, 10 | $type_:ty 11 | ) => { 12 | golang_type_decl_macro::gen_type_alias!( 13 | code = $code, 14 | type_ = $type_, 15 | ); 16 | }; 17 | ( 18 | $code:literal, 19 | $( $opt_k:ident = $opt_v:literal ),+ $(,)? 20 | ) => { 21 | golang_type_decl_macro::gen_type_alias!( 22 | code = $code, 23 | $( $opt_k = $opt_v ,)* 24 | ); 25 | }; 26 | // 27 | ( 28 | $code:literal, 29 | $type_:ty, 30 | $( $opt_k:ident = $opt_v:literal ),+ $(,)? 31 | ) => { 32 | golang_type_decl_macro::gen_type_alias!( 33 | code = $code, 34 | type_ = $type_, 35 | $( $opt_k = $opt_v ,)* 36 | ); 37 | }; 38 | } 39 | 40 | #[macro_export] 41 | macro_rules! gen_type_alias_from_file { 42 | // 43 | ($path:literal) => { 44 | golang_type_decl_macro::gen_type_alias!(path = $path,); 45 | }; 46 | // 47 | ( 48 | $path:literal, 49 | $type_:ty 50 | ) => { 51 | golang_type_decl_macro::gen_type_alias!( 52 | path = $path, 53 | type_ = $type_, 54 | ); 55 | }; 56 | ( 57 | $path:literal, 58 | $( $opt_k:ident = $opt_v:literal ),+ $(,)? 59 | ) => { 60 | golang_type_decl_macro::gen_type_alias!( 61 | path = $path, 62 | $( $opt_k = $opt_v ,)* 63 | ); 64 | }; 65 | // 66 | ( 67 | $path:literal, 68 | $type_:ty, 69 | $( $opt_k:ident = $opt_v:literal ),+ $(,)? 70 | ) => { 71 | golang_type_decl_macro::gen_type_alias!( 72 | path = $path, 73 | type_ = $type_, 74 | $( $opt_k = $opt_v ,)* 75 | ); 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /golang-parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use tree_sitter; 2 | 3 | use tree_sitter::{Node, Tree}; 4 | 5 | pub const NODE_KIND_COMMENT: &str = "comment"; 6 | 7 | pub struct Parser { 8 | code: String, 9 | tree: Tree, 10 | } 11 | impl Parser { 12 | pub fn new(code: impl AsRef) -> Result { 13 | let mut parser = tree_sitter::Parser::new(); 14 | parser 15 | .set_language(tree_sitter_go::language()) 16 | .map_err(|err| Error::TreeSitterLanguageError(err.to_string()))?; 17 | 18 | let code = code.as_ref(); 19 | let code = if code.ends_with(';') { 20 | code.to_owned() 21 | } else { 22 | format!("{};", code) 23 | }; 24 | 25 | let tree = parser 26 | .parse(&code, None) 27 | .ok_or(Error::TreeSitterParseCodeFailed)?; 28 | 29 | debug_assert!(tree.root_node().kind() == "source_file"); 30 | 31 | Ok(Self { code, tree }) 32 | } 33 | 34 | pub fn get_source(&self) -> &[u8] { 35 | &self.code.as_bytes() 36 | } 37 | 38 | pub fn get_root_node(&self) -> Node<'_> { 39 | self.tree.root_node() 40 | } 41 | } 42 | 43 | #[derive(thiserror::Error, Debug)] 44 | pub enum Error { 45 | #[error("TreeSitterLanguageError {0}")] 46 | TreeSitterLanguageError(String), 47 | #[error("TreeSitterParseCodeFailed")] 48 | TreeSitterParseCodeFailed, 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use super::*; 54 | 55 | #[test] 56 | fn simple() { 57 | match Parser::new("var _ int") { 58 | Ok(parser) => { 59 | assert_eq!(parser.get_source(), b"var _ int;"); 60 | assert_eq!( 61 | parser.get_root_node().utf8_text(parser.get_source()), 62 | Ok("var _ int;") 63 | ); 64 | } 65 | Err(err) => assert!(false, "{:?}", err), 66 | } 67 | 68 | match Parser::new("var _ int;") { 69 | Ok(parser) => { 70 | assert_eq!(parser.get_source(), b"var _ int;"); 71 | } 72 | Err(err) => assert!(false, "{:?}", err), 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_type_alias/input.rs: -------------------------------------------------------------------------------- 1 | use syn::{ 2 | parse::{Parse, ParseStream}, 3 | Error as SynError, Ident, LitInt, LitStr, Token, Type, 4 | }; 5 | 6 | use crate::utils::path_to_code; 7 | 8 | pub struct Input { 9 | pub code: String, 10 | pub nth: usize, 11 | // 12 | pub alias_name: Option, 13 | pub r#type: Option, 14 | } 15 | 16 | impl Parse for Input { 17 | fn parse(input: ParseStream) -> Result { 18 | let mut code = String::new(); 19 | let mut nth = 0; 20 | 21 | let mut alias_name = None; 22 | let mut r#type = None; 23 | 24 | while !input.is_empty() { 25 | let key = input.parse::()?; 26 | input.parse::()?; 27 | 28 | if key == "code" { 29 | let s = input.parse::()?.value(); 30 | input.parse::()?; 31 | 32 | code = s.trim_start().trim_end().to_owned(); 33 | } else if key == "path" { 34 | let s = input.parse::()?.value(); 35 | input.parse::()?; 36 | 37 | match path_to_code(&s) { 38 | Ok(s) => code = s, 39 | Err(err) => { 40 | return Err(SynError::new_spanned(key, err)); 41 | } 42 | } 43 | } else if key == "nth" { 44 | nth = input.parse::()?.base10_parse::()?; 45 | input.parse::()?; 46 | } else if key == "alias_name" { 47 | alias_name = Some(input.parse::()?.value()); 48 | input.parse::()?; 49 | } else if key == "type_" { 50 | r#type = Some(input.parse::()?); 51 | input.parse::()?; 52 | } else { 53 | let err = format!("unexpected input key: {}", key); 54 | return Err(SynError::new_spanned(key, err)); 55 | } 56 | } 57 | 58 | Ok(Self { 59 | code, 60 | nth, 61 | alias_name, 62 | r#type, 63 | }) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/array_type.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_core::{ 4 | golang_type_name_core::TypeName, ArrayLength, ArrayType, PointerType, Type, 5 | }; 6 | 7 | #[test] 8 | fn test_parse() -> Result<(), Box> { 9 | let content = fs::read_to_string(PathBuf::new().join("tests/files/array_type.txt"))?; 10 | for (i, str) in content.lines().enumerate() { 11 | match i + 1 { 12 | 1 => assert_eq!( 13 | Type::ArrayType(ArrayType { 14 | length: ArrayLength::IntLiteral(32), 15 | element: Type::TypeName(TypeName::Byte).into() 16 | }), 17 | str.parse()? 18 | ), 19 | 2 => {} 20 | 3 => assert_eq!( 21 | Type::ArrayType(ArrayType { 22 | length: ArrayLength::IntLiteral(1000), 23 | element: Type::PointerType(PointerType( 24 | Type::TypeName(TypeName::Float64).into() 25 | )) 26 | .into() 27 | }), 28 | str.parse()? 29 | ), 30 | 4 => assert_eq!( 31 | Type::ArrayType(ArrayType { 32 | length: ArrayLength::IntLiteral(3), 33 | element: Type::ArrayType(ArrayType { 34 | length: ArrayLength::IntLiteral(5), 35 | element: Type::TypeName(TypeName::Int).into() 36 | }) 37 | .into() 38 | }), 39 | str.parse()? 40 | ), 41 | 5 => assert_eq!( 42 | Type::ArrayType(ArrayType { 43 | length: ArrayLength::IntLiteral(2), 44 | element: Type::ArrayType(ArrayType { 45 | length: ArrayLength::IntLiteral(2), 46 | element: Type::ArrayType(ArrayType { 47 | length: ArrayLength::IntLiteral(2), 48 | element: Type::TypeName(TypeName::Float64).into() 49 | }) 50 | .into() 51 | }) 52 | .into() 53 | }), 54 | str.parse()? 55 | ), 56 | _ => {} 57 | } 58 | } 59 | 60 | Ok(()) 61 | } 62 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_json_struct/field_opts.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use golang_type_decl_core::type_def::json_struct::{JsonStructFieldName, JsonStructFieldOption}; 4 | use syn::{ 5 | parse::{Parse, ParseStream}, 6 | Error as SynError, LitBool, LitStr, Token, 7 | }; 8 | 9 | #[derive(Default)] 10 | pub struct FieldOpts(pub HashMap); 11 | 12 | impl Parse for FieldOpts { 13 | fn parse(input: ParseStream) -> Result { 14 | let mut inner = HashMap::new(); 15 | 16 | loop { 17 | let field_name = input.parse::()?; 18 | input.parse::]>()?; 19 | 20 | let mut field_opt = JsonStructFieldOption::default(); 21 | 22 | loop { 23 | if input.peek(LitStr) && input.peek2(Token![->]) { 24 | let field_opt_k = input.parse::()?.value(); 25 | input.parse::]>()?; 26 | if field_opt_k == "attr_serde_deserialize_with" { 27 | let attr_serde_deserialize_with = input.parse::()?.value(); 28 | field_opt.attr_serde_deserialize_with = Some(attr_serde_deserialize_with); 29 | } else if field_opt_k == "box_type" { 30 | let box_type = input.parse::()?.value(); 31 | field_opt.box_type = box_type; 32 | } else { 33 | let err = format!("unexpected opt key: {}", field_opt_k); 34 | return Err(SynError::new_spanned(field_opt_k, err)); 35 | } 36 | } 37 | 38 | input.parse::()?; 39 | 40 | if !(input.peek(LitStr) && input.peek2(Token![->])) { 41 | break; 42 | } 43 | } 44 | 45 | if inner.insert(field_name.value(), field_opt).is_some() { 46 | let err = format!("duplicate field name: {}", &field_name.value()); 47 | return Err(SynError::new_spanned(field_name, err)); 48 | } 49 | 50 | input.parse::()?; 51 | 52 | if !(input.peek(LitStr) && input.peek2(Token![=>])) { 53 | break; 54 | } 55 | } 56 | 57 | Ok(Self(inner)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/array_type.rs: -------------------------------------------------------------------------------- 1 | use std::{num::ParseIntError, str}; 2 | 3 | use golang_parser::tree_sitter::Node; 4 | 5 | use crate::{Type, TypeParseError}; 6 | 7 | #[derive(PartialEq, Eq, Debug, Clone)] 8 | pub struct ArrayType { 9 | pub length: ArrayLength, 10 | pub element: Box, 11 | } 12 | 13 | #[derive(PartialEq, Eq, Debug, Clone)] 14 | pub enum ArrayLength { 15 | IntLiteral(usize), 16 | Other(String), 17 | } 18 | 19 | #[derive(thiserror::Error, Debug)] 20 | pub enum ArrayTypeParseError { 21 | #[error("NodeMissing {0}")] 22 | NodeMissing(String), 23 | #[error("Utf8Error {0:?}")] 24 | Utf8Error(str::Utf8Error), 25 | #[error("IntLiteralValueInvalid {0:?}")] 26 | IntLiteralValueInvalid(ParseIntError), 27 | } 28 | impl ArrayType { 29 | pub(crate) fn from_array_type_node(node: Node, source: &[u8]) -> Result { 30 | let node_array_type_length = node 31 | .named_child(0) 32 | .ok_or_else(|| ArrayTypeParseError::NodeMissing("array_type length".to_string()))?; 33 | let node_array_type_element = node 34 | .named_child(1) 35 | .ok_or_else(|| ArrayTypeParseError::NodeMissing("array_type element".to_string()))?; 36 | 37 | let length_str = node_array_type_length 38 | .utf8_text(source) 39 | .map_err(ArrayTypeParseError::Utf8Error)?; 40 | 41 | let length = match node_array_type_length.kind() { 42 | "int_literal" => { 43 | let length: usize = length_str 44 | .parse() 45 | .map_err(ArrayTypeParseError::IntLiteralValueInvalid)?; 46 | ArrayLength::IntLiteral(length) 47 | } 48 | _ => ArrayLength::Other(length_str.to_string()), 49 | }; 50 | 51 | let element = Type::from_node(node_array_type_element, source)?; 52 | 53 | Ok(Self { 54 | length, 55 | element: element.into(), 56 | }) 57 | } 58 | } 59 | 60 | #[cfg(feature = "enable-quote-to_tokens")] 61 | mod enable_quote_to_tokens { 62 | use super::ArrayType; 63 | 64 | use proc_macro2::{Punct, Spacing, TokenStream}; 65 | use quote::{format_ident, quote, ToTokens, TokenStreamExt as _}; 66 | 67 | impl ToTokens for ArrayType { 68 | fn to_tokens(&self, tokens: &mut TokenStream) { 69 | let vec_ident = format_ident!("{}", "Vec"); 70 | tokens.append_all(quote!(#vec_ident)); 71 | tokens.append(Punct::new('<', Spacing::Alone)); 72 | let element = &self.element; 73 | tokens.append_all(quote!(#element)); 74 | tokens.append(Punct::new('>', Spacing::Alone)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_json_struct/mod.rs: -------------------------------------------------------------------------------- 1 | use golang_type_decl_core::{ 2 | golang_type_core::{StructField, Type}, 3 | type_def::json_struct::{JsonStruct, JsonStructOption}, 4 | TypeDecl, TypeSpec, 5 | }; 6 | use proc_macro2::TokenStream; 7 | use quote::quote; 8 | 9 | mod field_opts; 10 | mod field_types; 11 | mod input; 12 | 13 | pub use self::input::Input; 14 | 15 | #[allow(clippy::needless_collect)] 16 | pub fn get_output(input: Input) -> TokenStream { 17 | let type_decl = match input.code.parse::() { 18 | Ok(type_decl) => type_decl, 19 | Err(err) => { 20 | let err = err.to_string(); 21 | return quote!(compile_error!(#err)); 22 | } 23 | }; 24 | 25 | let type_def = match type_decl.type_specs.into_iter().nth(input.nth) { 26 | Some(TypeSpec::TypeDef(type_def)) => type_def, 27 | Some(TypeSpec::AliasDecl(_)) => { 28 | let err = "Require [Type definitions](https://golang.org/ref/spec#TypeDef)"; 29 | return quote!(compile_error!(#err)); 30 | } 31 | None => { 32 | let err = "Require [Type definitions](https://golang.org/ref/spec#TypeDef)"; 33 | return quote!(compile_error!(#err)); 34 | } 35 | }; 36 | 37 | let name = &type_def.name; 38 | let struct_type = match &type_def.r#type { 39 | Type::StructType(struct_type) => struct_type, 40 | _ => { 41 | let err = 42 | "Require type definition [StructType](https://golang.org/ref/spec#StructType)"; 43 | return quote!(compile_error!(#err)); 44 | } 45 | }; 46 | 47 | let field_names: Vec<_> = struct_type 48 | .field_decls 49 | .iter() 50 | .map(|field_decl| match &field_decl.struct_field { 51 | StructField::IdentifierListType(names, _) => names.to_owned(), 52 | StructField::EmbeddedField(embedded_field) => vec![embedded_field.name()], 53 | }) 54 | .flatten() 55 | .collect(); 56 | for field_name in input.field_opts.0.keys() { 57 | if !field_names.contains(field_name) { 58 | let err = format!("field [{}] not found", field_name); 59 | return quote!(compile_error!(#err)); 60 | } 61 | } 62 | 63 | let json_struct = JsonStruct { 64 | name: name.to_owned(), 65 | struct_type: struct_type.to_owned(), 66 | opt: JsonStructOption { 67 | enable_derive_serde_ser: !input.disable_derive_serde_ser, 68 | enable_derive_serde_de: !input.disable_derive_serde_de, 69 | custom_derive: input.custom_derive, 70 | alias_name: input.alias_name, 71 | }, 72 | field_opts: input.field_opts.0, 73 | }; 74 | 75 | quote!(#json_struct) 76 | } 77 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/tests/gen_json_struct.rs: -------------------------------------------------------------------------------- 1 | use std::error; 2 | 3 | use golang_type_decl::gen_json_struct; 4 | use serde_aux::field_attributes::deserialize_bool_from_anything; 5 | 6 | #[test] 7 | fn simple() -> Result<(), Box> { 8 | gen_json_struct!( 9 | r#" 10 | type User struct { 11 | Name string 12 | Age uint `json:",string"` 13 | Gender string `json:",omitempty"` 14 | Addr string `json:"Address"` 15 | CreatedAt int `json:"-"` 16 | } 17 | "# 18 | ); 19 | 20 | let user: User = serde_json::from_str( 21 | r#" 22 | { 23 | "Name": "foo", 24 | "Age": "20", 25 | "Address": "bar" 26 | } 27 | "#, 28 | )?; 29 | 30 | assert_eq!(user.name, "foo"); 31 | assert_eq!(user.age, "20"); 32 | assert_eq!(user.gender, None); 33 | assert_eq!(user.addr, "bar"); 34 | assert_eq!(user.created_at, None); 35 | 36 | Ok(()) 37 | } 38 | 39 | #[test] 40 | fn with_nth() { 41 | gen_json_struct!( 42 | r#" 43 | type ( 44 | Bar = int 45 | Foo struct { 46 | bar uint 47 | } 48 | ) 49 | "#, 50 | nth = 1 51 | ); 52 | Foo { bar: 0 }; 53 | } 54 | 55 | #[test] 56 | fn with_field_types_and_field_opts() -> Result<(), Box> { 57 | gen_json_struct!( 58 | r#" 59 | type User struct { 60 | Age int 61 | Actived string 62 | } 63 | "#; 64 | "Age" => u8, 65 | "Actived" => bool 66 | ; 67 | "Actived" => { 68 | "attr_serde_deserialize_with": "deserialize_bool_from_anything" 69 | } 70 | ); 71 | User { 72 | age: 18_u8, 73 | actived: true, 74 | }; 75 | 76 | let user: User = serde_json::from_str( 77 | r#" 78 | { 79 | "Age": 18, 80 | "Actived": "1" 81 | } 82 | "#, 83 | )?; 84 | 85 | assert_eq!(user.age, 18); 86 | assert_eq!(user.actived, true); 87 | 88 | Ok(()) 89 | } 90 | 91 | #[test] 92 | fn with_nth_and_field_types() { 93 | gen_json_struct!( 94 | r#" 95 | type ( 96 | Bar = int 97 | Foo struct { 98 | bar uint 99 | } 100 | ) 101 | "#, 102 | nth = 1 103 | ; 104 | "bar" => Option 105 | ); 106 | Foo { bar: None }; 107 | } 108 | 109 | #[test] 110 | fn with_other_opts() { 111 | gen_json_struct!( 112 | r#" 113 | type Foo struct { 114 | bar uint 115 | } 116 | "#, 117 | disable_derive_serde_ser = true, 118 | disable_derive_serde_de = true, 119 | custom_derive = "Debug, Clone, Default", 120 | alias_name = "Bar" 121 | ); 122 | 123 | println!("{:?}", Bar::default().clone()); 124 | } 125 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/tests/type_def.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_decl_core::{ 4 | golang_type_core::{FieldDecl, PointerType, StructField, StructType, Type, TypeName}, 5 | TypeDecl, TypeDef, TypeSpec, 6 | }; 7 | 8 | #[test] 9 | fn test_parse_single() -> Result<(), Box> { 10 | let content = fs::read_to_string(PathBuf::new().join("tests/files/simple.go"))?; 11 | let str = content 12 | .lines() 13 | .skip(15) 14 | .take(4) 15 | .collect::>() 16 | .join("\r\n"); 17 | 18 | assert_eq!( 19 | TypeDecl { 20 | type_specs: vec![TypeSpec::TypeDef(TypeDef { 21 | name: "TreeNode".to_owned(), 22 | r#type: Type::StructType(StructType { 23 | field_decls: vec![ 24 | FieldDecl { 25 | struct_field: StructField::IdentifierListType( 26 | vec!["left".to_owned(), "right".to_owned()], 27 | Type::PointerType(PointerType( 28 | Type::TypeName(TypeName::Identifier("TreeNode".to_owned())) 29 | .into() 30 | )) 31 | .into() 32 | ), 33 | tag: None, 34 | }, 35 | FieldDecl { 36 | struct_field: StructField::IdentifierListType( 37 | vec!["value".to_owned()], 38 | Type::PointerType(PointerType( 39 | Type::TypeName(TypeName::Identifier("Comparable".to_owned())) 40 | .into() 41 | )) 42 | .into() 43 | ), 44 | tag: None, 45 | }, 46 | ] 47 | }) 48 | })] 49 | }, 50 | str.parse()? 51 | ); 52 | 53 | Ok(()) 54 | } 55 | 56 | #[test] 57 | fn test_parse_multi() -> Result<(), Box> { 58 | let content = fs::read_to_string(PathBuf::new().join("tests/files/simple.go"))?; 59 | let str = content 60 | .lines() 61 | .skip(10) 62 | .take(4) 63 | .collect::>() 64 | .join("\r\n"); 65 | 66 | assert_eq!( 67 | TypeDecl { 68 | type_specs: vec![ 69 | TypeSpec::TypeDef(TypeDef { 70 | name: "Point".to_owned(), 71 | r#type: Type::StructType(StructType { 72 | field_decls: vec![FieldDecl { 73 | struct_field: StructField::IdentifierListType( 74 | vec!["x".to_owned(), "y".to_owned()], 75 | Type::TypeName(TypeName::Float64).into(), 76 | ), 77 | tag: None, 78 | },] 79 | }) 80 | }), 81 | TypeSpec::TypeDef(TypeDef { 82 | name: "polar".to_owned(), 83 | r#type: Type::TypeName(TypeName::Identifier("Point".to_owned())), 84 | }) 85 | ] 86 | }, 87 | str.parse()? 88 | ); 89 | 90 | Ok(()) 91 | } 92 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use golang_type_core; 2 | 3 | use std::str::FromStr; 4 | 5 | use golang_parser::{Parser, NODE_KIND_COMMENT}; 6 | 7 | pub mod alias_decl; 8 | pub mod type_def; 9 | 10 | pub use self::alias_decl::{AliasDecl, AliasDeclParseError}; 11 | pub use self::type_def::{TypeDef, TypeDefParseError}; 12 | 13 | #[derive(PartialEq, Eq, Debug, Clone)] 14 | pub struct TypeDecl { 15 | pub type_specs: Vec, 16 | } 17 | 18 | #[derive(PartialEq, Eq, Debug, Clone)] 19 | pub enum TypeSpec { 20 | AliasDecl(AliasDecl), 21 | TypeDef(TypeDef), 22 | } 23 | 24 | #[derive(thiserror::Error, Debug)] 25 | pub enum TypeDeclParseError { 26 | #[error("GolangParserError {0:?}")] 27 | GolangParserError(#[from] golang_parser::Error), 28 | #[error("NodeKindUnknown {0}")] 29 | NodeKindUnknown(String), 30 | // 31 | #[error("AliasDeclParseError {0:?}")] 32 | AliasDeclParseError(#[from] AliasDeclParseError), 33 | #[error("TypeDefParseError {0:?}")] 34 | TypeDefParseError(#[from] TypeDefParseError), 35 | } 36 | 37 | impl FromStr for TypeDecl { 38 | type Err = TypeDeclParseError; 39 | 40 | fn from_str(s: &str) -> Result { 41 | let parser = Parser::new(s)?; 42 | let source = parser.get_source(); 43 | let root_node = parser.get_root_node(); 44 | 45 | let mut type_specs = vec![]; 46 | 47 | let mut cursor = root_node.walk(); 48 | let mut node_source_file_named_children_iter = root_node 49 | .named_children(&mut cursor) 50 | .filter(|x| x.kind() != NODE_KIND_COMMENT); 51 | 52 | if let Some(node_type_declaration) = node_source_file_named_children_iter.next() { 53 | for node in node_type_declaration 54 | .named_children(&mut node_type_declaration.walk()) 55 | .filter(|x| x.kind() != NODE_KIND_COMMENT) 56 | { 57 | match node.kind() { 58 | "type_alias" => { 59 | let type_spec = 60 | TypeSpec::AliasDecl(AliasDecl::from_type_alias_node(node, source)?); 61 | type_specs.push(type_spec); 62 | } 63 | "type_spec" => { 64 | let type_spec = 65 | TypeSpec::TypeDef(TypeDef::from_type_spec_node(node, source)?); 66 | type_specs.push(type_spec); 67 | } 68 | _ => return Err(TypeDeclParseError::NodeKindUnknown(node.kind().to_owned())), 69 | } 70 | } 71 | } 72 | 73 | if let Some(node) = node_source_file_named_children_iter.next() { 74 | return Err(TypeDeclParseError::NodeKindUnknown(node.kind().to_owned())); 75 | } 76 | 77 | Ok(Self { type_specs }) 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::*; 84 | 85 | #[test] 86 | fn test_parse_empty() { 87 | match "type ()".parse::() { 88 | Ok(type_decl) => assert_eq!(type_decl.type_specs.len(), 0), 89 | Err(err) => assert!(false, "{:?}", err), 90 | } 91 | } 92 | 93 | #[test] 94 | fn test_parse_only_comment() { 95 | match r#" 96 | type ( 97 | // 98 | ) 99 | "# 100 | .trim_start() 101 | .trim_end() 102 | .parse::() 103 | { 104 | Ok(type_decl) => assert_eq!(type_decl.type_specs.len(), 0), 105 | Err(err) => assert!(false, "{:?}", err), 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::{env, fs, path::PathBuf}; 2 | 3 | use regex::Regex; 4 | use url::Url; 5 | 6 | pub(crate) fn path_to_code(path: &str) -> Result { 7 | let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR") 8 | .map_err(|_| "CARGO_MANIFEST_DIR is not set; please use Cargo to build".to_owned())?; 9 | 10 | let mut path = PathBuf::from(cargo_manifest_dir).join(path); 11 | 12 | let url = Url::parse(format!("file://{}", path.to_str().unwrap()).as_str()) 13 | .map_err(|err| format!("failed to read file at {:?}: {}", path, err))?; 14 | 15 | let (line_start, line_end) = if let Some(fragment) = url.fragment() { 16 | parse_fragment(fragment) 17 | .map(|v| { 18 | path = PathBuf::from(url.path()); 19 | v 20 | }) 21 | .map_err(|err| format!("file invalid at {:?}: {}", path, err))? 22 | } else { 23 | (None, None) 24 | }; 25 | 26 | if !path.exists() { 27 | return Err(format!("file not exists at {:?}", path)); 28 | } 29 | 30 | let content = fs::read_to_string(&path) 31 | .map_err(|err| format!("failed to read file at {:?}: {}", path, err))?; 32 | 33 | if let Some(line_start) = line_start { 34 | Ok(content 35 | .lines() 36 | .skip(line_start - 1) 37 | .take( 38 | if let Some(line_end) = line_end { 39 | line_end - line_start 40 | } else { 41 | 0 42 | } + 1, 43 | ) 44 | .collect::>() 45 | .join("\r\n")) 46 | } else { 47 | Ok(content) 48 | } 49 | } 50 | 51 | fn parse_fragment(fragment: &str) -> Result<(Option, Option), String> { 52 | let re = Regex::new(r"^L(?P[\d]+)(-L(?P[\d]+))?$").unwrap(); 53 | 54 | let cap = re 55 | .captures_iter(fragment) 56 | .next() 57 | .ok_or_else(|| "fragment invalid".to_owned())?; 58 | 59 | let start = if let Some(val) = cap.name("start") { 60 | Some( 61 | val.as_str() 62 | .parse::() 63 | .map_err(|err| err.to_string())?, 64 | ) 65 | } else { 66 | None 67 | }; 68 | 69 | let end = if let Some(val) = cap.name("end") { 70 | Some( 71 | val.as_str() 72 | .parse::() 73 | .map_err(|err| err.to_string())?, 74 | ) 75 | } else { 76 | None 77 | }; 78 | 79 | if end.is_some() && start.unwrap_or_default() > end.unwrap_or_default() { 80 | return Err("fragment invalid".to_owned()); 81 | } 82 | 83 | Ok((start, end)) 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | 90 | #[test] 91 | fn test_parse_fragment() { 92 | match parse_fragment("L1") { 93 | Ok((start, end)) => { 94 | assert_eq!(start, Some(1)); 95 | assert_eq!(end, None); 96 | } 97 | Err(err) => assert!(false, "{}", err), 98 | } 99 | 100 | match parse_fragment("L1-L2") { 101 | Ok((start, end)) => { 102 | assert_eq!(start, Some(1)); 103 | assert_eq!(end, Some(2)); 104 | } 105 | Err(err) => assert!(false, "{}", err), 106 | } 107 | 108 | match parse_fragment("Ln") { 109 | Ok(_) => assert!(false), 110 | Err(_) => (), 111 | } 112 | 113 | match parse_fragment("L1-L2-L3") { 114 | Ok(_) => assert!(false), 115 | Err(_) => (), 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-macro/src/gen_json_struct/input.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | use syn::{ 3 | parse::{Parse, ParseStream}, 4 | Error as SynError, Ident, LitBool, LitInt, LitStr, Token, 5 | }; 6 | 7 | use crate::utils::path_to_code; 8 | 9 | use super::{field_opts::FieldOpts, field_types::FieldTypes}; 10 | 11 | pub struct Input { 12 | pub code: String, 13 | pub nth: usize, 14 | // 15 | pub disable_derive_serde_ser: bool, 16 | pub disable_derive_serde_de: bool, 17 | pub custom_derive: Vec, 18 | 19 | pub alias_name: Option, 20 | // 21 | pub field_opts: FieldOpts, 22 | } 23 | 24 | impl Parse for Input { 25 | fn parse(input: ParseStream) -> Result { 26 | let mut code = String::new(); 27 | let mut nth = 0; 28 | 29 | let mut disable_derive_serde_ser = false; 30 | let mut disable_derive_serde_de = false; 31 | let mut custom_derive = vec![]; 32 | 33 | let mut alias_name = None; 34 | 35 | let mut field_types = FieldTypes::default(); 36 | let mut field_opts = FieldOpts::default(); 37 | 38 | while !input.is_empty() { 39 | let key = input.parse::()?; 40 | input.parse::()?; 41 | 42 | if key == "code" { 43 | let s = input.parse::()?.value(); 44 | input.parse::()?; 45 | 46 | code = s.trim_start().trim_end().to_owned(); 47 | } else if key == "path" { 48 | let s = input.parse::()?.value(); 49 | input.parse::()?; 50 | 51 | match path_to_code(&s) { 52 | Ok(s) => code = s, 53 | Err(err) => { 54 | return Err(SynError::new_spanned(key, err)); 55 | } 56 | } 57 | } else if key == "nth" { 58 | nth = input.parse::()?.base10_parse::()?; 59 | input.parse::()?; 60 | } else if key == "disable_derive_serde_ser" { 61 | disable_derive_serde_ser = input.parse::()?.value(); 62 | input.parse::()?; 63 | } else if key == "disable_derive_serde_de" { 64 | disable_derive_serde_de = input.parse::()?.value(); 65 | input.parse::()?; 66 | } else if key == "custom_derive" { 67 | let s = input.parse::()?.value(); 68 | if !s.is_empty() { 69 | custom_derive = s.split(',').map(|x| x.trim().to_owned()).collect() 70 | }; 71 | input.parse::()?; 72 | } else if key == "alias_name" { 73 | alias_name = Some(input.parse::()?.value()); 74 | input.parse::()?; 75 | } else if key == "field_types" { 76 | field_types = input.parse()?; 77 | input.parse::()?; 78 | } else if key == "field_opts" { 79 | field_opts = input.parse()?; 80 | input.parse::()?; 81 | } else { 82 | let err = format!("unexpected input key: {}", key); 83 | return Err(SynError::new_spanned(key, err)); 84 | } 85 | } 86 | 87 | for (field_name, field_type) in field_types.0 { 88 | let field_opt = field_opts.0.entry(field_name).or_default(); 89 | field_opt.special_type = Some(quote!(#field_type)); 90 | } 91 | 92 | Ok(Self { 93 | code, 94 | nth, 95 | disable_derive_serde_ser, 96 | disable_derive_serde_de, 97 | custom_derive, 98 | alias_name, 99 | field_opts, 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /golang-struct-tag/tests/json.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_struct_tag::{ConventionStructTag, JsonStructTag, JsonStructTagOption, StructTag}; 4 | 5 | #[test] 6 | fn test_parse() -> Result<(), Box> { 7 | let content = fs::read_to_string(PathBuf::new().join("tests/files/json.txt"))?; 8 | for line in content.lines() { 9 | let mut split = line.split("\t"); 10 | let name = split.next().unwrap(); 11 | let str = split.next().unwrap(); 12 | assert!(split.next().is_none()); 13 | 14 | match name { 15 | "A" => assert_eq!( 16 | StructTag::Convention( 17 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 18 | None, 19 | vec![] 20 | ))] 21 | .into_iter() 22 | .collect(), 23 | ), 24 | str.parse()? 25 | ), 26 | "B" => assert_eq!( 27 | StructTag::Convention( 28 | vec![ConventionStructTag::Json(JsonStructTag::Ignored)] 29 | .into_iter() 30 | .collect() 31 | ), 32 | str.parse()? 33 | ), 34 | "C" => assert_eq!( 35 | StructTag::Convention( 36 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 37 | Some("-".to_owned()), 38 | vec![] 39 | ))] 40 | .into_iter() 41 | .collect() 42 | ), 43 | str.parse()? 44 | ), 45 | "D" => assert_eq!( 46 | StructTag::Convention( 47 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 48 | Some("d".to_owned()), 49 | vec![] 50 | ))] 51 | .into_iter() 52 | .collect() 53 | ), 54 | str.parse()? 55 | ), 56 | "E" => assert_eq!( 57 | StructTag::Convention( 58 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 59 | None, 60 | vec![JsonStructTagOption::Omitempty] 61 | ))] 62 | .into_iter() 63 | .collect() 64 | ), 65 | str.parse()? 66 | ), 67 | "F" => assert_eq!( 68 | StructTag::Convention( 69 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 70 | None, 71 | vec![JsonStructTagOption::String] 72 | ))] 73 | .into_iter() 74 | .collect() 75 | ), 76 | str.parse()? 77 | ), 78 | "G" => assert_eq!( 79 | StructTag::Convention( 80 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 81 | Some("g".to_owned()), 82 | vec![JsonStructTagOption::Omitempty] 83 | ))] 84 | .into_iter() 85 | .collect() 86 | ), 87 | str.parse()? 88 | ), 89 | "H" => assert_eq!( 90 | StructTag::Convention( 91 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 92 | Some("h".to_owned()), 93 | vec![JsonStructTagOption::String] 94 | ))] 95 | .into_iter() 96 | .collect() 97 | ), 98 | str.parse()? 99 | ), 100 | "I" => assert_eq!( 101 | StructTag::Convention( 102 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 103 | Some("i".to_owned()), 104 | vec![JsonStructTagOption::Omitempty, JsonStructTagOption::String] 105 | ))] 106 | .into_iter() 107 | .collect() 108 | ), 109 | str.parse()? 110 | ), 111 | "J" => assert_eq!( 112 | StructTag::Convention( 113 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 114 | Some("j".to_owned()), 115 | vec![JsonStructTagOption::String, JsonStructTagOption::Omitempty] 116 | ))] 117 | .into_iter() 118 | .collect() 119 | ), 120 | str.parse()? 121 | ), 122 | "K" => assert_eq!( 123 | StructTag::Convention( 124 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 125 | Some("k".to_owned()), 126 | vec![ 127 | JsonStructTagOption::Unknown("foo".to_owned()), 128 | JsonStructTagOption::Unknown("bar".to_owned()) 129 | ] 130 | ))] 131 | .into_iter() 132 | .collect() 133 | ), 134 | str.parse()? 135 | ), 136 | "L" => assert_eq!( 137 | StructTag::Convention( 138 | vec![ 139 | ConventionStructTag::Json(JsonStructTag::Normal( 140 | Some("l".to_owned()), 141 | vec![] 142 | )), 143 | ConventionStructTag::Unknown("xml".to_owned(), "".to_owned()) 144 | ] 145 | .into_iter() 146 | .collect() 147 | ), 148 | str.parse()? 149 | ), 150 | _ => assert!(false), 151 | } 152 | } 153 | 154 | Ok(()) 155 | } 156 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl/src/gen_json_struct.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! gen_json_struct { 3 | // 4 | ($code:literal) => { 5 | golang_type_decl_macro::gen_json_struct!(code = $code,); 6 | }; 7 | // 8 | ( 9 | $code:literal, 10 | $( $opt_k:ident = $opt_v:literal),+ $(,)? 11 | ) => { 12 | golang_type_decl_macro::gen_json_struct!( 13 | code = $code, 14 | $( $opt_k = $opt_v ,)* 15 | ); 16 | }; 17 | ( 18 | $code:literal; 19 | $( $field_type_name:literal => $field_type:ty ),* $(,)? 20 | ) => { 21 | golang_type_decl_macro::gen_json_struct!( 22 | code = $code, 23 | field_types = $( $field_type_name => $field_type ,)* , 24 | ); 25 | }; 26 | ( 27 | $code:literal; 28 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 29 | ) => { 30 | golang_type_decl_macro::gen_json_struct!( 31 | code = $code, 32 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 33 | ); 34 | }; 35 | // 36 | ( 37 | $code:literal, 38 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 39 | $( $field_type_name:literal => $field_type:ty ),* $(,)? 40 | ) => { 41 | golang_type_decl_macro::gen_json_struct!( 42 | code = $code, 43 | $( $opt_k = $opt_v ,)* 44 | field_types = $( $field_type_name => $field_type ,)* , 45 | ); 46 | }; 47 | ( 48 | $code:literal, 49 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 50 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 51 | ) => { 52 | golang_type_decl_macro::gen_json_struct!( 53 | code = $code, 54 | $( $opt_k = $opt_v ,)* 55 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 56 | ); 57 | }; 58 | ( 59 | $code:literal; 60 | $( $field_type_name:literal => $field_type:ty ),* $(,)?; 61 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 62 | ) => { 63 | golang_type_decl_macro::gen_json_struct!( 64 | code = $code, 65 | field_types = $( $field_type_name => $field_type ,)* , 66 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 67 | ); 68 | }; 69 | // 70 | ( 71 | $code:literal, 72 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 73 | $( $field_type_name:literal => $field_type:ty ),* $(,)?; 74 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 75 | ) => { 76 | golang_type_decl_macro::gen_json_struct!( 77 | code = $code, 78 | $( $opt_k = $opt_v ,)* 79 | field_types = $( $field_type_name => $field_type ,)* , 80 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 81 | ); 82 | }; 83 | } 84 | 85 | #[macro_export] 86 | macro_rules! gen_json_struct_from_file { 87 | // 88 | ($path:literal) => { 89 | golang_type_decl_macro::gen_json_struct!(path = $path,); 90 | }; 91 | // 92 | ( 93 | $path:literal, 94 | $( $opt_k:ident = $opt_v:literal),+ $(,)? 95 | ) => { 96 | golang_type_decl_macro::gen_json_struct!( 97 | path = $path, 98 | $( $opt_k = $opt_v ,)* 99 | ); 100 | }; 101 | ( 102 | $path:literal; 103 | $( $field_type_name:literal => $field_type:ty ),* $(,)? 104 | ) => { 105 | golang_type_decl_macro::gen_json_struct!( 106 | path = $path, 107 | field_types = $( $field_type_name => $field_type ,)* , 108 | ); 109 | }; 110 | ( 111 | $path:literal; 112 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 113 | ) => { 114 | golang_type_decl_macro::gen_json_struct!( 115 | path = $path, 116 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 117 | ); 118 | }; 119 | // 120 | ( 121 | $path:literal, 122 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 123 | $( $field_type_name:literal => $field_type:ty ),* $(,)? 124 | ) => { 125 | golang_type_decl_macro::gen_json_struct!( 126 | path = $path, 127 | $( $opt_k = $opt_v ,)* 128 | field_types = $( $field_type_name => $field_type ,)* , 129 | ); 130 | }; 131 | ( 132 | $path:literal, 133 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 134 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 135 | ) => { 136 | golang_type_decl_macro::gen_json_struct!( 137 | path = $path, 138 | $( $opt_k = $opt_v ,)* 139 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 140 | ); 141 | }; 142 | ( 143 | $path:literal; 144 | $( $field_type_name:literal => $field_type:ty ),* $(,)?; 145 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 146 | ) => { 147 | golang_type_decl_macro::gen_json_struct!( 148 | path = $path, 149 | field_types = $( $field_type_name => $field_type ,)* , 150 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 151 | ); 152 | }; 153 | // 154 | ( 155 | $path:literal, 156 | $( $opt_k:ident = $opt_v:literal ),+ $(,)?; 157 | $( $field_type_name:literal => $field_type:ty ),* $(,)?; 158 | $( $field_opt_name:literal => { $( $field_opt_k:literal : $field_opt_v:tt ),* $(,)? } ),* $(,)? 159 | ) => { 160 | golang_type_decl_macro::gen_json_struct!( 161 | path = $path, 162 | $( $opt_k = $opt_v ,)* 163 | field_types = $( $field_type_name => $field_type ,)* , 164 | field_opts = $( $field_opt_name => $( $field_opt_k -> $field_opt_v ,)* ,)* , 165 | ); 166 | }; 167 | } 168 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/tests/struct_type.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fs, path::PathBuf}; 2 | 3 | use golang_type_core::{ 4 | golang_struct_tag::{ConventionStructTag, StructTag}, 5 | golang_type_name_core::TypeName, 6 | EmbeddedField, FieldDecl, FunctionType, PointerType, SliceType, StructField, StructType, 7 | StructTypeParseError, Type, TypeParseError, 8 | }; 9 | 10 | #[test] 11 | fn test_parse_embedded_field() -> Result<(), Box> { 12 | let content = 13 | fs::read_to_string(PathBuf::new().join("tests/files/struct_type/embedded_field.txt"))?; 14 | 15 | let r#type: Type = content.parse()?; 16 | 17 | assert_eq!( 18 | r#type, 19 | Type::StructType(StructType { 20 | field_decls: vec![ 21 | FieldDecl { 22 | struct_field: StructField::EmbeddedField(EmbeddedField::TypeName( 23 | TypeName::Identifier("T1".to_owned()) 24 | )), 25 | tag: None, 26 | }, 27 | FieldDecl { 28 | struct_field: StructField::EmbeddedField(EmbeddedField::TypeName( 29 | TypeName::Identifier("T2".to_owned()) 30 | )), 31 | tag: None, 32 | }, 33 | FieldDecl { 34 | struct_field: StructField::EmbeddedField(EmbeddedField::TypeName( 35 | TypeName::QualifiedIdent("P".to_owned(), "Duration".to_owned()) 36 | )), 37 | tag: None, 38 | }, 39 | FieldDecl { 40 | struct_field: StructField::EmbeddedField(EmbeddedField::TypeName( 41 | TypeName::QualifiedIdent("P".to_owned(), "Month".to_owned()) 42 | )), 43 | tag: None, 44 | }, 45 | FieldDecl { 46 | struct_field: StructField::IdentifierListType( 47 | vec!["x".to_owned(), "y".to_owned()], 48 | Type::TypeName(TypeName::Int).into() 49 | ), 50 | tag: None, 51 | }, 52 | ] 53 | }) 54 | ); 55 | 56 | Ok(()) 57 | } 58 | 59 | #[test] 60 | fn test_parse_normal() -> Result<(), Box> { 61 | let content = fs::read_to_string(PathBuf::new().join("tests/files/struct_type/normal.txt"))?; 62 | 63 | let r#type: Type = content.parse()?; 64 | 65 | assert_eq!( 66 | r#type, 67 | Type::StructType(StructType { 68 | field_decls: vec![ 69 | FieldDecl { 70 | struct_field: StructField::IdentifierListType( 71 | vec!["x".to_owned(), "y".to_owned()], 72 | Type::TypeName(TypeName::Int).into() 73 | ), 74 | tag: None, 75 | }, 76 | FieldDecl { 77 | struct_field: StructField::IdentifierListType( 78 | vec!["u".to_owned()], 79 | Type::TypeName(TypeName::Float32).into() 80 | ), 81 | tag: None, 82 | }, 83 | FieldDecl { 84 | struct_field: StructField::IdentifierListType( 85 | vec!["_".to_owned()], 86 | Type::TypeName(TypeName::Float32).into() 87 | ), 88 | tag: None, 89 | }, 90 | FieldDecl { 91 | struct_field: StructField::IdentifierListType( 92 | vec!["A".to_owned()], 93 | Type::PointerType( 94 | PointerType( 95 | Type::SliceType(SliceType { 96 | element: Type::TypeName(TypeName::Int).into() 97 | }) 98 | .into() 99 | ) 100 | .into() 101 | ) 102 | .into(), 103 | ), 104 | tag: None, 105 | }, 106 | FieldDecl { 107 | struct_field: StructField::IdentifierListType( 108 | vec!["F".to_owned()], 109 | Type::FunctionType(FunctionType {}).into(), 110 | ), 111 | tag: None, 112 | }, 113 | ] 114 | }) 115 | ); 116 | 117 | Ok(()) 118 | } 119 | 120 | #[test] 121 | fn test_parse_tag() -> Result<(), Box> { 122 | let content = fs::read_to_string(PathBuf::new().join("tests/files/struct_type/tag.txt"))?; 123 | 124 | let r#type: Type = content.parse()?; 125 | 126 | assert_eq!( 127 | r#type, 128 | Type::StructType(StructType { 129 | field_decls: vec![ 130 | FieldDecl { 131 | struct_field: StructField::IdentifierListType( 132 | vec!["microsec".to_owned()], 133 | Type::TypeName(TypeName::Uint64).into(), 134 | ), 135 | tag: Some(StructTag::Convention( 136 | vec![ConventionStructTag::Unknown( 137 | "protobuf".to_owned(), 138 | "1".to_owned() 139 | )] 140 | .into_iter() 141 | .collect() 142 | )), 143 | }, 144 | FieldDecl { 145 | struct_field: StructField::IdentifierListType( 146 | vec!["serverIP6".to_owned()], 147 | Type::TypeName(TypeName::Uint64).into(), 148 | ), 149 | tag: Some(StructTag::Convention( 150 | vec![ConventionStructTag::Unknown( 151 | "protobuf".to_owned(), 152 | "2".to_owned() 153 | )] 154 | .into_iter() 155 | .collect() 156 | )), 157 | }, 158 | ] 159 | }) 160 | ); 161 | 162 | Ok(()) 163 | } 164 | 165 | // TODO 166 | // #[test] 167 | // fn test_parse_with_unexpected_type() -> Result<(), Box> { 168 | // match r#" 169 | // struct { 170 | // map[string]int 171 | // } 172 | // "# 173 | // .parse::() 174 | // { 175 | // Ok(_) => assert!(false), 176 | // Err(TypeParseError::StructTypeParseError(StructTypeParseError::UnexpectedType( 177 | // ref err, 178 | // ))) if err.starts_with("unexpected type ") => {} 179 | // Err(err) => assert!(false, "{:?}", err), 180 | // } 181 | 182 | // Ok(()) 183 | // } 184 | 185 | #[test] 186 | fn test_parse_with_duplicate_field() -> Result<(), Box> { 187 | match r#" 188 | struct { 189 | int 190 | int 191 | } 192 | "# 193 | .parse::() 194 | { 195 | Ok(_) => assert!(false), 196 | Err(TypeParseError::StructTypeParseError(StructTypeParseError::DuplicateField( 197 | ref err, 198 | ))) if err == "duplicate field int" => {} 199 | Err(err) => assert!(false, "{:?}", err), 200 | } 201 | 202 | match r#" 203 | struct { 204 | a int 205 | a uint 206 | } 207 | "# 208 | .parse::() 209 | { 210 | Ok(_) => assert!(false), 211 | Err(TypeParseError::StructTypeParseError(StructTypeParseError::DuplicateField( 212 | ref err, 213 | ))) if err == "duplicate field a" => {} 214 | Err(err) => assert!(false, "{}", err), 215 | } 216 | 217 | Ok(()) 218 | } 219 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use golang_struct_tag; 2 | pub use golang_type_name_core::{self, TypeName, TypeNameParseError}; 3 | 4 | use std::str::{self, FromStr}; 5 | 6 | use golang_parser::{tree_sitter::Node, Parser}; 7 | 8 | pub mod array_type; 9 | pub mod channel_type; 10 | pub mod function_type; 11 | pub mod interface_type; 12 | pub mod map_type; 13 | pub mod parenthesized_type; 14 | pub mod pointer_type; 15 | pub mod slice_type; 16 | pub mod struct_type; 17 | 18 | pub use self::array_type::{ArrayLength, ArrayType, ArrayTypeParseError}; 19 | pub use self::channel_type::{ChannelType, ChannelTypeParseError}; 20 | pub use self::function_type::{FunctionType, FunctionTypeParseError}; 21 | pub use self::interface_type::{InterfaceType, InterfaceTypeParseError}; 22 | pub use self::map_type::{MapType, MapTypeParseError}; 23 | pub use self::parenthesized_type::{ParenthesizedType, ParenthesizedTypeParseError}; 24 | pub use self::pointer_type::{PointerType, PointerTypeParseError}; 25 | pub use self::slice_type::{SliceType, SliceTypeParseError}; 26 | pub use self::struct_type::{ 27 | EmbeddedField, FieldDecl, StructField, StructType, StructTypeParseError, 28 | }; 29 | 30 | #[derive(PartialEq, Eq, Debug, Clone)] 31 | pub enum Type { 32 | TypeName(TypeName), 33 | // 34 | ArrayType(ArrayType), 35 | StructType(StructType), 36 | PointerType(PointerType), 37 | FunctionType(FunctionType), 38 | InterfaceType(InterfaceType), 39 | SliceType(SliceType), 40 | MapType(MapType), 41 | ChannelType(ChannelType), 42 | // 43 | ParenthesizedType(ParenthesizedType), 44 | } 45 | 46 | #[derive(thiserror::Error, Debug)] 47 | pub enum TypeParseError { 48 | #[error("GolangParserError {0:?}")] 49 | GolangParserError(#[from] golang_parser::Error), 50 | #[error("NodeMissing {0}")] 51 | NodeMissing(String), 52 | #[error("NodeKindUnknown {0}")] 53 | NodeKindUnknown(String), 54 | // 55 | // 56 | #[error("TypeNameParseError {0:?}")] 57 | TypeNameParseError(#[from] TypeNameParseError), 58 | // 59 | #[error("ArrayTypeParseError {0:?}")] 60 | ArrayTypeParseError(#[from] ArrayTypeParseError), 61 | #[error("StructTypeParseError {0:?}")] 62 | StructTypeParseError(#[from] StructTypeParseError), 63 | #[error("PointerTypeParseError {0:?}")] 64 | PointerTypeParseError(#[from] PointerTypeParseError), 65 | #[error("FunctionTypeParseError {0:?}")] 66 | FunctionTypeParseError(#[from] FunctionTypeParseError), 67 | #[error("InterfaceTypeParseError {0:?}")] 68 | InterfaceTypeParseError(#[from] InterfaceTypeParseError), 69 | #[error("SliceTypeParseError {0:?}")] 70 | SliceTypeParseError(#[from] SliceTypeParseError), 71 | #[error("MapTypeParseError {0:?}")] 72 | MapTypeParseError(#[from] MapTypeParseError), 73 | #[error("ChannelTypeParseError {0:?}")] 74 | ChannelTypeParseError(#[from] ChannelTypeParseError), 75 | // 76 | #[error("ParenthesizedTypeParseError {0:?}")] 77 | ParenthesizedTypeParseError(#[from] ParenthesizedTypeParseError), 78 | } 79 | 80 | impl FromStr for Type { 81 | type Err = TypeParseError; 82 | 83 | fn from_str(s: &str) -> Result { 84 | let parser = Parser::new(format!("var _ {}", s))?; 85 | let source = parser.get_source(); 86 | let root_node = parser.get_root_node(); 87 | 88 | let mut cursor = root_node.walk(); 89 | let node_var_declaration = root_node 90 | .named_children(&mut cursor) 91 | .find(|node| node.kind() == "var_declaration") 92 | .ok_or_else(|| TypeParseError::NodeMissing("var_declaration".to_string()))?; 93 | let node_var_spec = node_var_declaration 94 | .named_children(&mut cursor) 95 | .find(|node| node.kind() == "var_spec") 96 | .ok_or_else(|| TypeParseError::NodeMissing("var_spec".to_string()))?; 97 | 98 | let _ = node_var_spec 99 | .named_child(0) 100 | .ok_or_else(|| TypeParseError::NodeMissing("var_spec name".to_string()))?; 101 | let node_var_spec_type = node_var_spec 102 | .named_child(1) 103 | .ok_or_else(|| TypeParseError::NodeMissing("var_spec type".to_string()))?; 104 | 105 | Self::from_node(node_var_spec_type, source) 106 | } 107 | } 108 | 109 | impl Type { 110 | pub fn from_node(node: Node, source: &[u8]) -> Result { 111 | match node.kind() { 112 | // 113 | "qualified_type" => TypeName::from_qualified_type_node(node, source) 114 | .map(Self::TypeName) 115 | .map_err(Into::into), 116 | "type_identifier" => TypeName::from_type_identifier_node(node, source) 117 | .map(Self::TypeName) 118 | .map_err(Into::into), 119 | // 120 | "array_type" => ArrayType::from_array_type_node(node, source).map(Self::ArrayType), 121 | "struct_type" => StructType::from_struct_type_node(node, source).map(Self::StructType), 122 | "pointer_type" => { 123 | PointerType::from_pointer_type_node(node, source).map(Self::PointerType) 124 | } 125 | "function_type" => { 126 | FunctionType::from_function_type_node(node, source).map(Self::FunctionType) 127 | } 128 | "interface_type" => { 129 | InterfaceType::from_interface_type_node(node, source).map(Self::InterfaceType) 130 | } 131 | "slice_type" => SliceType::from_slice_type_node(node, source).map(Self::SliceType), 132 | "map_type" => MapType::from_map_type_node(node, source).map(Self::MapType), 133 | "channel_type" => { 134 | ChannelType::from_channel_type_node(node, source).map(Self::ChannelType) 135 | } 136 | // 137 | "parenthesized_type" => ParenthesizedType::from_parenthesized_type_node(node, source) 138 | .map(Self::ParenthesizedType), 139 | _ => Err(TypeParseError::NodeKindUnknown(node.kind().to_owned())), 140 | } 141 | } 142 | } 143 | 144 | #[cfg(feature = "enable-quote-to_tokens")] 145 | mod enable_quote_to_tokens { 146 | use super::Type; 147 | 148 | use proc_macro2::TokenStream; 149 | use quote::{quote, ToTokens, TokenStreamExt as _}; 150 | 151 | impl ToTokens for Type { 152 | fn to_tokens(&self, tokens: &mut TokenStream) { 153 | match self { 154 | // 155 | Self::TypeName(type_name) => tokens.append_all(quote!(#type_name)), 156 | // 157 | Self::ArrayType(array_type) => tokens.append_all(quote!(#array_type)), 158 | Self::StructType(_) => { 159 | let err = "impl ToTokens for StructType is unsupported"; 160 | tokens.append_all(quote!(compile_error!(#err))) 161 | } 162 | Self::PointerType(pointer_type) => tokens.append_all(quote!(#pointer_type)), 163 | Self::FunctionType(_) => { 164 | let err = "impl ToTokens for FunctionType is unsupported"; 165 | tokens.append_all(quote!(compile_error!(#err))) 166 | } 167 | Self::InterfaceType(_) => { 168 | let err = "impl ToTokens for InterfaceType is unsupported"; 169 | tokens.append_all(quote!(compile_error!(#err))) 170 | } 171 | Self::SliceType(slice_type) => tokens.append_all(quote!(#slice_type)), 172 | Self::MapType(map_type) => tokens.append_all(quote!(#map_type)), 173 | Self::ChannelType(_) => { 174 | let err = "impl ToTokens for ChannelType is unsupported"; 175 | tokens.append_all(quote!(compile_error!(#err))) 176 | } 177 | // 178 | Self::ParenthesizedType(parenthesized_type) => { 179 | tokens.append_all(quote!(#parenthesized_type)) 180 | } 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /golang-type/golang-type-core/src/struct_type.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | use golang_parser::tree_sitter::Node; 4 | use golang_struct_tag::{StructTag, StructTagParseError}; 5 | 6 | use crate::{golang_type_name_core::TypeName, PointerType, Type, TypeParseError}; 7 | 8 | #[derive(PartialEq, Eq, Debug, Clone)] 9 | pub struct StructType { 10 | pub field_decls: Vec, 11 | } 12 | 13 | #[derive(PartialEq, Eq, Debug, Clone)] 14 | pub struct FieldDecl { 15 | pub struct_field: StructField, 16 | pub tag: Option, 17 | } 18 | 19 | #[derive(PartialEq, Eq, Debug, Clone)] 20 | pub enum StructField { 21 | IdentifierListType(Vec, Box), 22 | EmbeddedField(EmbeddedField), 23 | } 24 | 25 | #[derive(PartialEq, Eq, Debug, Clone)] 26 | pub enum EmbeddedField { 27 | TypeName(TypeName), 28 | PointerType(TypeName), 29 | } 30 | impl EmbeddedField { 31 | pub fn name(&self) -> String { 32 | match self { 33 | Self::TypeName(type_name) | Self::PointerType(type_name) => { 34 | TypeNameWrapper(type_name).struct_field_name() 35 | } 36 | } 37 | } 38 | pub fn r#type(&self) -> Type { 39 | match self { 40 | Self::TypeName(type_name) | Self::PointerType(type_name) => { 41 | Type::TypeName(type_name.to_owned()) 42 | } 43 | } 44 | } 45 | } 46 | 47 | #[derive(thiserror::Error, Debug)] 48 | pub enum StructTypeParseError { 49 | #[error("NodeMissing {0}")] 50 | NodeMissing(String), 51 | #[error("NodeKindUnknown {0}")] 52 | NodeKindUnknown(String), 53 | #[error("Utf8Error {0:?}")] 54 | Utf8Error(str::Utf8Error), 55 | #[error("UnexpectedType {0}")] 56 | UnexpectedType(String), 57 | #[error("DuplicateField {0}")] 58 | DuplicateField(String), 59 | 60 | #[error("StructTagParseError {0:?}")] 61 | StructTagParseError(#[from] StructTagParseError), 62 | } 63 | impl StructType { 64 | pub(crate) fn from_struct_type_node(node: Node, source: &[u8]) -> Result { 65 | let node_field_declaration_list = node.named_child(0).ok_or_else(|| { 66 | StructTypeParseError::NodeMissing("field_declaration_list".to_string()) 67 | })?; 68 | if node_field_declaration_list.kind() != "field_declaration_list" { 69 | return Err( 70 | StructTypeParseError::NodeMissing("field_declaration_list".to_string()).into(), 71 | ); 72 | } 73 | let mut tree_cursor = node_field_declaration_list.walk(); 74 | 75 | let mut field_decls = vec![]; 76 | let mut non_blank_field_names = vec![]; 77 | 78 | for node_field_declaration in node_field_declaration_list.named_children(&mut tree_cursor) { 79 | match node_field_declaration.kind() { 80 | "field_declaration" => {} 81 | "comment" => continue, 82 | _ => { 83 | return Err(StructTypeParseError::NodeKindUnknown( 84 | node_field_declaration.kind().to_owned(), 85 | ) 86 | .into()) 87 | } 88 | } 89 | 90 | // TODO, try parse 91 | // func() 92 | // map[string]int 93 | // 94 | 95 | let mut i = 0; 96 | let mut node_field_declaration_names = vec![]; 97 | let node_field_declaration_type = loop { 98 | let node_field_declaration_name_or_type = 99 | node_field_declaration.named_child(i).ok_or_else(|| { 100 | StructTypeParseError::NodeMissing( 101 | "field_declaration name or type".to_string(), 102 | ) 103 | })?; 104 | i += 1; 105 | 106 | match node_field_declaration_name_or_type.kind() { 107 | "field_identifier" => { 108 | node_field_declaration_names.push(node_field_declaration_name_or_type); 109 | } 110 | _ => break node_field_declaration_name_or_type, 111 | } 112 | }; 113 | 114 | let r#type = Type::from_node(node_field_declaration_type, source)?; 115 | 116 | let tag = if let Some(node_field_declaration_tag) = 117 | node_field_declaration.named_child(i) 118 | { 119 | match node_field_declaration_tag.kind() { 120 | "raw_string_literal" => Some( 121 | StructTag::from_raw_string_literal_node(node_field_declaration_tag, source) 122 | .map_err(StructTypeParseError::StructTagParseError)?, 123 | ), 124 | "interpreted_string_literal" => Some( 125 | StructTag::from_interpreted_string_literal_node( 126 | node_field_declaration_tag, 127 | source, 128 | ) 129 | .map_err(StructTypeParseError::StructTagParseError)?, 130 | ), 131 | _ => { 132 | return Err(StructTypeParseError::NodeKindUnknown( 133 | node_field_declaration_tag.kind().to_owned(), 134 | ) 135 | .into()) 136 | } 137 | } 138 | } else { 139 | None 140 | }; 141 | 142 | if node_field_declaration_names.is_empty() { 143 | let embedded_field = match &r#type { 144 | Type::TypeName(type_name) => EmbeddedField::TypeName(type_name.to_owned()), 145 | Type::PointerType(PointerType(pointer_type_element)) => { 146 | match **pointer_type_element { 147 | Type::TypeName(ref type_name) => { 148 | EmbeddedField::PointerType(type_name.to_owned()) 149 | } 150 | _ => { 151 | return Err(StructTypeParseError::UnexpectedType(format!( 152 | "unexpected type {:?}", 153 | pointer_type_element 154 | )) 155 | .into()) 156 | } 157 | } 158 | } 159 | _ => { 160 | return Err(StructTypeParseError::UnexpectedType(format!( 161 | "unexpected type {:?}", 162 | &r#type 163 | )) 164 | .into()) 165 | } 166 | }; 167 | 168 | if non_blank_field_names.contains(&embedded_field.name()) { 169 | return Err(StructTypeParseError::DuplicateField(format!( 170 | "duplicate field {}", 171 | &embedded_field.name() 172 | )) 173 | .into()); 174 | } 175 | non_blank_field_names.push(embedded_field.name().to_owned()); 176 | 177 | let field_decl = FieldDecl { 178 | struct_field: StructField::EmbeddedField(embedded_field), 179 | tag, 180 | }; 181 | field_decls.push(field_decl); 182 | } else { 183 | let mut names = vec![]; 184 | for node_field_declaration_name in node_field_declaration_names { 185 | let name = node_field_declaration_name 186 | .utf8_text(source) 187 | .map_err(StructTypeParseError::Utf8Error)? 188 | .to_owned(); 189 | 190 | if name != "_" { 191 | if non_blank_field_names.contains(&name) { 192 | return Err(StructTypeParseError::DuplicateField(format!( 193 | "duplicate field {}", 194 | name 195 | )) 196 | .into()); 197 | } 198 | non_blank_field_names.push(name.to_owned()); 199 | } 200 | 201 | names.push(name); 202 | } 203 | 204 | let field_decl = FieldDecl { 205 | struct_field: StructField::IdentifierListType(names, r#type.into()), 206 | tag: tag.to_owned(), 207 | }; 208 | field_decls.push(field_decl); 209 | } 210 | } 211 | 212 | Ok(Self { field_decls }) 213 | } 214 | } 215 | 216 | // 217 | // 218 | // 219 | trait StructFieldName { 220 | fn struct_field_name(&self) -> String; 221 | } 222 | struct TypeNameWrapper<'a>(&'a TypeName); 223 | impl StructFieldName for TypeNameWrapper<'_> { 224 | fn struct_field_name(&self) -> String { 225 | match self.0 { 226 | TypeName::QualifiedIdent(_, identifier_str) => identifier_str.to_owned(), 227 | _ => self.0.name(), 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /golang-struct-tag/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::PartialEq, 3 | collections::HashSet, 4 | hash::{Hash, Hasher}, 5 | str::{self, FromStr}, 6 | }; 7 | 8 | use golang_parser::{tree_sitter::Node, Parser}; 9 | use pest::{iterators::Pairs, Parser as _}; 10 | 11 | // https://github.com/pest-parser/pest/issues/490#issuecomment-808942497 12 | #[allow(clippy::upper_case_acronyms)] 13 | pub(crate) mod convention_struct_tag_parser; 14 | pub mod json; 15 | 16 | use self::convention_struct_tag_parser::{ConventionStructTagParser, Rule}; 17 | pub use self::json::{JsonStructTag, JsonStructTagOption}; 18 | 19 | // 20 | // 21 | // 22 | #[derive(PartialEq, Eq, Debug, Clone)] 23 | pub enum StructTag { 24 | RawStringLiteral(String), 25 | InterpretedStringLiteral(String), 26 | Convention(HashSet), 27 | } 28 | #[derive(Eq, Debug, Clone)] 29 | pub enum ConventionStructTag { 30 | Json(JsonStructTag), 31 | Unknown(String, String), 32 | } 33 | impl PartialEq for ConventionStructTag { 34 | fn eq(&self, other: &Self) -> bool { 35 | match (self, other) { 36 | (Self::Json(_), Self::Json(_)) => true, 37 | (Self::Unknown(key, _), Self::Unknown(other_key, _)) => key == other_key, 38 | _ => false, 39 | } 40 | } 41 | } 42 | impl Hash for ConventionStructTag { 43 | fn hash(&self, state: &mut H) { 44 | match self { 45 | Self::Json(_) => "json".hash(state), 46 | Self::Unknown(key, _) => key.hash(state), 47 | } 48 | } 49 | } 50 | impl StructTag { 51 | pub fn as_json_struct_tag(&self) -> Option<&JsonStructTag> { 52 | match self { 53 | Self::Convention(set) => match set 54 | .iter() 55 | .find(|x| x == &&ConventionStructTag::Json(JsonStructTag::Ignored)) 56 | { 57 | Some(ConventionStructTag::Json(x)) => Some(x), 58 | _ => None, 59 | }, 60 | _ => None, 61 | } 62 | } 63 | } 64 | 65 | #[derive(thiserror::Error, Debug)] 66 | pub enum StructTagParseError { 67 | #[error("GolangParserError {0:?}")] 68 | GolangParserError(#[from] golang_parser::Error), 69 | #[error("NodeMissing {0}")] 70 | NodeMissing(String), 71 | #[error("NodeKindUnknown {0}")] 72 | NodeKindUnknown(String), 73 | #[error("Utf8Error {0:?}")] 74 | Utf8Error(str::Utf8Error), 75 | #[error("Unknown")] 76 | Unknown, 77 | } 78 | 79 | impl FromStr for StructTag { 80 | type Err = StructTagParseError; 81 | 82 | fn from_str(s: &str) -> Result { 83 | let parser = Parser::new(format!("type _ struct {{ string {} }};", s))?; 84 | let source = parser.get_source(); 85 | let root_node = parser.get_root_node(); 86 | 87 | let mut cursor = root_node.walk(); 88 | 89 | let node_type_declaration = root_node 90 | .named_children(&mut cursor) 91 | .find(|node| node.kind() == "type_declaration") 92 | .ok_or_else(|| StructTagParseError::NodeMissing("type_declaration".to_string()))?; 93 | let node_type_spec = node_type_declaration 94 | .named_children(&mut cursor) 95 | .find(|node| node.kind() == "type_spec") 96 | .ok_or_else(|| StructTagParseError::NodeMissing("type_spec".to_string()))?; 97 | let _ = node_type_spec 98 | .named_child(0) 99 | .ok_or_else(|| StructTagParseError::NodeMissing("type_spec name".to_string()))?; 100 | let node_struct_type = node_type_spec 101 | .named_child(1) 102 | .ok_or_else(|| StructTagParseError::NodeMissing("type_spec type".to_string()))?; 103 | let node_field_declaration_list = node_struct_type 104 | .named_children(&mut cursor) 105 | .find(|node| node.kind() == "field_declaration_list") 106 | .ok_or_else(|| { 107 | StructTagParseError::NodeMissing("field_declaration_list".to_string()) 108 | })?; 109 | let node_field_declaration = node_field_declaration_list 110 | .named_children(&mut cursor) 111 | .find(|node| node.kind() == "field_declaration") 112 | .ok_or_else(|| StructTagParseError::NodeMissing("field_declaration".to_string()))?; 113 | let _ = node_field_declaration.named_child(0).ok_or_else(|| { 114 | StructTagParseError::NodeMissing("field_declaration type".to_string()) 115 | })?; 116 | let node_field_declaration_tag = node_field_declaration 117 | .named_child(1) 118 | .ok_or_else(|| StructTagParseError::NodeMissing("field_declaration tag".to_string()))?; 119 | 120 | match node_field_declaration_tag.kind() { 121 | "raw_string_literal" => { 122 | Self::from_raw_string_literal_node(node_field_declaration_tag, source) 123 | } 124 | "interpreted_string_literal" => { 125 | Self::from_interpreted_string_literal_node(node_field_declaration_tag, source) 126 | } 127 | _ => Err(StructTagParseError::NodeKindUnknown( 128 | node_field_declaration_tag.kind().to_owned(), 129 | )), 130 | } 131 | } 132 | } 133 | 134 | impl StructTag { 135 | pub fn from_raw_string_literal_node( 136 | node: Node, 137 | source: &[u8], 138 | ) -> Result { 139 | let s = node 140 | .utf8_text(source) 141 | .map_err(StructTagParseError::Utf8Error)?; 142 | 143 | match ConventionStructTagParser::parse(Rule::tag, s) { 144 | Ok(pairs) => ConventionStructTag::from_pairs(pairs) 145 | .map(|x| Self::Convention(x.into_iter().collect())), 146 | Err(_) => Ok(Self::RawStringLiteral(s.to_owned())), 147 | } 148 | } 149 | 150 | pub fn from_interpreted_string_literal_node( 151 | node: Node, 152 | source: &[u8], 153 | ) -> Result { 154 | let s = node 155 | .utf8_text(source) 156 | .map_err(StructTagParseError::Utf8Error)?; 157 | 158 | Ok(Self::InterpretedStringLiteral(s.to_owned())) 159 | } 160 | } 161 | 162 | impl ConventionStructTag { 163 | fn from_pairs(mut pairs: Pairs<'_, Rule>) -> Result, StructTagParseError> { 164 | let pair = pairs.next().ok_or(StructTagParseError::Unknown)?; 165 | 166 | match pair.as_rule() { 167 | Rule::tag => pair 168 | .into_inner() 169 | .map(|pair| { 170 | let pair = pair 171 | .into_inner() 172 | .next() 173 | .ok_or(StructTagParseError::Unknown)?; 174 | 175 | match pair.as_rule() { 176 | Rule::json => { 177 | JsonStructTag::from_json_pairs(pair.into_inner()).map(Self::Json) 178 | } 179 | Rule::other => { 180 | let mut pairs = pair.into_inner(); 181 | let key = pairs.next().ok_or(StructTagParseError::Unknown)?; 182 | let value = pairs.next().ok_or(StructTagParseError::Unknown)?; 183 | Ok(Self::Unknown( 184 | key.as_str().to_owned(), 185 | value.as_str().to_owned(), 186 | )) 187 | } 188 | _ => Err(StructTagParseError::Unknown), 189 | } 190 | }) 191 | .collect(), 192 | 193 | _ => Err(StructTagParseError::Unknown), 194 | } 195 | } 196 | } 197 | 198 | #[cfg(test)] 199 | mod tests { 200 | use super::*; 201 | 202 | use std::error; 203 | 204 | #[test] 205 | fn test_parse() -> Result<(), Box> { 206 | assert_eq!( 207 | StructTag::RawStringLiteral(r#"`foo"bar`"#.to_owned()), 208 | r#"`foo"bar`"#.parse()? 209 | ); 210 | 211 | assert_eq!( 212 | StructTag::InterpretedStringLiteral(r#""foo`bar""#.to_owned()), 213 | r#""foo`bar""#.parse()? 214 | ); 215 | 216 | assert_eq!( 217 | StructTag::Convention( 218 | vec![ConventionStructTag::Unknown( 219 | "foo".to_owned(), 220 | "bar".to_owned() 221 | )] 222 | .into_iter() 223 | .collect() 224 | ), 225 | r#"`foo:"bar"`"#.parse()? 226 | ); 227 | 228 | Ok(()) 229 | } 230 | 231 | #[test] 232 | fn test_json_struct_tag() { 233 | assert_eq!( 234 | StructTag::Convention( 235 | vec![ConventionStructTag::Json(JsonStructTag::Ignored)] 236 | .into_iter() 237 | .collect(), 238 | ) 239 | .as_json_struct_tag(), 240 | Some(&JsonStructTag::Ignored) 241 | ); 242 | 243 | assert_eq!( 244 | StructTag::Convention( 245 | vec![ConventionStructTag::Json(JsonStructTag::Normal( 246 | Some("foo".to_owned()), 247 | vec![] 248 | ))] 249 | .into_iter() 250 | .collect(), 251 | ) 252 | .as_json_struct_tag(), 253 | Some(&JsonStructTag::Normal(Some("foo".to_owned()), vec![])) 254 | ); 255 | 256 | assert_eq!( 257 | StructTag::Convention( 258 | vec![ConventionStructTag::Unknown( 259 | "foo".to_owned(), 260 | "bar".to_owned(), 261 | )] 262 | .into_iter() 263 | .collect(), 264 | ) 265 | .as_json_struct_tag(), 266 | None 267 | ); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /golang-type-decl/golang-type-decl-core/src/type_def/json_struct.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use convert_case::{Case, Casing as _}; 4 | use golang_type_core::{ 5 | golang_struct_tag::{JsonStructTag, JsonStructTagOption}, 6 | StructField, StructType, Type, TypeName, 7 | }; 8 | use proc_macro2::{Punct, Spacing, TokenStream}; 9 | use quote::{format_ident, quote, ToTokens, TokenStreamExt as _}; 10 | 11 | pub struct JsonStruct { 12 | pub name: String, 13 | pub struct_type: StructType, 14 | pub opt: JsonStructOption, 15 | pub field_opts: HashMap, 16 | } 17 | 18 | #[derive(Default, Debug)] 19 | pub struct JsonStructOption { 20 | pub enable_derive_serde_ser: bool, 21 | pub enable_derive_serde_de: bool, 22 | pub custom_derive: Vec, 23 | // 24 | pub alias_name: Option, 25 | } 26 | impl JsonStructOption { 27 | fn has_derive(&self) -> bool { 28 | self.enable_derive_serde_ser 29 | || self.enable_derive_serde_de 30 | || !self.custom_derive.is_empty() 31 | } 32 | 33 | fn has_serde_derive(&self) -> bool { 34 | self.enable_derive_serde_ser || self.enable_derive_serde_de 35 | } 36 | } 37 | 38 | pub type JsonStructFieldName = String; 39 | 40 | #[derive(Default, Debug, Clone)] 41 | pub struct JsonStructFieldOption { 42 | pub special_type: Option, 43 | // 44 | pub attr_serde_deserialize_with: Option, 45 | pub box_type: bool, 46 | } 47 | 48 | impl ToTokens for JsonStruct { 49 | fn to_tokens(&self, tokens: &mut TokenStream) { 50 | let struct_name = format_ident!( 51 | "{}", 52 | self.opt 53 | .alias_name 54 | .to_owned() 55 | .unwrap_or_else(|| self.name.to_case(Case::Pascal)) 56 | ); 57 | let struct_fields: Vec<_> = self 58 | .struct_type 59 | .field_decls 60 | .iter() 61 | .map(|field_decl| { 62 | let as_json_struct_tag = if let Some(tag) = &field_decl.tag { 63 | tag.as_json_struct_tag() 64 | } else { 65 | None 66 | }; 67 | 68 | let is_ignored = if let Some(JsonStructTag::Ignored) = as_json_struct_tag { 69 | Some(true) 70 | } else { 71 | None 72 | }; 73 | 74 | let rename = if let Some(JsonStructTag::Normal(rename, _)) = as_json_struct_tag { 75 | rename.to_owned() 76 | } else { 77 | None 78 | }; 79 | let is_omitempty = 80 | if let Some(JsonStructTag::Normal(_, options)) = as_json_struct_tag { 81 | Some(options.contains(&JsonStructTagOption::Omitempty)) 82 | } else { 83 | None 84 | }; 85 | let is_string = if let Some(JsonStructTag::Normal(_, options)) = as_json_struct_tag 86 | { 87 | Some(options.contains(&JsonStructTagOption::String)) 88 | } else { 89 | None 90 | }; 91 | 92 | match &field_decl.struct_field { 93 | StructField::IdentifierListType(names, r#type) => names 94 | .iter() 95 | .filter(|x| x != &"_") 96 | .map(|name| { 97 | let field_opt = self 98 | .field_opts 99 | .get(name) 100 | .map(ToOwned::to_owned) 101 | .unwrap_or_default(); 102 | 103 | let field_name = format_ident!("r#{}", name.to_case(Case::Snake)); 104 | let field_type = JsonStructFieldType { 105 | r#type: *r#type.to_owned(), 106 | is_ignored, 107 | is_string, 108 | is_omitempty, 109 | special_type: field_opt.special_type, 110 | box_type: field_opt.box_type, 111 | }; 112 | 113 | if self.opt.has_serde_derive() { 114 | let field_serde_attr = JsonStructFieldSerdeAttr { 115 | rename: rename.to_owned().unwrap_or_else(|| name.to_owned()), 116 | is_ignored, 117 | is_omitempty, 118 | attr_serde_deserialize_with: field_opt 119 | .attr_serde_deserialize_with, 120 | enable_serde_ser: self.opt.enable_derive_serde_ser, 121 | enable_serde_de: self.opt.enable_derive_serde_de, 122 | }; 123 | 124 | quote! { 125 | #[serde(#field_serde_attr)] 126 | pub #field_name: #field_type, 127 | } 128 | } else { 129 | quote! { 130 | pub #field_name: #field_type, 131 | } 132 | } 133 | }) 134 | .collect(), 135 | StructField::EmbeddedField(embedded_field) => { 136 | let name = embedded_field.name(); 137 | 138 | let field_opt = self 139 | .field_opts 140 | .get(&name) 141 | .map(ToOwned::to_owned) 142 | .unwrap_or_default(); 143 | 144 | let field_name = format_ident!("r#{}", name.to_case(Case::Snake)); 145 | let field_type = JsonStructFieldType { 146 | r#type: embedded_field.r#type(), 147 | is_ignored, 148 | is_string, 149 | is_omitempty, 150 | special_type: field_opt.special_type, 151 | box_type: field_opt.box_type, 152 | }; 153 | 154 | let token = if self.opt.has_serde_derive() { 155 | let field_serde_attr = JsonStructFieldSerdeAttr { 156 | rename: rename.unwrap_or_else(|| name.to_owned()), 157 | is_ignored, 158 | is_omitempty, 159 | attr_serde_deserialize_with: field_opt.attr_serde_deserialize_with, 160 | enable_serde_ser: self.opt.enable_derive_serde_ser, 161 | enable_serde_de: self.opt.enable_derive_serde_de, 162 | }; 163 | 164 | quote! { 165 | #[serde(#field_serde_attr)] 166 | pub #field_name: #field_type, 167 | } 168 | } else { 169 | quote! { 170 | pub #field_name: #field_type, 171 | } 172 | }; 173 | 174 | vec![token] 175 | } 176 | } 177 | }) 178 | .flatten() 179 | .collect(); 180 | 181 | let token = if self.opt.has_derive() { 182 | let derive_attr = JsonStructSerdeDeriveAttr { 183 | enable_serde_ser: self.opt.enable_derive_serde_ser, 184 | enable_serde_de: self.opt.enable_derive_serde_de, 185 | custom: self.opt.custom_derive.to_owned(), 186 | }; 187 | 188 | quote! { 189 | #[derive(#derive_attr)] 190 | pub struct #struct_name { 191 | #(#struct_fields)* 192 | } 193 | } 194 | } else { 195 | quote! { 196 | pub struct #struct_name { 197 | #(#struct_fields)* 198 | } 199 | } 200 | }; 201 | 202 | tokens.append_all(token); 203 | } 204 | } 205 | 206 | struct JsonStructSerdeDeriveAttr { 207 | enable_serde_ser: bool, 208 | enable_serde_de: bool, 209 | custom: Vec, 210 | } 211 | impl ToTokens for JsonStructSerdeDeriveAttr { 212 | fn to_tokens(&self, tokens: &mut TokenStream) { 213 | if self.enable_serde_de { 214 | tokens.append_all(quote!(::serde::Deserialize)); 215 | tokens.append(Punct::new(',', Spacing::Alone)); 216 | } 217 | 218 | if self.enable_serde_ser { 219 | tokens.append_all(quote!(::serde::Serialize)); 220 | tokens.append(Punct::new(',', Spacing::Alone)); 221 | } 222 | 223 | for custom in &self.custom { 224 | let custom = format_ident!("{}", custom); 225 | tokens.append_all(quote!(#custom)); 226 | tokens.append(Punct::new(',', Spacing::Alone)); 227 | } 228 | } 229 | } 230 | 231 | struct JsonStructFieldSerdeAttr { 232 | rename: String, 233 | is_ignored: Option, 234 | is_omitempty: Option, 235 | attr_serde_deserialize_with: Option, 236 | enable_serde_ser: bool, 237 | enable_serde_de: bool, 238 | } 239 | impl ToTokens for JsonStructFieldSerdeAttr { 240 | fn to_tokens(&self, tokens: &mut TokenStream) { 241 | tokens.append(format_ident!("rename")); 242 | tokens.append(Punct::new('=', Spacing::Alone)); 243 | let rename = &self.rename; 244 | tokens.append_all(quote!(#rename)); 245 | 246 | if self.is_ignored == Some(true) { 247 | tokens.append(Punct::new(',', Spacing::Alone)); 248 | 249 | tokens.append(format_ident!("default")); 250 | 251 | if self.enable_serde_ser { 252 | tokens.append(Punct::new(',', Spacing::Alone)); 253 | 254 | tokens.append(format_ident!("skip_serializing")); 255 | } 256 | } else if self.is_omitempty == Some(true) { 257 | tokens.append(Punct::new(',', Spacing::Alone)); 258 | 259 | tokens.append(format_ident!("default")); 260 | 261 | if self.enable_serde_ser { 262 | tokens.append(Punct::new(',', Spacing::Alone)); 263 | 264 | tokens.append(format_ident!("skip_serializing_if")); 265 | tokens.append(Punct::new('=', Spacing::Alone)); 266 | let skip_serializing_if_val = "Option::is_none"; 267 | tokens.append_all(quote!(#skip_serializing_if_val)); 268 | } 269 | } 270 | 271 | if let Some(serde_deserialize_with) = &self.attr_serde_deserialize_with { 272 | if self.enable_serde_de { 273 | tokens.append(Punct::new(',', Spacing::Alone)); 274 | 275 | tokens.append(format_ident!("deserialize_with")); 276 | tokens.append(Punct::new('=', Spacing::Alone)); 277 | tokens.append_all(quote!(#serde_deserialize_with)); 278 | } 279 | } 280 | } 281 | } 282 | 283 | struct JsonStructFieldType { 284 | r#type: Type, 285 | is_ignored: Option, 286 | is_string: Option, 287 | is_omitempty: Option, 288 | special_type: Option, 289 | box_type: bool, 290 | } 291 | impl ToTokens for JsonStructFieldType { 292 | fn to_tokens(&self, tokens: &mut TokenStream) { 293 | let mut token = if let Some(special_type) = &self.special_type { 294 | special_type.to_owned() 295 | } else { 296 | let r#type = &self.r#type; 297 | let mut token = if self.box_type { 298 | quote!(Box<#r#type>) 299 | } else { 300 | quote!(#r#type) 301 | }; 302 | 303 | if self.is_string == Some(true) { 304 | let r#type = Type::TypeName(TypeName::String); 305 | token = quote!(#r#type); 306 | } 307 | 308 | token 309 | }; 310 | 311 | if self.is_ignored == Some(true) || self.is_omitempty == Some(true) { 312 | let mut tokens_tmp = TokenStream::new(); 313 | tokens_tmp.append_all(quote!(::core::option::Option)); 314 | tokens_tmp.append(Punct::new('<', Spacing::Alone)); 315 | tokens_tmp.append_all(token); 316 | tokens_tmp.append(Punct::new('>', Spacing::Alone)); 317 | token = tokens_tmp; 318 | } 319 | 320 | tokens.append_all(token); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /golang-type-name/golang-type-name-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::str::{self, FromStr}; 2 | 3 | use golang_parser::{tree_sitter::Node, Parser}; 4 | 5 | #[derive(PartialEq, Eq, Debug, Clone)] 6 | pub enum TypeName { 7 | // https://golang.org/ref/spec#Boolean_types 8 | // https://github.com/golang/go/blob/go1.16.3/src/builtin/builtin.go#L13-L14 9 | Bool, 10 | // https://golang.org/ref/spec#Numeric_types 11 | // https://github.com/golang/go/blob/go1.16.3/src/builtin/builtin.go#L22-L66 12 | // https://github.com/golang/go/blob/go1.16.3/src/builtin/builtin.go#L73-L92 13 | Uint8, 14 | Uint16, 15 | Uint32, 16 | Uint64, 17 | Int8, 18 | Int16, 19 | Int32, 20 | Int64, 21 | Float32, 22 | Float64, 23 | Complex64, 24 | Complex128, 25 | Byte, 26 | Rune, 27 | Uint, 28 | Int, 29 | Uintptr, 30 | // https://golang.org/ref/spec#String_types 31 | // https://github.com/golang/go/blob/go1.16.3/src/builtin/builtin.go#L68-L71 32 | String, 33 | // https://golang.org/ref/spec#QualifiedIdent 34 | QualifiedIdent(PackageName, String), 35 | // 36 | Identifier(String), 37 | } 38 | pub type PackageName = String; 39 | 40 | #[derive(thiserror::Error, Debug)] 41 | pub enum TypeNameParseError { 42 | #[error("GolangParserError {0:?}")] 43 | GolangParserError(#[from] golang_parser::Error), 44 | #[error("NodeMissing {0}")] 45 | NodeMissing(String), 46 | #[error("NodeKindUnknown {0}")] 47 | NodeKindUnknown(String), 48 | #[error("Utf8Error {0:?}")] 49 | Utf8Error(str::Utf8Error), 50 | #[error("IdentifierMissing")] 51 | IdentifierMissing, 52 | } 53 | 54 | impl FromStr for TypeName { 55 | type Err = TypeNameParseError; 56 | 57 | fn from_str(s: &str) -> Result { 58 | let parser = Parser::new(format!("var _ {}", s))?; 59 | let source = parser.get_source(); 60 | let root_node = parser.get_root_node(); 61 | 62 | let mut cursor = root_node.walk(); 63 | 64 | let node_var_declaration = root_node 65 | .named_children(&mut cursor) 66 | .find(|node| node.kind() == "var_declaration") 67 | .ok_or_else(|| TypeNameParseError::NodeMissing("var_declaration".to_string()))?; 68 | let node_var_spec = node_var_declaration 69 | .named_children(&mut cursor) 70 | .find(|node| node.kind() == "var_spec") 71 | .ok_or_else(|| TypeNameParseError::NodeMissing("var_spec".to_string()))?; 72 | 73 | let _ = node_var_spec 74 | .named_child(0) 75 | .ok_or_else(|| TypeNameParseError::NodeMissing("var_spec name".to_string()))?; 76 | let node_var_spec_type = node_var_spec 77 | .named_child(1) 78 | .ok_or_else(|| TypeNameParseError::NodeMissing("var_spec type".to_string()))?; 79 | 80 | match node_var_spec_type.kind() { 81 | "qualified_type" => Self::from_qualified_type_node(node_var_spec_type, source), 82 | "type_identifier" => Self::from_type_identifier_node(node_var_spec_type, source), 83 | _ => Err(TypeNameParseError::NodeKindUnknown( 84 | node_var_spec_type.kind().to_owned(), 85 | )), 86 | } 87 | } 88 | } 89 | 90 | impl TypeName { 91 | pub fn from_qualified_type_node(node: Node, source: &[u8]) -> Result { 92 | let node_qualified_type_package = node 93 | .named_child(0) 94 | .ok_or_else(|| TypeNameParseError::NodeMissing("qualified_type package".to_string()))?; 95 | let node_qualified_type_name = node 96 | .named_child(1) 97 | .ok_or_else(|| TypeNameParseError::NodeMissing("qualified_type name".to_string()))?; 98 | 99 | let package_str = node_qualified_type_package 100 | .utf8_text(source) 101 | .map_err(TypeNameParseError::Utf8Error)?; 102 | let name_str = node_qualified_type_name 103 | .utf8_text(source) 104 | .map_err(TypeNameParseError::Utf8Error)?; 105 | 106 | Ok(Self::QualifiedIdent( 107 | package_str.to_owned(), 108 | name_str.to_owned(), 109 | )) 110 | } 111 | 112 | pub fn from_type_identifier_node( 113 | node: Node, 114 | source: &[u8], 115 | ) -> Result { 116 | let s = node 117 | .utf8_text(source) 118 | .map_err(TypeNameParseError::Utf8Error)?; 119 | 120 | if s.is_empty() { 121 | return Err(TypeNameParseError::IdentifierMissing); 122 | } 123 | 124 | match s { 125 | // 126 | "bool" => Ok(Self::Bool), 127 | // 128 | "uint8" => Ok(Self::Uint8), 129 | "uint16" => Ok(Self::Uint16), 130 | "uint32" => Ok(Self::Uint32), 131 | "uint64" => Ok(Self::Uint64), 132 | "int8" => Ok(Self::Int8), 133 | "int16" => Ok(Self::Int16), 134 | "int32" => Ok(Self::Int32), 135 | "int64" => Ok(Self::Int64), 136 | "float32" => Ok(Self::Float32), 137 | "float64" => Ok(Self::Float64), 138 | "complex64" => Ok(Self::Complex64), 139 | "complex128" => Ok(Self::Complex128), 140 | "byte" => Ok(Self::Byte), 141 | "rune" => Ok(Self::Rune), 142 | "uint" => Ok(Self::Uint), 143 | "int" => Ok(Self::Int), 144 | "uintptr" => Ok(Self::Uintptr), 145 | // 146 | "string" => Ok(Self::String), 147 | // 148 | _ => Ok(Self::Identifier(s.to_owned())), 149 | } 150 | } 151 | 152 | pub fn name(&self) -> String { 153 | match self { 154 | Self::Bool => "bool".to_owned(), 155 | Self::Uint8 => "uint8".to_owned(), 156 | Self::Uint16 => "uint16".to_owned(), 157 | Self::Uint32 => "uint32".to_owned(), 158 | Self::Uint64 => "uint64".to_owned(), 159 | Self::Int8 => "int8".to_owned(), 160 | Self::Int16 => "int16".to_owned(), 161 | Self::Int32 => "int32".to_owned(), 162 | Self::Int64 => "int64".to_owned(), 163 | Self::Float32 => "float32".to_owned(), 164 | Self::Float64 => "float64".to_owned(), 165 | Self::Complex64 => "complex64".to_owned(), 166 | Self::Complex128 => "complex128".to_owned(), 167 | Self::Byte => "byte".to_owned(), 168 | Self::Rune => "rune".to_owned(), 169 | Self::Uint => "uint".to_owned(), 170 | Self::Int => "int".to_owned(), 171 | Self::Uintptr => "uintptr".to_owned(), 172 | Self::String => "string".to_owned(), 173 | Self::QualifiedIdent(package_str, identifier_str) => { 174 | format!("{}.{}", package_str, identifier_str) 175 | } 176 | Self::Identifier(identifier_str) => identifier_str.to_owned(), 177 | } 178 | } 179 | } 180 | 181 | #[cfg(feature = "enable-quote-to_tokens")] 182 | mod enable_quote_to_tokens { 183 | use super::TypeName; 184 | 185 | use proc_macro2::{Punct, Spacing, TokenStream}; 186 | use quote::{format_ident, quote, ToTokens, TokenStreamExt as _}; 187 | 188 | impl ToTokens for TypeName { 189 | fn to_tokens(&self, tokens: &mut TokenStream) { 190 | match self { 191 | Self::Bool => tokens.append_all(quote!(::core::primitive::bool)), 192 | Self::Uint8 | Self::Byte => tokens.append_all(quote!(::core::primitive::u8)), 193 | Self::Uint16 => tokens.append_all(quote!(::core::primitive::u16)), 194 | Self::Uint32 => tokens.append_all(quote!(::core::primitive::u32)), 195 | Self::Uint64 => tokens.append_all(quote!(::core::primitive::u64)), 196 | Self::Int8 => tokens.append_all(quote!(::core::primitive::i8)), 197 | Self::Int16 => tokens.append_all(quote!(::core::primitive::i16)), 198 | Self::Int32 | Self::Rune => tokens.append_all(quote!(::core::primitive::i32)), 199 | Self::Int64 => tokens.append_all(quote!(::core::primitive::i64)), 200 | Self::Float32 => tokens.append_all(quote!(::core::primitive::f32)), 201 | Self::Float64 => tokens.append_all(quote!(::core::primitive::f64)), 202 | Self::Complex64 => tokens.append_all(quote!(::num_complex::Complex32)), 203 | Self::Complex128 => tokens.append_all(quote!(::num_complex::Complex64)), 204 | Self::Uint => tokens.append_all(quote!(::core::primitive::usize)), 205 | Self::Int => tokens.append_all(quote!(::core::primitive::isize)), 206 | Self::Uintptr => tokens.append_all(quote!(::core::primitive::usize)), 207 | Self::String => tokens.append_all(quote!(::std::string::String)), 208 | Self::QualifiedIdent(package_str, identifier_str) => { 209 | let package_ident = format_ident!("{}", package_str); 210 | let identifier_ident = format_ident!("{}", identifier_str); 211 | 212 | tokens.append(Punct::new(':', Spacing::Joint)); 213 | tokens.append(Punct::new(':', Spacing::Alone)); 214 | tokens.append_all(quote!(#package_ident)); 215 | tokens.append(Punct::new(':', Spacing::Joint)); 216 | tokens.append(Punct::new(':', Spacing::Alone)); 217 | tokens.append_all(quote!(#identifier_ident)); 218 | } 219 | Self::Identifier(identifier_str) => { 220 | let identifier_ident = format_ident!("{}", identifier_str); 221 | 222 | tokens.append_all(quote!(#identifier_ident)); 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | #[cfg(test)] 230 | mod tests { 231 | use super::*; 232 | 233 | use std::{error, fs, path::PathBuf}; 234 | 235 | #[test] 236 | fn test_parse() -> Result<(), Box> { 237 | let content = fs::read_to_string(PathBuf::new().join("tests/files/type_names.txt"))?; 238 | for (i, str) in content.lines().enumerate() { 239 | match i + 1 { 240 | // 241 | 1 => { 242 | assert_eq!(TypeName::Bool, str.parse()?); 243 | assert_eq!(TypeName::Bool.name(), str) 244 | } 245 | // 246 | 2 => { 247 | assert_eq!(TypeName::Uint8, str.parse()?); 248 | assert_eq!(TypeName::Uint8.name(), str) 249 | } 250 | 3 => { 251 | assert_eq!(TypeName::Uint16, str.parse()?); 252 | assert_eq!(TypeName::Uint16.name(), str) 253 | } 254 | 4 => { 255 | assert_eq!(TypeName::Uint32, str.parse()?); 256 | assert_eq!(TypeName::Uint32.name(), str) 257 | } 258 | 5 => { 259 | assert_eq!(TypeName::Uint64, str.parse()?); 260 | assert_eq!(TypeName::Uint64.name(), str) 261 | } 262 | 6 => { 263 | assert_eq!(TypeName::Int8, str.parse()?); 264 | assert_eq!(TypeName::Int8.name(), str) 265 | } 266 | 7 => { 267 | assert_eq!(TypeName::Int16, str.parse()?); 268 | assert_eq!(TypeName::Int16.name(), str) 269 | } 270 | 8 => { 271 | assert_eq!(TypeName::Int32, str.parse()?); 272 | assert_eq!(TypeName::Int32.name(), str) 273 | } 274 | 9 => { 275 | assert_eq!(TypeName::Int64, str.parse()?); 276 | assert_eq!(TypeName::Int64.name(), str) 277 | } 278 | 10 => { 279 | assert_eq!(TypeName::Float32, str.parse()?); 280 | assert_eq!(TypeName::Float32.name(), str) 281 | } 282 | 11 => { 283 | assert_eq!(TypeName::Float64, str.parse()?); 284 | assert_eq!(TypeName::Float64.name(), str) 285 | } 286 | 12 => { 287 | assert_eq!(TypeName::Complex64, str.parse()?); 288 | assert_eq!(TypeName::Complex64.name(), str) 289 | } 290 | 13 => { 291 | assert_eq!(TypeName::Complex128, str.parse()?); 292 | assert_eq!(TypeName::Complex128.name(), str) 293 | } 294 | 14 => { 295 | assert_eq!(TypeName::Byte, str.parse()?); 296 | assert_eq!(TypeName::Byte.name(), str) 297 | } 298 | 15 => { 299 | assert_eq!(TypeName::Rune, str.parse()?); 300 | assert_eq!(TypeName::Rune.name(), str) 301 | } 302 | 16 => { 303 | assert_eq!(TypeName::Uint, str.parse()?); 304 | assert_eq!(TypeName::Uint.name(), str) 305 | } 306 | 17 => { 307 | assert_eq!(TypeName::Int, str.parse()?); 308 | assert_eq!(TypeName::Int.name(), str) 309 | } 310 | 18 => { 311 | assert_eq!(TypeName::Uintptr, str.parse()?); 312 | assert_eq!(TypeName::Uintptr.name(), str) 313 | } 314 | // 315 | 19 => { 316 | assert_eq!(TypeName::String, str.parse()?); 317 | assert_eq!(TypeName::String.name(), str) 318 | } 319 | // 320 | 20 => { 321 | assert_eq!( 322 | TypeName::QualifiedIdent("time".to_owned(), "Duration".to_owned()), 323 | str.parse()? 324 | ); 325 | assert_eq!( 326 | TypeName::QualifiedIdent("time".to_owned(), "Duration".to_owned()).name(), 327 | str 328 | ) 329 | } 330 | // 331 | 21 => { 332 | assert_eq!(TypeName::Identifier("foo".to_owned()), str.parse()?); 333 | assert_eq!(TypeName::Identifier("foo".to_owned()).name(), str) 334 | } 335 | _ => assert!(false), 336 | } 337 | } 338 | 339 | Ok(()) 340 | } 341 | 342 | #[test] 343 | fn test_parse_with_identifier_missing() { 344 | match "".parse::() { 345 | Ok(_) => assert!(false), 346 | Err(TypeNameParseError::IdentifierMissing) => {} 347 | Err(err) => assert!(false, "{:?}", err), 348 | } 349 | } 350 | } 351 | --------------------------------------------------------------------------------