mz_walkabout/
parse.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! A basic parser for Rust code.
11
12use std::fs;
13use std::path::Path;
14
15use anyhow::{Context, Result};
16use syn::{Data, DataEnum, DataStruct, DataUnion, DeriveInput, Item};
17
18/// Parses the module at `path` and any contained submodules.
19///
20/// Returns [`DeriveInput`]s representing all struct and enum items in the
21/// module. This is exactly what a custom derive procedural macro would see,
22/// except that we can present information for all types simultaneously.
23pub fn parse_mod<P>(path: P) -> Result<Vec<DeriveInput>>
24where
25    P: AsRef<Path>,
26{
27    let mut out = vec![];
28    collect_items(path, &mut out)?;
29    Ok(out)
30}
31
32fn collect_items<P>(path: P, out: &mut Vec<DeriveInput>) -> Result<()>
33where
34    P: AsRef<Path>,
35{
36    let path = path.as_ref();
37    let dir = path.parent().expect("missing parent directory");
38    let stem = path
39        .file_stem()
40        .expect("missing file stem")
41        .to_str()
42        .expect("file stem is not valid UTF-8");
43
44    let src =
45        fs::read_to_string(path).with_context(|| format!("Failed to read {}", path.display()))?;
46    let file = syn::parse_file(&src)?;
47
48    for item in file.items {
49        match item {
50            Item::Mod(item) if item.content.is_none() => {
51                let path = match stem {
52                    "mod" | "lib" => dir.join(format!("{}.rs", item.ident)),
53                    _ => dir.join(format!("{}/{}.rs", stem, item.ident)),
54                };
55                collect_items(path, out)?;
56            }
57            Item::Struct(item) => {
58                out.push(DeriveInput {
59                    ident: item.ident,
60                    vis: item.vis,
61                    attrs: item.attrs,
62                    generics: item.generics,
63                    data: Data::Struct(DataStruct {
64                        fields: item.fields,
65                        struct_token: item.struct_token,
66                        semi_token: item.semi_token,
67                    }),
68                });
69            }
70            Item::Enum(item) => {
71                out.push(DeriveInput {
72                    ident: item.ident,
73                    vis: item.vis,
74                    attrs: item.attrs,
75                    generics: item.generics,
76                    data: Data::Enum(DataEnum {
77                        enum_token: item.enum_token,
78                        brace_token: item.brace_token,
79                        variants: item.variants,
80                    }),
81                });
82            }
83            Item::Union(item) => {
84                out.push(DeriveInput {
85                    ident: item.ident,
86                    vis: item.vis,
87                    attrs: item.attrs,
88                    generics: item.generics,
89                    data: Data::Union(DataUnion {
90                        union_token: item.union_token,
91                        fields: item.fields,
92                    }),
93                });
94            }
95            _ => (),
96        }
97    }
98    Ok(())
99}