Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
19d7539
add `variantly`
JR-1991 Mar 31, 2025
8ab25f6
add json schema import
JR-1991 Mar 31, 2025
95599f8
fix clippy issues
JR-1991 Mar 31, 2025
ac9511a
allow json schema object where required or title is not defined
Logende Apr 2, 2025
5a47ca1
fix clippy warning
Logende Apr 2, 2025
a81adbf
Merge branch 'main' into json-schema-import
JR-1991 Apr 18, 2025
6cf2f38
add json schema import
JR-1991 Apr 18, 2025
2b567f0
allow either JSON Schema or Markdown input
JR-1991 Apr 18, 2025
3cd8b11
wrap in `Some`
JR-1991 Apr 18, 2025
6d20a03
use placeholder
JR-1991 Apr 18, 2025
2520de3
optional `title` and `name`
JR-1991 Apr 18, 2025
fb6e904
add `DataModelError`
JR-1991 Apr 18, 2025
b3ffb86
fix descriptions missing spaces
JR-1991 Apr 18, 2025
4cb723e
check for multiples
JR-1991 Apr 18, 2025
c18ec0b
ensure there are no duplicate spaces
JR-1991 Apr 18, 2025
7c8a937
fix clippy issues
JR-1991 Apr 18, 2025
c8654cc
add `openai` option for JSON export
JR-1991 May 2, 2025
4da37ba
adapt to expected structured output schema
JR-1991 May 2, 2025
b6e2bb4
do not use temp for reasoning model
JR-1991 May 2, 2025
bfcd13c
accept `JSValue` and import from an object
JR-1991 May 5, 2025
e68c0d9
allow large return value
JR-1991 May 5, 2025
009ac8a
enum keys to upper case
JR-1991 May 9, 2025
7d2883c
check if input is json
JR-1991 May 9, 2025
83381a1
ensure enum props are of type `string`
JR-1991 May 9, 2025
8e62780
parse `allOf` into `oneOf` for now
JR-1991 May 9, 2025
9a159aa
`parent` to multiple `mixins`
JR-1991 May 10, 2025
79cf316
adapt to rename
JR-1991 May 10, 2025
dfce34e
add `mixins` and set `is_a` to term
JR-1991 May 10, 2025
4980869
handle `anyOf` and `oneOf` equally
JR-1991 May 10, 2025
81de187
add `all_of`
JR-1991 May 10, 2025
979cad0
handle singular `allOf`
JR-1991 May 10, 2025
a76db6c
Merge branch 'main' into json-schema-import
JR-1991 May 26, 2025
5b16373
Revert "Merge branch 'main' into json-schema-import"
JR-1991 May 26, 2025
ef20065
return `Err` if `allOf` is greater one
JR-1991 May 27, 2025
70dadf8
Merge branch 'main' into json-schema-import
JR-1991 May 27, 2025
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
158 changes: 122 additions & 36 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 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 <jan.range@simtech.uni-stuttgart.de>"]
description = "A tool to generate models, code and schemas from markdown files"
version = "0.2.4"
version = "0.2.3"
edition = "2021"
license = "MIT"
repository = "https://github.com/FAIRChemistry/md-models"
Expand Down Expand Up @@ -44,6 +44,7 @@ petgraph = { version = "0.7.1", features = ["serde"] }
schemars = { version = "0.8.22", features = ["derive_json_schema"] }
json-patch = "4.0.0"
thiserror = "2.0.12"
variantly = "0.4.0"

