Skip to content

Commit

Permalink
add more docs
Browse files Browse the repository at this point in the history
  • Loading branch information
kurtbuilds committed Dec 18, 2023
1 parent f6d9f85 commit 200b1da
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 51 deletions.
4 changes: 2 additions & 2 deletions core/src/extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,12 @@ pub fn extract_operation_doc(operation: &oa::Operation, format: DocFormat) -> Op
}
}

pub fn extract_schema_docs(schema: &oa::Schema) -> Option<Doc> {
pub fn extract_schema_docs(schema: &Schema) -> Option<Doc> {
schema
.schema_data
.description
.as_ref()
.map(|d| Doc(d.clone()))
.map(|d| Doc(d.trim().to_string()))
}

pub fn make_name_from_method_and_url(method: &str, url: &str) -> String {
Expand Down
16 changes: 12 additions & 4 deletions core/src/extractor/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ pub fn effective_length(all_of: &[ReferenceOr<Schema>]) -> usize {
for schema_ref in all_of {
length += schema_ref.as_ref_str().map(|_s| 1).unwrap_or_default();
length += schema_ref.as_item()
.and_then(|s| s.properties() )
.map(|s| s.iter().len() )
.and_then(|s| s.properties())
.map(|s| s.iter().len())
.unwrap_or_default();
}
length
Expand All @@ -53,7 +53,12 @@ pub fn create_record(name: &str, schema: &Schema, spec: &OpenAPI) -> Record {
// The base case, a regular object
SchemaKind::Type(Type::Object(ObjectType { properties, .. })) => {
let fields = properties_to_fields(properties, schema, spec);
Record::Struct(Struct { name, fields, nullable: schema.schema_data.nullable })
Record::Struct(Struct {
name,
fields,
nullable: schema.schema_data.nullable,
docs: schema.schema_data.description.as_ref().map(|d| Doc(d.trim().to_string())),
})
}
// An enum
SchemaKind::Type(Type::String(StringType { enumeration, .. }))
Expand All @@ -65,6 +70,7 @@ pub fn create_record(name: &str, schema: &Schema, spec: &OpenAPI) -> Record {
.iter()
.map(|s| s.to_string())
.collect(),
docs: schema.schema_data.description.as_ref().map(|d| Doc(d.clone())),
})
}
// A newtype with multiple fields
Expand All @@ -89,6 +95,7 @@ pub fn create_record(name: &str, schema: &Schema, spec: &OpenAPI) -> Record {
example: None,
flatten: false,
}],
docs: schema.schema_data.description.as_ref().map(|d| Doc(d.clone())),
}),
}
}
Expand Down Expand Up @@ -140,6 +147,7 @@ fn create_record_from_all_of(name: &str, all_of: &[ReferenceOr<Schema>], schema_
nullable: schema_data.nullable,
name: name.to_string(),
fields,
docs: schema_data.description.as_ref().map(|d| Doc(d.clone())),
})
}

Expand All @@ -154,7 +162,7 @@ pub fn extract_records(spec: &OpenAPI, result: &mut HirSpec) -> Result<()> {
result.schemas.insert(name, rec);
}

