Skip to content

Commit f1188cb

Browse files
Merge pull request 1Password#209 from 1Password/dr/context-types
Fix duplicate root added to walker.
2 parents 7d873dd + 0370f23 commit f1188cb

File tree

8 files changed

+120
-78
lines changed

8 files changed

+120
-78
lines changed

cli/src/main.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rayon::iter::ParallelBridge;
2323
#[cfg(feature = "go")]
2424
use typeshare_core::language::Go;
2525
use typeshare_core::{
26+
context::ParseContext,
2627
language::{
2728
CrateName, GenericConstraints, Kotlin, Language, Scala, SupportedLanguage, Swift,
2829
TypeScript,
@@ -72,6 +73,8 @@ fn main() -> anyhow::Result<()> {
7273

7374
let directories = options.directories.as_slice();
7475

76+
info!("Using directories: {directories:?}");
77+
7578
let language_type = match options.language {
7679
None => panic!("no language specified; `clap` should have guaranteed its presence"),
7780
Some(language) => match language {
@@ -110,7 +113,7 @@ fn main() -> anyhow::Result<()> {
110113
.overrides(overrides)
111114
.follow_links(options.follow_links);
112115

113-
for root in directories {
116+
for root in directories.iter().skip(1) {
114117
walker_builder.add(root);
115118
}
116119

@@ -127,9 +130,13 @@ fn main() -> anyhow::Result<()> {
127130

128131
let multi_file = matches!(destination, Output::Folder(_));
129132
let target_os = config.target_os.clone();
130-
131133
let mut lang = language(language_type, config, multi_file);
132-
let ignored_types = lang.ignored_reference_types();
134+
135+
let parse_context = ParseContext {
136+
ignored_types: lang.ignored_reference_types(),
137+
multi_file,
138+
target_os,
139+
};
133140

134141
// The walker ignores directories that are git-ignored. If you need
135142
// a git-ignored directory to be processed, add the specific directory to
@@ -141,9 +148,7 @@ fn main() -> anyhow::Result<()> {
141148
// https://docs.rs/ignore/latest/ignore/struct.WalkParallel.html
142149
let crate_parsed_data = parse_input(
143150
parser_inputs(walker_builder, language_type, multi_file).par_bridge(),
144-
&ignored_types,
145-
multi_file,
146-
&target_os,
151+
&parse_context,
147152
)?;
148153

149154
// Collect all the types into a map of the file name they
@@ -157,6 +162,8 @@ fn main() -> anyhow::Result<()> {
157162

158163
check_parse_errors(&crate_parsed_data)?;
159164

165+
info!("typeshare started writing generated types");
166+
160167
write_generated(
161168
destination,
162169
lang.as_mut(),

cli/src/parse.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
path::PathBuf,
88
};
99
use typeshare_core::{
10+
context::{ParseContext, ParseFileContext},
1011
language::{CrateName, CrateTypes, SupportedLanguage, SINGLE_FILE_CRATE_NAME},
1112
parser::ParsedData,
1213
RenameExt,
@@ -89,9 +90,7 @@ pub fn all_types(file_mappings: &BTreeMap<CrateName, ParsedData>) -> CrateTypes
8990
/// Collect all the parsed sources into a mapping of crate name to parsed data.
9091
pub fn parse_input(
9192
inputs: impl ParallelIterator<Item = ParserInput>,
92-
ignored_types: &[&str],
93-
multi_file: bool,
94-
target_os: &[String],
93+
parse_context: &ParseContext,
9594
) -> anyhow::Result<BTreeMap<CrateName, ParsedData>> {
9695
inputs
9796
.into_par_iter()
@@ -103,17 +102,17 @@ pub fn parse_input(
103102
file_name,
104103
crate_name,
105104
}| {
106-
let parsed_result = typeshare_core::parser::parse(
107-
&std::fs::read_to_string(&file_path)
105+
let parse_file_context = ParseFileContext {
106+
source_code: std::fs::read_to_string(&file_path)
108107
.with_context(|| format!("Failed to read input: {file_name}"))?,
109-
crate_name.clone(),
110-
file_name.clone(),
108+
crate_name: crate_name.clone(),
109+
file_name: file_name.clone(),
111110
file_path,
112-
ignored_types,
113-
multi_file,
114-
target_os,
115-
)
116-
.with_context(|| format!("Failed to parse: {file_name}"))?;
111+
};
112+
113+
let parsed_result =
114+
typeshare_core::parser::parse(parse_context, parse_file_context)
115+
.with_context(|| format!("Failed to parse: {file_name}"))?;
117116

118117
if let Some(parsed_data) = parsed_result {
119118
parsed_crates

core/src/context.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! Context types for parsing.
2+
//!
3+
use crate::language::CrateName;
4+
use std::path::PathBuf;
5+
6+
/// Context for parsing rust source files.
7+
#[derive(Default)]
8+
pub struct ParseContext<'a> {
9+
/// Types to ignore
10+
pub ignored_types: Vec<&'a str>,
11+
/// Multi file output enabled.
12+
pub multi_file: bool,
13+
/// `target_os` filtering.
14+
pub target_os: Vec<String>,
15+
}
16+
17+
/// Parsing context for a single rust source file.
18+
pub struct ParseFileContext {
19+
/// Source code content
20+
pub source_code: String,
21+
/// Name of the crate this file belongs to.
22+
pub crate_name: CrateName,
23+
/// File name.
24+
pub file_name: String,
25+
/// Full path to the source file.
26+
pub file_path: PathBuf,
27+
}

core/src/lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
//! The core library for typeshare.
22
//! Contains the parser and language converters.
3-
43
use thiserror::Error;
54

6-
mod rename;
7-
5+
pub mod context;
86
/// Implementations for each language converter
97
pub mod language;
108
/// Parsing Rust code into a format the `language` modules can understand
119
pub mod parser;
10+
mod rename;
1211
/// Codifying Rust types and how they convert to various languages.
1312
pub mod rust_types;
1413
mod target_os_check;

core/src/parser.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
context::{ParseContext, ParseFileContext},
23
language::{CrateName, SupportedLanguage},
34
rename::RenameExt,
45
rust_types::{
@@ -15,7 +16,6 @@ use proc_macro2::Ident;
1516
use std::{
1617
collections::{BTreeSet, HashMap, HashSet},
1718
convert::TryFrom,
18-
path::PathBuf,
1919
};
2020
use syn::{
2121
ext::IdentExt, parse::ParseBuffer, punctuated::Punctuated, visit::Visit, Attribute, Expr,
@@ -162,32 +162,27 @@ impl ParsedData {
162162

163163
/// Parse the given Rust source string into `ParsedData`.
164164
pub fn parse(
165-
source_code: &str,
166-
crate_name: CrateName,
167-
file_name: String,
168-
file_path: PathBuf,
169-
ignored_types: &[&str],
170-
mult_file: bool,
171-
target_os: &[String],
165+
parse_context: &ParseContext,
166+
parse_file_context: ParseFileContext,
172167
) -> Result<Option<ParsedData>, ParseError> {
173168
// We will only produce output for files that contain the `#[typeshare]`
174169
// attribute, so this is a quick and easy performance win
175-
if !source_code.contains("#[typeshare") {
170+
if !parse_file_context.source_code.contains("#[typeshare") {
176171
return Ok(None);
177172
}
178173

179-
debug!("parsing {file_name}");
180-
// Parse and process the input, ensuring we parse only items marked with
181-
// `#[typeshare]`
182-
let mut import_visitor = TypeShareVisitor::new(
174+
let ParseFileContext {
175+
source_code,
183176
crate_name,
184177
file_name,
185178
file_path,
186-
ignored_types,
187-
mult_file,
188-
target_os,
189-
);
190-
import_visitor.visit_file(&syn::parse_file(source_code)?);
179+
} = parse_file_context;
180+
181+
debug!("parsing {file_name}");
182+
// Parse and process the input, ensuring we parse only items marked with
183+
// `#[typeshare]`
184+
let mut import_visitor = TypeShareVisitor::new(parse_context, crate_name, file_name, file_path);
185+
import_visitor.visit_file(&syn::parse_file(&source_code)?);
191186

192187
Ok(import_visitor.parsed_data())
193188
}

core/src/visitors.rs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Visitors to collect various items from the AST.
22
use crate::{
3+
context::ParseContext,
34
language::CrateName,
45
parser::{
56
has_typeshare_annotation, parse_enum, parse_struct, parse_type_alias, ErrorInfo,
@@ -46,29 +47,24 @@ const IGNORED_TYPES: &[&str] = &["Option", "String", "Vec", "HashMap", "T", "I54
4647

4748
/// An import visitor that collects all use or
4849
/// qualified referenced items.
49-
#[derive(Default)]
5050
pub struct TypeShareVisitor<'a> {
5151
parsed_data: ParsedData,
5252
file_path: PathBuf,
53-
ignored_types: &'a [&'a str],
54-
target_os: &'a [String],
53+
parse_context: &'a ParseContext<'a>,
5554
}
5655

5756
impl<'a> TypeShareVisitor<'a> {
5857
/// Create an import visitor for a given crate name.
5958
pub fn new(
59+
parse_context: &'a ParseContext<'a>,
6060
crate_name: CrateName,
6161
file_name: String,
6262
file_path: PathBuf,
63-
ignored_types: &'a [&'a str],
64-
multi_file: bool,
65-
target_os: &'a [String],
6663
) -> Self {
6764
Self {
68-
parsed_data: ParsedData::new(crate_name, file_name, multi_file),
65+
parsed_data: ParsedData::new(crate_name, file_name, parse_context.multi_file),
6966
file_path,
70-
ignored_types,
71-
target_os,
67+
parse_context,
7268
}
7369
}
7470

@@ -193,7 +189,7 @@ impl<'a> TypeShareVisitor<'a> {
193189
/// not match `--target-os` argument?
194190
#[inline(always)]
195191
fn target_os_accepted(&self, attrs: &[Attribute]) -> bool {
196-
accept_target_os(attrs, self.target_os)
192+
accept_target_os(attrs, &self.parse_context.target_os)
197193
}
198194
}
199195

@@ -224,7 +220,10 @@ impl<'ast, 'a> Visit<'ast> for TypeShareVisitor<'a> {
224220

225221
(accept_crate(&crate_candidate)
226222
&& accept_type(&type_candidate)
227-
&& !self.ignored_types.contains(&type_candidate.as_str())
223+
&& !self
224+
.parse_context
225+
.ignored_types
226+
.contains(&type_candidate.as_str())
228227
&& crate_candidate != type_candidate)
229228
.then(|| {
230229
// resolve crate and super aliases into the crate name.
@@ -254,10 +253,14 @@ impl<'ast, 'a> Visit<'ast> for TypeShareVisitor<'a> {
254253
if !self.parsed_data.multi_file {
255254
return;
256255
}
257-
self.parsed_data.import_types.extend(
258-
parse_import(i, &self.parsed_data.crate_name)
259-
.filter(|imp| !self.ignored_types.contains(&imp.type_name.as_str())),
260-
);
256+
self.parsed_data
257+
.import_types
258+
.extend(parse_import(i, &self.parsed_data.crate_name).filter(|imp| {
259+
!self
260+
.parse_context
261+
.ignored_types
262+
.contains(&imp.type_name.as_str())
263+
}));
261264
syn::visit::visit_item_use(self, i);
262265
}
263266

@@ -266,7 +269,7 @@ impl<'ast, 'a> Visit<'ast> for TypeShareVisitor<'a> {
266269
debug!("Visiting {}", i.ident);
267270
if has_typeshare_annotation(&i.attrs) && self.target_os_accepted(&i.attrs) {
268271
debug!("\tParsing {}", i.ident);
269-
self.collect_result(parse_struct(i, self.target_os));
272+
self.collect_result(parse_struct(i, &self.parse_context.target_os));
270273
}
271274

272275
syn::visit::visit_item_struct(self, i);
@@ -277,7 +280,7 @@ impl<'ast, 'a> Visit<'ast> for TypeShareVisitor<'a> {
277280
debug!("Visiting {}", i.ident);
278281
if has_typeshare_annotation(&i.attrs) && self.target_os_accepted(&i.attrs) {
279282
debug!("\tParsing {}", i.ident);
280-
self.collect_result(parse_enum(i, self.target_os));
283+
self.collect_result(parse_enum(i, &self.parse_context.target_os));
281284
}
282285

283286
syn::visit::visit_item_enum(self, i);
@@ -432,7 +435,7 @@ fn parse_import<'a>(
432435
#[cfg(test)]
433436
mod test {
434437
use super::{parse_import, TypeShareVisitor};
435-
use crate::visitors::ImportedType;
438+
use crate::{context::ParseContext, visitors::ImportedType};
436439
use cool_asserts::assert_matches;
437440
use itertools::Itertools;
438441
use syn::{visit::Visit, File};
@@ -603,14 +606,18 @@ mod test {
603606
}
604607
";
605608

609+
let parse_context = ParseContext {
610+
ignored_types: Vec::new(),
611+
multi_file: true,
612+
target_os: Vec::new(),
613+
};
614+
606615
let file: File = syn::parse_str(rust_code).unwrap();
607616
let mut visitor = TypeShareVisitor::new(
617+
&parse_context,
608618
"my_crate".into(),
609619
"my_file".into(),
610620
"file_path".into(),
611-
&[],
612-
true,
613-
&[],
614621
);
615622
visitor.visit_file(&file);
616623

core/tests/agnostic_tests.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::io::Write;
22
use typeshare_core::{
3+
context::{ParseContext, ParseFileContext},
34
language::{CrateTypes, Language, TypeScript},
45
parser::{self, ParseError},
56
rust_types::RustTypeParseError,
@@ -12,14 +13,16 @@ pub fn process_input(
1213
imports: &CrateTypes,
1314
out: &mut dyn Write,
1415
) -> Result<(), ProcessInputError> {
16+
let parse_context = ParseContext::default();
17+
1518
let mut parsed_data = parser::parse(
16-
input,
17-
"default_name".into(),
18-
"file_name".into(),
19-
"file_path".into(),
20-
&[],
21-
false,
22-
&[],
19+
&parse_context,
20+
ParseFileContext {
21+
source_code: input.to_string(),
22+
crate_name: "default_name".into(),
23+
file_name: "file_name".into(),
24+
file_path: "file_path".into(),
25+
},
2326
)?
2427
.unwrap();
2528

0 commit comments

Comments
 (0)