[features]
default = ["openai"]
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.2.4"
version = "0.2.3"
description = "A tool to generate models, code and schemas from markdown files"
requires-python = ">=3.8"
classifiers = [
Expand Down
30 changes: 28 additions & 2 deletions src/bin/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,12 @@ fn query_llm(args: ExtractArgs) -> Result<(), Box<dyn Error>> {
fn convert(args: ConvertArgs) -> Result<(), Box<dyn Error>> {
// Parse the markdown model.
let path = resolve_input_path(&args.input);
let mut model = DataModel::from_markdown(&path)?;

let mut model = if is_json_schema(&path)? {
DataModel::from_json_schema(&path)?
} else {
DataModel::from_markdown(&path)?
};

// Special case JSON Schema all
if let Templates::JsonSchemaAll = args.template {
Expand All @@ -336,7 +341,9 @@ fn convert(args: ConvertArgs) -> Result<(), Box<dyn Error>> {
.map(|s| (s.clone(), "true".to_string()))
.collect();
let rendered = match args.template {
Templates::JsonSchema => model.json_schema(args.root, false)?,
Templates::JsonSchema => {
model.json_schema(args.root, args.options.contains(&"openai".to_string()))?
}
Templates::Linkml => serialize_linkml(model, args.output.as_ref())?,
_ => render_jinja_template(&args.template, &mut model, Some(&config))?,
};
Expand All @@ -354,6 +361,25 @@ fn convert(args: ConvertArgs) -> Result<(), Box<dyn Error>> {
Ok(())
}

/// Checks if the input is a JSON Schema.
///
/// # Arguments
///
/// * `path` - The path to the input file.
///
/// # Returns
///
/// True if the input is a JSON Schema, false otherwise.
fn is_json_schema(path: &PathBuf) -> Result<bool, Box<dyn Error>> {
let content = std::fs::read_to_string(path)?;
let parsed = serde_json::from_str::<serde_json::Value>(&content);

match parsed {
Ok(value) => Ok(value.is_object()),
Err(_) => Ok(false),
}
}

/// Resolves the input path based on the InputType.
///
/// If the input is a remote URL, it fetches the content and saves it to a temporary file.
Expand Down
23 changes: 23 additions & 0 deletions src/bindings/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use crate::datamodel::DataModel;
use crate::exporters::Templates;
use crate::json::export::to_json_schema;
use crate::json::schema::SchemaObject;
use crate::validation::Validator;
use wasm_bindgen::prelude::*;

Expand Down Expand Up @@ -71,6 +72,28 @@ pub fn convert_to(markdown_content: &str, template: Templates) -> Result<String,
.map_err(|e| JsValue::from_str(&format!("Error converting markdown content: {}", e)))
}

/// Parses the given JSON schema string into a `DataModel`.
///
/// # Arguments
///
/// * `json_schema` - A string slice that holds the JSON schema to be parsed.
///
/// # Returns
///
/// A `String` or an error `JsError`.
#[wasm_bindgen]
pub fn from_json_schema(json_schema: JsValue) -> Result<String, JsError> {
let schema: SchemaObject = serde_wasm_bindgen::from_value(json_schema)
.map_err(|e| JsError::new(&format!("Error deserializing JSON schema: {}", e)))?;

let mut model = DataModel::from_json_schema_object(schema)
.map_err(|e| JsError::new(&format!("Error parsing JSON schema: {}", e)))?;

model
.convert_to(&Templates::Markdown, None)
.map_err(|e| JsError::new(&format!("Error converting markdown content: {}", e)))
}

/// Returns the JSON schema for the given markdown content.
///
/// # Arguments
Expand Down
61 changes: 60 additions & 1 deletion src/datamodel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ use std::{error::Error, fs, path::Path};
use log::error;
use serde::{Deserialize, Serialize};

use crate::error::DataModelError;
use crate::exporters::{render_jinja_template, Templates};
use crate::json::export::to_json_schema;
use crate::json::schema::SchemaObject;
use crate::json::validation::{validate_json, ValidationError};
use crate::linkml::export::serialize_linkml;
use crate::markdown::frontmatter::FrontMatter;
use crate::markdown::parser::parse_markdown;
use crate::markdown::parser::{parse_markdown, validate_model};
use crate::object::{Enumeration, Object};
use crate::validation::Validator;
use colored::Colorize;
Expand Down Expand Up @@ -365,6 +367,63 @@ impl DataModel {
pub fn from_markdown_string(content: &str) -> Result<Self, Validator> {
parse_markdown(content, None)
}

/// Parse a JSON schema file and create a data model
///
/// * `path` - Path to the JSON schema file
///
/// # Returns
/// A data model
#[allow(clippy::result_large_err)]
pub fn from_json_schema(path: &Path) -> Result<Self, DataModelError> {
let content = fs::read_to_string(path)?;
let schema: SchemaObject = serde_json::from_str(&content)?;
let model: DataModel = schema
.try_into()
.expect("Could not convert schema to data model");

// Validate the data model
validate_model(&model).map_err(DataModelError::ValidationError)?;

Ok(model)
}

/// Parse a JSON schema string and create a data model
///
/// * `content` - The JSON schema string
///
/// # Returns
/// A data model
#[allow(clippy::result_large_err)]
pub fn from_json_schema_string(content: &str) -> Result<Self, DataModelError> {
let schema: SchemaObject = serde_json::from_str(content)?;
let model: DataModel = schema
.try_into()
.expect("Could not convert schema to data model");

// Validate the data model
validate_model(&model).map_err(DataModelError::ValidationError)?;

Ok(model)
}

/// Parse a JSON schema object and create a data model
///
/// * `schema` - The JSON schema object
///
/// # Returns
/// A data model
#[allow(clippy::result_large_err)]
pub fn from_json_schema_object(schema: SchemaObject) -> Result<Self, DataModelError> {
let model: DataModel = schema
.try_into()
.expect("Could not convert schema to data model");

// Validate the data model
validate_model(&model).map_err(DataModelError::ValidationError)?;

Ok(model)
}
}

#[cfg(test)]
Expand Down
34 changes: 34 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! Error types for the mdmodels crate.
//!
//! This module defines the error types used throughout the crate,
//! particularly for operations related to data model processing.

use thiserror::Error;

use crate::prelude::Validator;

/// Errors that can occur when working with data models.
///
/// This enum represents the various error conditions that may arise
/// during data model operations such as validation, deserialization,
/// and file I/O.
#[derive(Debug, Error)]
pub enum DataModelError {
/// Error that occurs when a data model fails validation.
///
/// Contains the validator with detailed validation errors.
#[error("Validation error: {0}")]
ValidationError(Validator),

/// Error that occurs when deserializing JSON data.
///
/// This typically happens when parsing JSON schemas or model data.
#[error("Deserialize error: {0}")]
DeserializeError(#[from] serde_json::Error),

/// Error that occurs when reading files.
///
/// This can happen when attempting to read model files from disk.
#[error("Read error: {0}")]
ReadError(#[from] std::io::Error),
}
Loading
Loading