for (name, schema_ref) in spec.schemas() {
for (name, schema_ref) in spec.schemas() {
let Some(reference) = schema_ref.as_ref_str() else { continue; };
result.schemas.insert(name.clone(), Record::TypeAlias(name.clone(), create_field(&schema_ref, spec)));
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use hir::Language;
pub struct ConfigFlags {
/// Only for Rust. Adds ormlite::TableMeta flags to the code.
pub ormlite: bool,
/// Only for Rust (for now). Adds fake::Dummy flags to the code.
pub fake: bool
}

#[derive(Debug, Clone)]
Expand Down
4 changes: 4 additions & 0 deletions hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,14 @@ pub struct Struct {
pub name: String,
pub nullable: bool,
pub fields: BTreeMap<String, HirField>,
pub docs: Option<Doc>,
}

#[derive(Debug, Clone)]
pub struct NewType {
pub name: String,
pub fields: Vec<HirField>,
pub docs: Option<Doc>,
}

#[derive(Debug, Clone)]
Expand All @@ -241,6 +243,7 @@ pub struct TypeAlias {
pub struct StrEnum {
pub name: String,
pub variants: Vec<String>,
pub docs: Option<Doc>,
}

/// an object type in the HIR
Expand Down Expand Up @@ -498,6 +501,7 @@ impl Operation {
nullable: false,
name: self.required_struct_name(),
fields,
docs: None,
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions libninja/src/command/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ use ln_core::{ConfigFlags};
pub enum Config {
/// Only used by Rust. Adds ormlite::TableMeta flags to the code.
Ormlite,
/// Only used by Rust (for now). Adds fake::Dummy flags to the code.
Fake,
}

fn build_config(configs: &[Config]) -> ConfigFlags {
let mut config = ConfigFlags::default();
for c in configs {
match c {
Config::Ormlite => config.ormlite = true,
Config::Fake => config.fake = true,
}
}
config
Expand Down
2 changes: 1 addition & 1 deletion libninja/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub struct Extras {
date_serialization: bool,
currency: bool,
integer_date_serialization: bool,
basic_auth: bool
basic_auth: bool,
}

impl Extras {
Expand Down
3 changes: 3 additions & 0 deletions libninja/src/rust/cargo_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ pub fn update_cargo_toml(extras: &Extras, opts: &OutputConfig, context: &HashMap
if opts.config.ormlite {
ensure_dependency(&mut m.dependencies, "ormlite", "0.16.0", &["decimal"]);
}
if opts.config.fake {
ensure_dependency(&mut m.dependencies, "fake", "2.9", &["derive", "chrono", "rust_decimal", "http", "uuid"]);
}
if extras.basic_auth {
ensure_dependency(&mut m.dependencies, "base64", "0.21.0", &[]);
}
Expand Down
17 changes: 7 additions & 10 deletions libninja/src/rust/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,10 @@ impl ToRustCode for Option<Doc> {
fn to_rust_code(self) -> TokenStream {
match self {
None => TokenStream::new(),
Some(Doc(doc)) => quote!(#[doc = #doc]),
Some(Doc(doc)) => {
let doc = doc.trim();
quote!(#[doc = #doc])
},
}
}
}
Expand Down Expand Up @@ -312,7 +315,7 @@ pub fn to_rust_example_value(ty: &Ty, name: &str, spec: &HirSpec, use_ref_value:
let record = spec.get_record(model)?;
let force_ref = model.ends_with("Required");
match record {
Record::Struct(Struct { name: _name, fields, nullable }) => {
Record::Struct(Struct { name: _name, fields, nullable, docs: _docs }) => {
let fields = fields.iter().map(|(name, field)| {
let not_ref = !force_ref || field.optional;
let mut value = to_rust_example_value(&field.ty, name, spec, !not_ref)?;
Expand All @@ -325,14 +328,14 @@ pub fn to_rust_example_value(ty: &Ty, name: &str, spec: &HirSpec, use_ref_value:
let model = model.to_rust_struct();
quote!(#model{#(#fields),*})
}
Record::NewType(NewType { name, fields }) => {
Record::NewType(NewType { name, fields, docs: _docs }) => {
let fields = fields.iter().map(|f| {
to_rust_example_value(&f.ty, name, spec, false)
}).collect::<Result<Vec<_>, _>>()?;
let name = name.to_rust_struct();
quote!(#name(#(#fields),*))
}
Record::Enum(StrEnum { name, variants }) => {
Record::Enum(StrEnum { name, variants, docs: _docs }) => {
let variant = variants.first().unwrap();
let variant = variant.to_rust_struct();
let model = model.to_rust_struct();
Expand Down Expand Up @@ -452,12 +455,6 @@ fn assert_valid_ident(s: &str, original: &str) {
}
}

/// This is for testing more than anything else
pub fn formatted_code(code: impl ToRustCode) -> String {
let code = code.to_rust_code();
format::format_code(code).unwrap()
}

#[cfg(test)]
mod tests {
use mir::{Ident, import, Import};
Expand Down
21 changes: 12 additions & 9 deletions libninja/src/rust/codegen/typ.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use proc_macro2::TokenStream;
use quote::quote;
use hir::Ty;
use hir::{HirSpec, Ty};
use crate::rust::codegen::ToRustIdent;
use crate::rust::lower_mir::HirFieldExt;

/// Use this to generate Rust code types.
pub trait ToRustType {
fn to_rust_type(&self) -> TokenStream;
fn to_reference_type(&self, specifier: TokenStream) -> TokenStream;
fn is_reference_type(&self) -> bool;
fn implements_default(&self) -> bool;
fn implements_default(&self, spec: &HirSpec) -> bool;
}

impl ToRustType for Ty {
Expand Down Expand Up @@ -67,20 +68,22 @@ impl ToRustType for Ty {
}
}


fn implements_default(&self) -> bool {
fn implements_default(&self, spec: &HirSpec) -> bool {
match self {
Ty::String => true,
Ty::Integer { .. } => true,
Ty::Float => true,
Ty::Boolean => true,
Ty::Array(_) => true,
Ty::Model(..) => false,
Ty::Model(name) => {
let model = spec.get_record(name.as_str()).expect("Model not found");
model.fields().all(|f| f.implements_default(spec))
}
Ty::Unit => true,
Ty::Any => false,
Ty::Date { .. } => false,
Ty::DateTime => false,
Ty::Currency { .. } => false,
Ty::Any => true,
Ty::Date { .. } => true,
Ty::DateTime => true,
Ty::Currency { .. } => true,
}
}
}
43 changes: 30 additions & 13 deletions libninja/src/rust/lower_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,20 @@ impl FieldExt for HirField {
}

pub trait StructExt {
fn implements_default(&self) -> bool;
fn derive_default(&self) -> TokenStream;
fn implements_default(&self, spec: &HirSpec) -> bool;
fn derive_default(&self, spec: &HirSpec) -> TokenStream;
fn model_fields<'a>(&'a self, config: &'a ConfigFlags) -> Box<dyn Iterator<Item=Field<TokenStream>> + 'a>;
fn ref_target(&self) -> Option<RefTarget>;
}

impl StructExt for Struct {

fn implements_default(&self) -> bool {
self.fields.iter().all(|(_, f)| f.optional || f.ty.implements_default())
fn implements_default(&self, spec: &HirSpec) -> bool {
self.fields.values().all(|f| f.implements_default(spec))
}

fn derive_default(&self) -> TokenStream {
if self.implements_default() {
fn derive_default(&self, spec: &HirSpec) -> TokenStream {
if self.implements_default(spec) {
quote! { , Default }
} else {
TokenStream::new()
Expand All @@ -132,6 +132,7 @@ impl StructExt for Struct {
visibility: Visibility::Public,
decorators,
optional,
doc: field.doc.clone(),
..Field::default()
}
}))
Expand Down Expand Up @@ -165,6 +166,16 @@ impl RecordExt for Record {
}
}

pub trait HirFieldExt {
fn implements_default(&self, spec: &HirSpec) -> bool;
}

impl HirFieldExt for HirField {
fn implements_default(&self, spec: &HirSpec) -> bool {
self.optional || self.ty.implements_default(spec)
}
}

/// Generate a model.rs file that just imports from dependents.
pub fn generate_model_rs(spec: &HirSpec, config: &ConfigFlags) -> File<TokenStream> {
let imports = spec.schemas.keys().map(|name: &String| {
Expand Down Expand Up @@ -195,8 +206,11 @@ pub fn generate_single_model_file(name: &str, record: &Record, spec: &HirSpec, c
if config.ormlite {
imports.push(import!("ormlite", TableMeta, IntoArguments));
}
if config.fake {
imports.push(import!("fake", Dummy));
}
File {
code: Some(create_struct(record, config)),
code: Some(create_struct(record, config, spec)),
imports,
..File::default()
}
Expand All @@ -207,9 +221,11 @@ pub struct RefTarget {
ty: Ty,
}

pub fn create_sumtype_struct(schema: &Struct, config: &ConfigFlags) -> TokenStream {
let default = schema.derive_default();
let ormlite = if config.ormlite { quote! { , TableMeta, IntoArguments } } else { TokenStream::new() };
pub fn create_sumtype_struct(schema: &Struct, config: &ConfigFlags, spec: &HirSpec) -> TokenStream {
let default = schema.derive_default(spec);
let ormlite = config.ormlite.then(|| { quote! { , TableMeta, IntoArguments } }).unwrap_or_default();
let dummy = config.fake.then(|| { quote! { , Dummy } }).unwrap_or_default();
let docs = schema.docs.clone().to_rust_code();

let name = schema.name.to_rust_struct();
let fields = schema.model_fields(config).map(ToRustCode::to_rust_code);
Expand All @@ -232,7 +248,8 @@ pub fn create_sumtype_struct(schema: &Struct, config: &ConfigFlags) -> TokenStre
}).unwrap_or_default();

quote! {
#[derive(Debug, Clone, Serialize, Deserialize #default #ormlite)]
#docs
#[derive(Debug, Clone, Serialize, Deserialize #default #ormlite #dummy)]
pub struct #name {
#(#fields)*
}
Expand Down Expand Up @@ -292,9 +309,9 @@ pub fn create_typealias(name: &str, schema: &HirField) -> TokenStream {
}
}

pub fn create_struct(record: &Record, config: &ConfigFlags) -> TokenStream {
pub fn create_struct(record: &Record, config: &ConfigFlags, spec: &HirSpec) -> TokenStream {
match record {
Record::Struct(s) => create_sumtype_struct(s, config),
Record::Struct(s) => create_sumtype_struct(s, config, spec),
Record::NewType(nt) => create_newtype_struct(nt),
Record::Enum(en) => create_enum_struct(en),
Record::TypeAlias(name, field) => create_typealias(name, field),
Expand Down
14 changes: 8 additions & 6 deletions libninja/tests/all_of/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use pretty_assertions::assert_eq;

/// Tests that the `allOf` keyword is handled correctly.
use ln_core::{ConfigFlags};
use hir::Record;
use hir::{HirSpec, Record};
use ln_core::extractor::{extract_api_operations, extract_records};

const TRANSACTION: &str = include_str!("transaction.yaml");
const TRANSACTION_RS: &str = include_str!("transaction.rs");
Expand All @@ -19,9 +20,9 @@ fn record_for_schema(name: &str, schema: &str, spec: &OpenAPI) -> Record {
record
}

fn formatted_code(record: Record) -> String {
fn formatted_code(record: Record, spec: &HirSpec) -> String {
let config = ConfigFlags::default();
let code = libninja::rust::lower_mir::create_struct(&record, &config);
let code = libninja::rust::lower_mir::create_struct(&record, &config, spec);
libninja::rust::format::format_code(code).unwrap()
}

Expand All @@ -33,9 +34,10 @@ fn test_transaction() {
spec.add_schema("PersonalFinanceCategory", Schema::new_string());
spec.add_schema("TransactionCounterparty", Schema::new_string());

let mut result = HirSpec::default();
extract_records(&spec, &mut result).unwrap();
let record = record_for_schema("Transaction", TRANSACTION, &spec);
let code = formatted_code(record);
println!("{}", code);
let code = formatted_code(record, &result);
assert_eq!(code, TRANSACTION_RS);
}

Expand All @@ -45,6 +47,6 @@ fn test_nullable_doesnt_deref() {
spec.add_schema("RecipientBACS", Schema::new_object());

let record = record_for_schema("PaymentInitiationOptionalRestrictionBacs", RESTRICTION_BACS, &spec);
let code = formatted_code(record);
let code = formatted_code(record, &HirSpec::default());
assert_eq!(code, RESTRICTION_BACS_RS);
}
2 changes: 1 addition & 1 deletion libninja/tests/all_of/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Transaction {
#[serde(flatten)]
pub transaction_base: TransactionBase,
Expand Down
Loading

0 comments on commit 200b1da

Please sign in to comment.