Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update and extend conversion targets #20

Merged
merged 5 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "mdmodels"
authors = ["Jan Range <[email protected]>"]
description = "A tool to generate models, code and schemas from markdown files"
version = "0.1.8"
version = "0.2.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/FAIRChemistry/md-models"
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# MD-Models

![Crates.io Version](https://img.shields.io/crates/v/mdmodels) ![NPM Version](https://img.shields.io/npm/v/mdmodels-core)
Expand Down Expand Up @@ -73,10 +72,19 @@ The following templates are available:
- `python-dataclass`: Python dataclass implementation with JSON-LD support
- `python-pydantic`: PyDantic implementation with JSON-LD support
- `python-pydantic-xml`: PyDantic implementation with XML support
- `typescript`: TypeScript interface definitions with JSON-LD support
- `typescript-zod`: TypeScript Zod schema definitions
- `rust`: Rust struct definitions with serde support
- `golang`: Go struct definitions
- `protobuf`: Protocol Buffer schema definition
- `graphql`: GraphQL schema definition
- `xml-schema`: XML schema definition
- `json-schema`: JSON schema definition
- `json-schema-all`: Multiple JSON schema definitions (one per object)
- `shacl`: SHACL shapes definition
- `shex`: ShEx shapes definition
- `compact-markdown`: Compact markdown representation
- `mkdocs`: MkDocs documentation format

## Installation options

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "mdmodels_core"
version = "0.1.8"
version = "0.2.0"
description = "A tool to generate models, code and schemas from markdown files"
requires-python = ">=3.8"
classifiers = [
Expand Down
115 changes: 113 additions & 2 deletions src/exporters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use std::{collections::HashMap, error::Error, fmt::Display, str::FromStr};

use crate::{datamodel::DataModel, markdown::frontmatter::FrontMatter};
use clap::ValueEnum;
use convert_case::{Case, Casing};
use lazy_static::lazy_static;
use minijinja::{context, Environment};
use textwrap::wrap;
Expand Down Expand Up @@ -71,6 +72,19 @@ lazy_static! {
m.insert("bytes".to_string(), "string".to_string());
m
};

/// Maps MD-Models type names to GraphQL-specific type names.
static ref GRAPHQL_TYPE_MAPS: std::collections::HashMap<String, String> = {
let mut m = std::collections::HashMap::new();
m.insert("integer".to_string(), "Int".to_string());
m.insert("number".to_string(), "Float".to_string());
m.insert("float".to_string(), "Float".to_string());
m.insert("boolean".to_string(), "Boolean".to_string());
m.insert("string".to_string(), "String".to_string());
m.insert("bytes".to_string(), "String".to_string());
m.insert("date".to_string(), "String".to_string());
m
};
}

/// Enumeration of available templates.
Expand All @@ -91,6 +105,11 @@ pub enum Templates {
MkDocs,
Internal,
Typescript,
TypescriptZod,
Rust,
Protobuf,
Graphql,
Golang,
}

impl Display for Templates {
Expand All @@ -109,6 +128,11 @@ impl Display for Templates {
Templates::MkDocs => write!(f, "mk-docs"),
Templates::Internal => write!(f, "internal"),
Templates::Typescript => write!(f, "typescript"),
Templates::TypescriptZod => write!(f, "typescript-zod"),
Templates::Rust => write!(f, "rust"),
Templates::Protobuf => write!(f, "protobuf"),
Templates::Graphql => write!(f, "graphql"),
Templates::Golang => write!(f, "golang"),
}
}
}
Expand All @@ -132,6 +156,11 @@ impl FromStr for Templates {
"mk-docs" => Ok(Templates::MkDocs),
"internal" => Ok(Templates::Internal),
"typescript" => Ok(Templates::Typescript),
"typescript-zod" => Ok(Templates::TypescriptZod),
"rust" => Ok(Templates::Rust),
"protobuf" => Ok(Templates::Protobuf),
"graphql" => Ok(Templates::Graphql),
"golang" => Ok(Templates::Golang),
_ => {
let err = format!("Invalid template type: {}", s);
Err(err.into())
Expand Down Expand Up @@ -163,6 +192,7 @@ pub fn render_jinja_template(
match template {
Templates::XmlSchema => convert_model_types(model, &XSD_TYPE_MAPS),
Templates::Typescript => convert_model_types(model, &TYPESCRIPT_TYPE_MAPS),
Templates::Graphql => convert_model_types(model, &GRAPHQL_TYPE_MAPS),
Templates::Shacl | Templates::Shex => {
convert_model_types(model, &SHACL_TYPE_MAPS);
filter_objects_wo_terms(model);
Expand All @@ -176,6 +206,8 @@ pub fn render_jinja_template(

// Add custom functions to the Jinja environment
env.add_function("wrap", wrap_text);
env.add_filter("pascal_case", pascal_case);
env.add_filter("snake_case", snake_case);

// Get the appropriate template
let template = match template {
Expand All @@ -189,6 +221,11 @@ pub fn render_jinja_template(
Templates::PythonPydanticXML => env.get_template("python-pydantic-xml.jinja")?,
Templates::MkDocs => env.get_template("mkdocs.jinja")?,
Templates::Typescript => env.get_template("typescript.jinja")?,
Templates::TypescriptZod => env.get_template("typescript-zod.jinja")?,
Templates::Rust => env.get_template("rust.jinja")?,
Templates::Protobuf => env.get_template("protobuf.jinja")?,
Templates::Graphql => env.get_template("graphql.jinja")?,
Templates::Golang => env.get_template("golang.jinja")?,
_ => {
panic!(
"The template is not available as a Jinja Template and should not be used using the jinja exporter.
Expand Down Expand Up @@ -234,15 +271,34 @@ pub fn render_jinja_template(
/// # Returns
///
/// A string with the wrapped text.
fn wrap_text(text: &str, width: usize, initial_offset: &str, offset: &str) -> String {
fn wrap_text(
text: &str,
width: usize,
initial_offset: &str,
offset: &str,
delimiter: Option<&str>,
) -> String {
let delimiter = delimiter.unwrap_or("");
// Remove multiple spaces
let options = textwrap::Options::new(width)
.initial_indent(initial_offset)
.subsequent_indent(offset)
.width(width)
.break_words(false);

wrap(remove_multiple_spaces(text).as_str(), options).join("\n")
wrap(remove_multiple_spaces(text).as_str(), options).join(&format!("{delimiter}\n"))
}

/// Filter use only for Jinja templates.
/// Converts a string to PascalCase.
fn pascal_case(s: String) -> String {
s.to_case(Case::Pascal)
}

/// Filter use only for Jinja templates.
/// Converts a string to snake_case.
fn snake_case(s: String) -> String {
s.to_case(Case::Snake)
}

/// Removes leading and trailing whitespace and multiple spaces from a string.
Expand Down Expand Up @@ -438,6 +494,17 @@ mod tests {
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_typescript_zod() {
// Arrange
let rendered = build_and_convert(Templates::TypescriptZod);

// Assert
let expected = fs::read_to_string("tests/data/expected_typescript_zod.ts")
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_pydantic() {
// Arrange
Expand All @@ -448,4 +515,48 @@ mod tests {
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_graphql() {
// Arrange
let rendered = build_and_convert(Templates::Graphql);

// Assert
let expected = fs::read_to_string("tests/data/expected_graphql.graphql")
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_golang() {
// Arrange
let rendered = build_and_convert(Templates::Golang);

// Assert
let expected = fs::read_to_string("tests/data/expected_golang.go")
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_rust() {
// Arrange
let rendered = build_and_convert(Templates::Rust);

// Assert
let expected = fs::read_to_string("tests/data/expected_rust.rs")
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}

#[test]
fn test_convert_to_protobuf() {
// Arrange
let rendered = build_and_convert(Templates::Protobuf);

// Assert
let expected = fs::read_to_string("tests/data/expected_protobuf.proto")
.expect("Could not read expected file");
assert_eq!(rendered, expected);
}
}
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ pub mod exporters;
pub mod pipeline;
pub mod validation;

pub(crate) mod attribute;
pub(crate) mod object;
pub(crate) mod xmltype;
pub mod attribute;
pub mod object;
pub mod xmltype;

pub mod prelude {
pub use crate::datamodel::DataModel;
Expand All @@ -51,7 +51,7 @@ pub mod json {
}

pub(crate) mod markdown {
pub(crate) mod frontmatter;
pub mod frontmatter;
pub(crate) mod parser;
pub(crate) mod position;
}
Expand Down
6 changes: 6 additions & 0 deletions src/markdown/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ fn extract_attribute_options(iterator: &mut OffsetIter) -> Vec<String> {
let last_option = options.last_mut().unwrap();
*last_option = format!("{}[]", last_option);
}
Event::Text(text) if text.to_string() != "]" => {
let last_option = options.last_mut().unwrap();
if last_option.to_lowercase().contains("description:") {
*last_option = format!("{} {}", last_option.trim(), text);
}
}
_ => {}
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,51 @@ pub fn process_pipeline(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>
Some(&specs.config),
)?;
}
Templates::TypescriptZod => {
serialize_by_template(
&specs.out,
paths,
&merge_state,
&template,
Some(&specs.config),
)?;
}
Templates::Rust => {
serialize_by_template(
&specs.out,
paths,
&merge_state,
&template,
Some(&specs.config),
)?;
}
Templates::Golang => {
serialize_by_template(
&specs.out,
paths,
&merge_state,
&template,
Some(&specs.config),
)?;
}
Templates::Protobuf => {
serialize_by_template(
&specs.out,
paths,
&merge_state,
&template,
Some(&specs.config),
)?;
}
Templates::Graphql => {
serialize_by_template(
&specs.out,
paths,
&merge_state,
&template,
Some(&specs.config),
)?;
}
Templates::MkDocs => {
// If the template is not set to merge, then disable the navigation.
if let MergeState::Merge = merge_state {
Expand Down
Loading
Loading