Skip to content

Commit

Permalink
Merge branch 'main' into hculea/add-python-support-in-typeshare
Browse files Browse the repository at this point in the history
  • Loading branch information
MOmarMiraj committed Jul 31, 2024
2 parents 2039818 + ecc8ce7 commit 48a8adb
Show file tree
Hide file tree
Showing 62 changed files with 1,085 additions and 583 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Version 1.10.0-beta

## 1.10.0-beta.6

- Added support for skipping fields/variants via the `target_os` argument [#176](https://github.com/1Password/typeshare/pull/176)

## 1.10.0-beta.5

- Added support for Swift generic constraints via `#[typeshare(swiftGenericConstraints)]` [#174](https://github.com/1Password/typeshare/pull/174)
- Added Swift config option for defining constraints on `CodableVoid` generated type [#174](https://github.com/1Password/typeshare/pull/174)

## 1.10.0-beta.4

Fixed a bug involving `#[typeshare(skip)]` on fields in struct variants of enums.

## 1.10.0-beta.2

Fixed a bug involving type aliases.
Expand All @@ -10,7 +23,7 @@ This release brings support for multiple file generation, allowing splitting gen
files when used in large projects. This can dramatically increase compilation speed of
the generated files and increase maintainability.

This is a *pre-release* version which may have bugs or break compatibility.
This is a _pre-release_ version which may have bugs or break compatibility.

- Multiple file output [#166](https://github.com/1Password/typeshare/pull/166)

Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typeshare-cli"
version = "1.10.0-beta.2"
version = "1.10.0-beta.6"
edition = "2021"
description = "Command Line Tool for generating language files with typeshare"
license = "MIT OR Apache-2.0"
Expand All @@ -22,5 +22,5 @@ once_cell = "1"
rayon = "1.10"
serde = { version = "1", features = ["derive"] }
toml = "0.8"
typeshare-core = { path = "../core", version = "1.10.0-beta.2" }
typeshare-core = { path = "../core", version = "1.10.0-beta.6" }
anyhow = "1"
7 changes: 7 additions & 0 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub const ARG_GENERATE_CONFIG: &str = "generate-config-file";
pub const ARG_OUTPUT_FILE: &str = "output-file";
pub const ARG_OUTPUT_FOLDER: &str = "output-folder";
pub const ARG_FOLLOW_LINKS: &str = "follow-links";
pub const ARG_TARGET_OS: &str = "target_os";

#[cfg(feature = "go")]
const AVAILABLE_LANGUAGES: [&str; 6] = ["kotlin", "scala", "swift", "typescript", "go", "python"];
Expand Down Expand Up @@ -147,5 +148,11 @@ pub(crate) fn build_command() -> Command<'static> {
.help("Directories within which to recursively find and process rust files")
.required_unless(ARG_GENERATE_CONFIG)
.min_values(1),
).arg(
Arg::new(ARG_TARGET_OS)
.long("target-os")
.help("Optional restrict to target_os")
.takes_value(true)
.required(false)
)
}
4 changes: 4 additions & 0 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub struct SwiftParams {
pub type_mappings: HashMap<String, String>,
pub default_decorators: Vec<String>,
pub default_generic_constraints: Vec<String>,
/// The contraints to apply to `CodableVoid`.
pub codablevoid_constraints: Vec<String>,
}

#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -69,6 +71,8 @@ pub(crate) struct Config {
pub python: PythonParams,
#[cfg(feature = "go")]
pub go: GoParams,
#[serde(skip)]
pub target_os: Option<String>,
}

pub(crate) fn store_config(config: &Config, file_path: Option<&str>) -> anyhow::Result<()> {
Expand Down
19 changes: 15 additions & 4 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use anyhow::{anyhow, Context};
use args::{
build_command, ARG_CONFIG_FILE_NAME, ARG_FOLLOW_LINKS, ARG_GENERATE_CONFIG, ARG_JAVA_PACKAGE,
ARG_KOTLIN_PREFIX, ARG_MODULE_NAME, ARG_OUTPUT_FOLDER, ARG_SCALA_MODULE_NAME,
ARG_SCALA_PACKAGE, ARG_SWIFT_PREFIX, ARG_TYPE,
ARG_SCALA_PACKAGE, ARG_SWIFT_PREFIX, ARG_TARGET_OS, ARG_TYPE,
};
use clap::ArgMatches;
use config::Config;
use ignore::{overrides::OverrideBuilder, types::TypesBuilder, WalkBuilder};
use parse::{all_types, parse_input, parser_inputs};
use std::collections::HashMap;
use rayon::iter::ParallelBridge;
use std::collections::{BTreeMap, HashMap};
#[cfg(feature = "go")]
use typeshare_core::language::Go;
use typeshare_core::language::{GenericConstraints, Python};
Expand Down Expand Up @@ -101,17 +102,25 @@ fn main() -> anyhow::Result<()> {
}

let multi_file = options.value_of(ARG_OUTPUT_FOLDER).is_some();

let target_os = config.target_os.clone();

let lang = language(language_type, config, multi_file);
let ignored_types = lang.ignored_reference_types();

// The walker ignores directories that are git-ignored. If you need
// a git-ignored directory to be processed, add the specific directory to
// the list of directories given to typeshare when it's invoked in the
// makefiles
// TODO: The `ignore` walker supports parallel walking. We should use this
// and implement a `ParallelVisitor` that builds up the mapping of parsed
// data. That way both walking and parsing are in parallel.
// https://docs.rs/ignore/latest/ignore/struct.WalkParallel.html
let crate_parsed_data = parse_input(
parser_inputs(walker_builder, language_type, multi_file),
parser_inputs(walker_builder, language_type, multi_file).par_bridge(),
&ignored_types,
multi_file,
target_os,
)?;

// Collect all the types into a map of the file name they
Expand Down Expand Up @@ -144,6 +153,7 @@ fn language(
config.swift.default_generic_constraints,
),
multi_file,
codablevoid_constraints: config.swift.codablevoid_constraints,
..Default::default()
}),
SupportedLanguage::Kotlin => Box::new(Kotlin {
Expand Down Expand Up @@ -212,11 +222,12 @@ fn override_configuration(mut config: Config, options: &ArgMatches) -> Config {
config.go.package = go_package.to_string();
}

config.target_os = options.value_of(ARG_TARGET_OS).map(|s| s.to_string());
config
}

/// Prints out all parsing errors if any and returns Err.
fn check_parse_errors(parsed_crates: &HashMap<CrateName, ParsedData>) -> anyhow::Result<()> {
fn check_parse_errors(parsed_crates: &BTreeMap<CrateName, ParsedData>) -> anyhow::Result<()> {
let mut errors_encountered = false;
for data in parsed_crates
.values()
Expand Down
91 changes: 31 additions & 60 deletions cli/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use anyhow::Context;
use ignore::WalkBuilder;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::{
collections::{hash_map::Entry, HashMap},
ops::Not,
collections::{hash_map::Entry, BTreeMap, HashMap},
path::PathBuf,
};
use typeshare_core::{
Expand All @@ -28,12 +27,12 @@ pub fn parser_inputs(
walker_builder: WalkBuilder,
language_type: SupportedLanguage,
multi_file: bool,
) -> Vec<ParserInput> {
) -> impl Iterator<Item = ParserInput> {
walker_builder
.build()
.filter_map(Result::ok)
.filter(|dir_entry| !dir_entry.path().is_dir())
.filter_map(|dir_entry| {
.filter_map(move |dir_entry| {
let crate_name = if multi_file {
CrateName::find_crate_name(dir_entry.path())?
} else {
Expand All @@ -47,7 +46,6 @@ pub fn parser_inputs(
crate_name,
})
})
.collect()
}

/// The output file name to write to.
Expand All @@ -69,7 +67,7 @@ fn output_file_name(language_type: SupportedLanguage, crate_name: &CrateName) ->

/// Collect all the typeshared types into a mapping of crate names to typeshared types. This
/// mapping is used to lookup and generated import statements for generated files.
pub fn all_types(file_mappings: &HashMap<CrateName, ParsedData>) -> CrateTypes {
pub fn all_types(file_mappings: &BTreeMap<CrateName, ParsedData>) -> CrateTypes {
file_mappings
.iter()
.map(|(crate_name, parsed_data)| (crate_name, parsed_data.type_names.clone()))
Expand All @@ -91,74 +89,47 @@ pub fn all_types(file_mappings: &HashMap<CrateName, ParsedData>) -> CrateTypes {

/// Collect all the parsed sources into a mapping of crate name to parsed data.
pub fn parse_input(
inputs: Vec<ParserInput>,
inputs: impl ParallelIterator<Item = ParserInput>,
ignored_types: &[&str],
multi_file: bool,
) -> anyhow::Result<HashMap<CrateName, ParsedData>> {
target_os: Option<String>,
) -> anyhow::Result<BTreeMap<CrateName, ParsedData>> {
inputs
.into_par_iter()
.try_fold(
HashMap::new,
|mut results: HashMap<CrateName, ParsedData>,
BTreeMap::new,
|mut parsed_crates: BTreeMap<CrateName, ParsedData>,
ParserInput {
file_path,
file_name,
crate_name,
}| {
match std::fs::read_to_string(&file_path)
.context("Failed to read input")
.and_then(|data| {
typeshare_core::parser::parse(
&data,
crate_name.clone(),
file_name.clone(),
file_path,
ignored_types,
multi_file,
)
.context("Failed to parse")
})
.map(|parsed_data| {
parsed_data.and_then(|parsed_data| {
is_parsed_data_empty(&parsed_data)
.not()
.then_some((crate_name, parsed_data))
})
})? {
Some((crate_name, parsed_data)) => {
match results.entry(crate_name) {
Entry::Occupied(mut entry) => {
entry.get_mut().add(parsed_data);
}
Entry::Vacant(entry) => {
entry.insert(parsed_data);
}
}
Ok::<_, anyhow::Error>(results)
}
None => Ok(results),
let parsed_result = typeshare_core::parser::parse(
&std::fs::read_to_string(&file_path)
.with_context(|| format!("Failed to read input: {file_name}"))?,
crate_name.clone(),
file_name.clone(),
file_path,
ignored_types,
multi_file,
target_os.clone(),
)
.with_context(|| format!("Failed to parse: {file_name}"))?;

if let Some(parsed_data) = parsed_result {
parsed_crates
.entry(crate_name)
.or_default()
.add(parsed_data);
}

Ok(parsed_crates)
},
)
.try_reduce(HashMap::new, |mut file_maps, mapping| {
for (crate_name, parsed_data) in mapping {
match file_maps.entry(crate_name) {
Entry::Occupied(mut e) => {
e.get_mut().add(parsed_data);
}
Entry::Vacant(e) => {
e.insert(parsed_data);
}
}
.try_reduce(BTreeMap::new, |mut file_maps, parsed_crates| {
for (crate_name, parsed_data) in parsed_crates {
file_maps.entry(crate_name).or_default().add(parsed_data);
}
Ok(file_maps)
})
}

/// Check if we have not parsed any relavent typehsared types.
fn is_parsed_data_empty(parsed_data: &ParsedData) -> bool {
parsed_data.enums.is_empty()
&& parsed_data.aliases.is_empty()
&& parsed_data.structs.is_empty()
&& parsed_data.errors.is_empty()
}
13 changes: 7 additions & 6 deletions cli/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::args::{ARG_OUTPUT_FILE, ARG_OUTPUT_FOLDER};
use anyhow::Context;
use clap::ArgMatches;
use std::{
collections::HashMap,
collections::{BTreeMap, HashMap},
fs,
path::{Path, PathBuf},
};
Expand All @@ -16,7 +16,7 @@ use typeshare_core::{
pub fn write_generated(
options: ArgMatches,
lang: Box<dyn Language>,
crate_parsed_data: HashMap<CrateName, ParsedData>,
crate_parsed_data: BTreeMap<CrateName, ParsedData>,
import_candidates: CrateTypes,
) -> Result<(), anyhow::Error> {
let output_folder = options.value_of(ARG_OUTPUT_FOLDER);
Expand All @@ -35,7 +35,7 @@ pub fn write_generated(
fn write_multiple_files(
mut lang: Box<dyn Language>,
output_folder: &str,
crate_parsed_data: HashMap<CrateName, ParsedData>,
crate_parsed_data: BTreeMap<CrateName, ParsedData>,
import_candidates: CrateTypes,
) -> Result<(), anyhow::Error> {
for (_crate_name, parsed_data) in crate_parsed_data {
Expand Down Expand Up @@ -67,13 +67,14 @@ fn check_write_file(outfile: &PathBuf, output: Vec<u8>) -> anyhow::Result<()> {
if !output.is_empty() {
let out_dir = outfile
.parent()
.context(format!("Could not get parent for {outfile:?}"))?;
.with_context(|| format!("Could not get parent for {outfile:?}"))?;
// If the output directory doesn't already exist, create it.
if !out_dir.exists() {
fs::create_dir_all(out_dir).context("failed to create output directory")?;
}

fs::write(outfile, output).context("failed to write output")?;
fs::write(outfile, output)
.with_context(|| format!("failed to write output: {}", outfile.to_string_lossy()))?;
}
Ok(())
}
Expand All @@ -82,7 +83,7 @@ fn check_write_file(outfile: &PathBuf, output: Vec<u8>) -> anyhow::Result<()> {
fn write_single_file(
mut lang: Box<dyn Language>,
file_name: &str,
mut crate_parsed_data: HashMap<CrateName, ParsedData>,
mut crate_parsed_data: BTreeMap<CrateName, ParsedData>,
) -> Result<(), anyhow::Error> {
let parsed_data = crate_parsed_data
.remove(&SINGLE_FILE_CRATE_NAME)
Expand Down
3 changes: 2 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typeshare-core"
version = "1.10.0-beta.2"
version = "1.10.0-beta.6"
license = "MIT OR Apache-2.0"
edition = "2021"
description = "The code generator used by Typeshare's command line tool"
Expand All @@ -23,3 +23,4 @@ anyhow = "1"
expect-test = "1.5"
once_cell = "1"
cool_asserts = "2"
syn = { version = "2", features = ["full", "visit", "extra-traits"] }
2 changes: 1 addition & 1 deletion core/data/tests/can_generate_generic_struct/output.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,4 @@ public enum CoreEnumUsingGenericStruct: Codable {
}

/// () isn't codable, so we use this instead to represent Rust's unit type
public struct CodableVoid: Codable {}
public struct CodableVoid: Codable, Equatable {}
2 changes: 1 addition & 1 deletion core/data/tests/can_handle_unit_type/output.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ public enum EnumHasVoidType: Codable {
}

/// () isn't codable, so we use this instead to represent Rust's unit type
public struct CodableVoid: Codable {}
public struct CodableVoid: Codable, Equatable {}
4 changes: 2 additions & 2 deletions core/data/tests/can_recognize_types_inside_modules/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import "encoding/json"
type A struct {
Field uint32 `json:"field"`
}
type ABC struct {
type AB struct {
Field uint32 `json:"field"`
}
type AB struct {
type ABC struct {
Field uint32 `json:"field"`
}
type OutsideOfModules struct {
Expand Down
Loading

0 comments on commit 48a8adb

Please sign in to comment.