| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| //! Markdown renderer for DataFusion example documentation. | |
| //! | |
| //! This module takes parsed example metadata and generates the | |
| //! `README.md` content for `datafusion-examples`, including group | |
| //! sections and example tables. | |
| use std::fmt::Write as _; | |
| use std::path::PathBuf; | |
| use datafusion::error::{DataFusionError, Result}; | |
| use crate::utils::example_metadata::discover::discover_example_groups; | |
| use crate::utils::example_metadata::model::ExampleGroup; | |
| use crate::utils::example_metadata::{Category, RepoLayout}; | |
| const STATIC_HEADER: &str = r#"<!--- | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, | |
| software distributed under the License is distributed on an | |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| KIND, either express or implied. See the License for the | |
| specific language governing permissions and limitations | |
| under the License. | |
| --> | |
| # DataFusion Examples | |
| This crate includes end to end, highly commented examples of how to use | |
| various DataFusion APIs to help you get started. | |
| ## Prerequisites | |
| Run `git submodule update --init` to init test files. | |
| ## Running Examples | |
| To run an example, use the `cargo run` command, such as: | |
| ```bash | |
| git clone https://github.com/apache/datafusion | |
| cd datafusion | |
| # Download test data | |
| git submodule update --init | |
| # Change to the examples directory | |
| cd datafusion-examples/examples | |
| # Run all examples in a group | |
| cargo run --example <group> -- all | |
| # Run a specific example within a group | |
| cargo run --example <group> -- <subcommand> | |
| # Run all examples in the `dataframe` group | |
| cargo run --example dataframe -- all | |
| # Run a single example from the `dataframe` group | |
| # (apply the same pattern for any other group) | |
| cargo run --example dataframe -- dataframe | |
| ``` | |
| "#; | |
| /// Generates Markdown documentation for DataFusion examples. | |
| /// | |
| /// If `group` is `None`, documentation is generated for all example groups. | |
| /// If `group` is `Some`, only that group is rendered. | |
| /// | |
| /// # Errors | |
| /// | |
| /// Returns an error if: | |
| /// - the requested group does not exist | |
| /// - a `main.rs` file is missing | |
| /// - documentation comments are malformed | |
| pub fn generate_examples_readme( | |
| layout: &RepoLayout, | |
| group: Option<&str>, | |
| ) -> Result<String> { | |
| let examples_root = layout.examples_root(); | |
| let mut out = String::new(); | |
| out.push_str(STATIC_HEADER); | |
| let group_dirs: Vec<PathBuf> = match group { | |
| Some(name) => { | |
| let dir = examples_root.join(name); | |
| if !dir.is_dir() { | |
| return Err(DataFusionError::Execution(format!( | |
| "Example group `{name}` does not exist" | |
| ))); | |
| } | |
| vec![dir] | |
| } | |
| None => discover_example_groups(&examples_root)?, | |
| }; | |
| for group_dir in group_dirs { | |
| let raw_name = | |
| group_dir | |
| .file_name() | |
| .and_then(|s| s.to_str()) | |
| .ok_or_else(|| { | |
| DataFusionError::Execution("Invalid example group dir".to_string()) | |
| })?; | |
| let category = Category::for_group(raw_name); | |
| let group = ExampleGroup::from_dir(&group_dir, category)?; | |
| out.push_str(&group.render_markdown()); | |
| } | |
| Ok(out) | |
| } | |
| impl ExampleGroup { | |
| /// Renders this example group as a Markdown section for the README. | |
| pub fn render_markdown(&self) -> String { | |
| let mut out = String::new(); | |
| write!(out, "\n## {} Examples\n\n", self.name.title()).ok(); | |
| write!(out, "### Group: `{}`\n\n", self.name.raw()).ok(); | |
| write!(out, "#### Category: {}\n\n", self.category.name()).ok(); | |
| out.push_str("| Subcommand | File Path | Description |\n"); | |
| out.push_str("| --- | --- | --- |\n"); | |
| for example in &self.examples { | |
| writeln!( | |
| out, | |
| "| {} | [`{}/{}`](examples/{}/{}) | {} |", | |
| example.subcommand, | |
| self.name.raw(), | |
| example.file, | |
| self.name.raw(), | |
| example.file, | |
| example.desc | |
| ) | |
| .ok(); | |
| } | |
| out | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| use crate::utils::example_metadata::test_utils::assert_exec_err_contains; | |
| use std::fs; | |
| use tempfile::TempDir; | |
| #[test] | |
| fn single_group_generation_works() { | |
| let tmp = TempDir::new().unwrap(); | |
| // Fake repo root | |
| let layout = RepoLayout::from_root(tmp.path().to_path_buf()); | |
| // Create: datafusion-examples/examples/builtin_functions | |
| let examples_dir = layout.example_group_dir("builtin_functions"); | |
| fs::create_dir_all(&examples_dir).unwrap(); | |
| fs::write( | |
| examples_dir.join("main.rs"), | |
| "//! - `x`\n//! (file: foo.rs, desc: test)", | |
| ) | |
| .unwrap(); | |
| let out = generate_examples_readme(&layout, Some("builtin_functions")).unwrap(); | |
| assert!(out.contains("Builtin Functions")); | |
| assert!(out.contains("| x | [`builtin_functions/foo.rs`]")); | |
| } | |
| #[test] | |
| fn single_group_generation_fails_if_group_missing() { | |
| let tmp = TempDir::new().unwrap(); | |
| let layout = RepoLayout::from_root(tmp.path().to_path_buf()); | |
| let err = generate_examples_readme(&layout, Some("missing_group")).unwrap_err(); | |
| assert_exec_err_contains(err, "Example group `missing_group` does not exist"); | |
| } | |
| } |