Skip to content

Commit

Permalink
auto-generated stub implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wkarwacki committed Aug 11, 2024
1 parent c204250 commit 1ac5b22
Show file tree
Hide file tree
Showing 15 changed files with 287 additions and 94 deletions.
10 changes: 4 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,15 @@ pub enum Layout {
Tag,
}

#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenCfg {
#[serde(default)]
type_mapping: HashMap<String, String>,
module: Option<PathBuf>,
dto_name: Option<String>,
#[serde(default)]
auto_implement: bool,
}

#[derive(Clone, ValueEnum)]
Expand Down Expand Up @@ -189,11 +191,7 @@ pub fn do_main(cli: Cli) {
} => {
let generator_config = config
.map(|c| serde_yaml::from_reader::<File, GenCfg>(File::open(c).unwrap()).unwrap())
.unwrap_or(GenCfg {
type_mapping: HashMap::new(),
module: None,
dto_name: None,
});
.unwrap_or(Default::default());
gen(&input, &lang, &role, &generator_config, &templates_path)
.iter()
.for_each(|(path, content)| {
Expand Down
23 changes: 8 additions & 15 deletions src/lib/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,28 +61,21 @@ impl Context {
serde_yaml::from_value(value.clone()).unwrap()
}

pub(crate) fn op_refs(&self, op: &Op) -> Vec<(Option<String>, Vec<String>)> {
pub(crate) fn op_refs(&self, op: &Op) -> HashMap<Option<String>, Vec<Ref>> {
let refs = op.refs();
self.src_to_refs(&refs)
Self::src_to_refs(refs)
}

pub(crate) fn def_refs(&self, def: &Def) -> Vec<(Option<String>, Vec<String>)> {
pub(crate) fn def_refs(&self, def: &Def) -> HashMap<Option<String>, Vec<Ref>> {
let refs = def.refs();
self.src_to_refs(&refs)
Self::src_to_refs(refs)
}

fn src_to_refs(&self, refs: &Vec<Ref>) -> Vec<(Option<String>, Vec<String>)> {
let grouped = refs.iter().into_group_map_by(|r#ref| r#ref.src.clone());
grouped
fn src_to_refs(refs: Vec<Ref>) -> HashMap<Option<String>, Vec<Ref>> {
refs.iter()
.into_group_map_by(|r#ref| r#ref.src.clone())
.iter()
.map(|(src, refs)| {
(
src.clone(),
refs.iter()
.map(|r#ref| r#ref.class_name())
.collect::<Vec<_>>(),
)
})
.map(|(src, refs)| (src.clone(), refs.iter().cloned().cloned().collect()))
.collect()
}
}
8 changes: 4 additions & 4 deletions src/lib/def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub(crate) struct Alias {
pub r#ref: Ref,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct Bool {
#[serde(default)]
#[serde(skip_serializing_if = "<&bool>::not")]
Expand All @@ -62,7 +62,7 @@ pub(crate) struct Const {
pub desc: Option<Box<Desc>>,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct Dec {
#[serde(default)]
#[serde(skip_serializing_if = "<&bool>::not")]
Expand All @@ -83,7 +83,7 @@ pub(crate) enum EnumVals {
Str(Vec<String>),
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct Int {
#[serde(default)]
#[serde(skip_serializing_if = "<&bool>::not")]
Expand Down Expand Up @@ -155,7 +155,7 @@ pub(crate) struct Seq {
pub null: bool,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub(crate) struct Str {
#[serde(default)]
#[serde(skip_serializing_if = "<&bool>::not")]
Expand Down
12 changes: 11 additions & 1 deletion src/lib/gen/gen.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
gen::template::r#fn::ToFlatCase,
gen::template::{proc::StubImpl, r#fn::ToFlatCase},
lib::{
context::Context,
gen::{
Expand Down Expand Up @@ -87,6 +87,16 @@ pub(crate) fn go(
handlebars.register_helper("toFlatCase", Box::new(ToFlatCase {}));
handlebars.register_helper("typeParams", Box::new(TypeParams {}.clone()));
handlebars.register_helper("valueDef", Box::new(ValueDef {}.clone()));
handlebars.register_helper(
"stubImpl",
Box::new(
StubImpl {
gen: gen.clone(),
context: context.clone(),
}
.clone(),
),
);

handlebars_misc_helpers::setup_handlebars(&mut handlebars);
handlebars.set_strict_mode(false);
Expand Down
11 changes: 10 additions & 1 deletion src/lib/gen/lang.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::lib::{def::Def, desc::Desc, r#ref::Ref};
use crate::lib::{context::Context, def::Def, desc::Desc, r#ref::Ref};
use handlebars::{Handlebars, JsonValue};
use std::path::PathBuf;

Expand All @@ -15,6 +15,7 @@ pub(crate) trait Lang {
fn fmt_src(&self, src: &str) -> String;
fn fmt_type(&self, def: &Def, name: &Option<&str>) -> String;
fn fmt_value(&self, json_value: &JsonValue) -> String;
fn stub_impl(&self, def: &Def, context: &Context) -> String;
}

pub static DTO_NAME_TEMPLATE_NAME: &str = "dtoName";
Expand All @@ -39,3 +40,11 @@ pub(crate) fn inner(
Desc::TypeParam { .. } => unimplemented!("Type parameter not supported yet."),
}
}

pub(crate) fn stub_impl(lang: &Box<dyn Lang>, desc: &Desc, context: &Context) -> String {
match desc {
Desc::Def(def) => lang.stub_impl(def, context),
Desc::Ref(r#ref) => lang.stub_impl(&context.resolve(r#ref), context),
Desc::TypeParam { .. } => unimplemented!("Type parameter not supported yet."),
}
}
102 changes: 79 additions & 23 deletions src/lib/gen/python/client/gen_python_http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,42 @@ impl Gen for GenPythonHttpClient {
let imports = context
.def_refs(def)
.iter()
.flat_map(|(src, names)| {
names.iter().map(move |name| {
"from ".to_string()
.flat_map(|(src, refs)| {
refs.iter().flat_map(move |r#ref| {
let mut names: Vec<String> = Default::default();
names.push("from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match src {
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ name.to_case(Case::Snake).as_str()
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(self.lang.fmt_class(name, &None).as_str(), &self.lang()).as_str()
+ dto_name(self.lang.fmt_class(r#ref.class_name().as_str(), &None).as_str(), &self.lang())
.as_str());
match context.resolve(r#ref) {
Def::Obj(obj) => {
obj.adt.iter().for_each(|_| names.push("from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(self.lang.fmt_class(r#ref.class_name().as_str(), &None).as_str(), &self.lang())
.as_str()
+ "Base"))
}
_ => {}
}
names
})
})
.unique()
Expand Down Expand Up @@ -178,23 +200,57 @@ impl Gen for GenPythonHttpClient {
pkg.ops
.iter()
.flat_map(|(_, ops)| ops.iter().flat_map(|op| context.op_refs(op)))
.flat_map(|(src, names)| {
names
.iter()
.map(move |name| {
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ name.to_case(Case::Snake).as_str()
+ " import "
+ dto_name(self.lang.fmt_class(name, &None).as_str(), &self.lang())
.flat_map(|(src, refs)| {
refs.iter()
.flat_map(|r#ref| {
let mut names: Vec<String> = Default::default();
names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str(),
);
match context.resolve(r#ref) {
Def::Obj(obj) => obj.adt.iter().for_each(|_| {
names.push(
"from ".to_string()
+ self.lang.module().as_str()
+ "."
+ match &src {
None => self.lang.feature.clone().to_case(Case::Snake),
Some(src) => self.lang.fmt_src(src.as_str()),
}
.as_str()
+ "."
+ r#ref.class_name().to_case(Case::Snake).as_str()
+ " import "
+ dto_name(
self.lang
.fmt_class(r#ref.class_name().as_str(), &None)
.as_str(),
&self.lang(),
)
.as_str()
+ "Base",
)
}),
_ => {}
}
names
})
.collect::<Vec<_>>()
})
Expand Down
17 changes: 11 additions & 6 deletions src/lib/gen/python/client/templates/dto.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{{#if val.item}}{{>dto key=(add key "Item") val=val.item tl=false}}
{{#if val.item}}{{>dto key=(add key "Item") val=val.item tl=false prefix=prefix}}

{{else if val.val}}{{>dto key=(add key "Value") val=val.val tl=false}}
{{else if val.val}}{{>dto key=(add key "Value") val=val.val tl=false prefix=prefix}}

{{/if}}{{#if val.adt}}class {{key}}Base(Dto):
{{indent}}{{#unless val.vars}}pass{{/unless}}{{#each val.vars}}{{#if (or (eq this.type "obj") this.item this.val)}}{{>dto key=(fmtClass @key) val=this indent=(add ../indent " ")}}{{/if}}{{/each}}{{#each val.vars}}
{{indent}} @classmethod
{{indent}} def get(cls) -> '{{fmtClass key}}':
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{#each val.adt.map}}{{#if @first}}{{../key}}{{fmtClass @key}}.get(){{/if}}{{/each}}{{#each val.vars}}{{#if (or (eq this.type "obj") this.item this.val)}}{{>dto key=(fmtClass @key) val=this indent=(add ../indent " ")}}{{/if}}{{/each}}{{#each val.vars}}
{{indent}}{{#if (eq @key ../val.adt.var)}}{{fmtName @key}}: {{#each../val.adt.map}}Literal[{{json @key}}]{{#unless @last}} | {{/unless}}{{/each}}
{{else}}{{../indent}}{{>var}}{{/if}}{{/each}}
{{#each val.adt.map}}
Expand All @@ -14,8 +16,11 @@

{{/each}}

{{key}} = {{>union}} {{else if (and (isAlias val) tl)}}{{key}} = {{fmtType val key}}{{else if (or val.vars adtParent)}}class {{adtParent}}{{fmtClass key}}({{#if (parents val)}}{{#each (parents val)}}{{fmtType this}}{{#unless @last}}, {{/unless}}{{/each}}{{else if adtParent}}{{adtParent}}Base{{else}}Dto{{/if}}){{>typeParams val=val}}:
{{indent}}{{#unless val.vars}} pass{{/unless}}{{#each val.vars}}{{#if (or (or (eq this.type "obj") (eq this.type "enum")) this.item this.val)}} {{>dto key=(fmtClass @key) val=this dtoFormLike=(or ../formLike ../dtoFormLike) indent=(add ../indent " ")}}{{/if}}{{/each}}{{#each val.vars}}{{#if (or (not ../this.formLike) (not (eq @key "file")))}}
{{key}} ={{>union}}{{else if (and (isAlias val) tl)}}{{key}} = {{fmtType val key}}{{else if (or val.vars adtParent)}}class {{adtParent}}{{fmtClass key}}({{#if (parents val)}}{{#each (parents val)}}{{fmtType this}}{{#unless @last}}, {{/unless}}{{/each}}{{else if adtParent}}{{adtParent}}Base{{else}}Dto{{/if}}){{>typeParams val=val}}:
{{indent}} @classmethod
{{indent}} def get(cls) -> '{{adtParent}}{{key}}':
{{indent}} return {{#with prefix}}{{this}}.{{/with}}{{>stubImpl key=(add adtParent key) val=val indent=(add indent " ")}}
{{indent}}{{#each val.vars}}{{#if (or (or (eq this.type "obj") (eq this.type "enum")) this.item this.val)}} {{>dto key=(fmtClass @key) val=this dtoFormLike=(or ../formLike ../dtoFormLike) indent=(add ../indent " ") prefix=../key}}{{/if}}{{/each}}{{#each val.vars}}{{#if (or (not ../this.formLike) (not (eq @key "file")))}}
{{../indent}}{{>var}}{{/if}}{{/each}}{{else if (eq val.type "enum")}}class {{key}}({{#with (valueDef val.vars.[0])}}{{#if (eq type "int")}}IntEnum{{else}}str, Enum{{/if}}{{/with}}):{{#each val.vals}}
{{fmtEnum this}} = {{json this}}{{/each}}{{/if}}{{#if (and (and (hasKey this "tl") (not tl)) (and dtoFormLike (eq val.type "obj")))}}

Expand All @@ -24,4 +29,4 @@
{{../indent}}return {{fmtName key}}.model_dump_json(exclude_none=True){{/if}}{{#if formLike}}

def form(self) -> dict[str, Any]:
return self.model_dump(exclude_none=True){{/if}}
return self.model_dump(exclude_none=True){{/if}}{{#if (and (eq val.type "obj") (and (not val.adt) (not (typeParams val))))}}{{/if}}
3 changes: 3 additions & 0 deletions src/lib/gen/python/client/templates/stubImpl.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{#if (and val.path (not (eq val.type "alias")))}}{{#with (resolve val)}}{{#if (eq this.type "obj")}}{{#if this.adt}}{{>dtoName val=(fmtClass (fmtName ../val.path))}}Base{{else}}{{>dtoName val=(fmtClass ../val.path)}}{{/if}}.get(){{else}}{{>stubImpl val=this indent=indent}}{{/if}}{{/with}}{{else if (or (eq val.type "obj") val.vars)}}{{key}}({{#each val.vars}}
{{../indent}} {{fmtName @key}}={{>stubImpl val=this key=(fmtClass @key) indent=(add ../indent " ") prefix=../key}},{{/each}}
{{indent}}){{else if (eq val.type "map")}}{ {{#if val.key}}{{#if (and prefix (not val.key.path))}}{{prefix}}.{{/if}}{{>stubImpl val=val.key key=(add key "Key") indent=indent}}{{else}}{{>stubImpl val="str" key=(add key "Key") indent=indent}}{{/if}}: {{#if val.val}}{{#if (and prefix (not val.val.path))}}{{prefix}}.{{/if}}{{>stubImpl val=val.val key=(add key "Val") indent=indent}}{{else}}{{>stubImpl val="str" key=(add key "Val") indent=indent}}{{/if}} }{{else if (eq val.type "seq")}}[{{#if (eq val.item.type "obj")}}{{#if prefix}}{{prefix}}.{{/if}}{{add key "Item"}}.get(){{else}}{{>stubImpl val=val.item indent=indent prefix=null}}{{/if}}]{{else if val.mix}}{{unimplemented}}{{else}}{{stubImpl val indent=indent}}{{/if}}
4 changes: 2 additions & 2 deletions src/lib/gen/python/client/templates/union.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Annotated[
Annotated[
{{#each val.adt.map}}{{../key}}{{fmtClass @key}}{{#unless @last}} | {{/unless}}{{/each}},
Field(discriminator="{{fmtName val.adt.var}}")
]
]
18 changes: 18 additions & 0 deletions src/lib/gen/python/lang_python.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
lib::{
context::Context,
def::{Def, Enum, EnumVals},
desc::Desc,
gen::{
Expand Down Expand Up @@ -190,4 +191,21 @@ impl Lang for LangPython {
Value::Null => "None".to_string(),
}
}

fn stub_impl(&self, def: &Def, context: &Context) -> String {
match def {
Def::Alias(alias) => self.stub_impl(&context.resolve(&alias.r#ref), context),
Def::Bool(_) => "False".to_string(),
Def::Const(r#const) => serde_json::to_string(&r#const.clone().val).unwrap(),
Def::Dec(_) => "0.0".to_string(),
Def::Enum(Enum { vals, null: _ }) => match vals {
EnumVals::Int(vals) => vals.first().unwrap().to_string(),
EnumVals::Str(vals) => "\"".to_string() + vals.first().unwrap() + "\"",
},
Def::Int(_) => "0".to_string(),
Def::Str(_) => "\"\"".to_string(),
Def::Struct(_) => "{}".to_string(),
_ => unreachable!(),
}
}
}
Loading

0 comments on commit 1ac5b22

Please sign in to comment.