## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/doc/src/lib.rs

    Line
    Count
    Source
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    // https://github.com/apache/datafusion/issues/18503
    19
    #![deny(clippy::needless_pass_by_value)]
    20
    #![cfg_attr(test, allow(clippy::needless_pass_by_value))]
    21
    #![doc(
    22
        html_logo_url = "https://raw.githubusercontent.com/apache/datafusion/19fe44cf2f30cbdd63d4a4f52c74055163c6cc38/docs/logos/standalone_logo/logo_original.svg",
    23
        html_favicon_url = "https://raw.githubusercontent.com/apache/datafusion/19fe44cf2f30cbdd63d4a4f52c74055163c6cc38/docs/logos/standalone_logo/logo_original.svg"
    24
    )]
    25
    #![cfg_attr(docsrs, feature(doc_cfg))]
    26
    27
    mod udaf;
    28
    mod udf;
    29
    mod udwf;
    30
    31
    pub use udaf::aggregate_doc_sections;
    32
    pub use udf::scalar_doc_sections;
    33
    pub use udwf::window_doc_sections;
    34
    35
    #[allow(rustdoc::broken_intra_doc_links)]
    36
    /// Documentation for use by [`ScalarUDFImpl`](ScalarUDFImpl),
    37
    /// [`AggregateUDFImpl`](AggregateUDFImpl) and [`WindowUDFImpl`](WindowUDFImpl) functions.
    38
    ///
    39
    /// See the [`DocumentationBuilder`] to create a new [`Documentation`] struct.
    40
    ///
    41
    /// The DataFusion [SQL function documentation] is automatically  generated from these structs.
    42
    /// The name of the udf will be pulled from the [`ScalarUDFImpl::name`](ScalarUDFImpl::name),
    43
    /// [`AggregateUDFImpl::name`](AggregateUDFImpl::name) or [`WindowUDFImpl::name`](WindowUDFImpl::name)
    44
    /// function as appropriate.
    45
    ///
    46
    /// All strings in the documentation are required to be
    47
    /// in [markdown format](https://www.markdownguide.org/basic-syntax/).
    48
    ///
    49
    /// Currently, documentation only supports a single language
    50
    /// thus all text should be in English.
    51
    ///
    52
    /// [SQL function documentation]: https://datafusion.apache.org/user-guide/sql/index.html
    53
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    54
    pub struct Documentation {
    55
        /// The section in the documentation where the UDF will be documented
    56
        pub doc_section: DocSection,
    57
        /// The description for the UDF
    58
        pub description: String,
    59
        /// A brief example of the syntax. For example "ascii(str)"
    60
        pub syntax_example: String,
    61
        /// A sql example for the UDF, usually in the form of a sql prompt
    62
        /// query and output. It is strongly recommended to provide an
    63
        /// example for anything but the most basic UDF's
    64
        pub sql_example: Option<String>,
    65
        /// Arguments for the UDF which will be displayed in array order.
    66
        /// Left member of a pair is the argument name, right is a
    67
        /// description for the argument
    68
        pub arguments: Option<Vec<(String, String)>>,
    69
        /// A list of alternative syntax examples for a function
    70
        pub alternative_syntax: Option<Vec<String>>,
    71
        /// Related functions if any. Values should match the related
    72
        /// udf's name exactly. Related udf's must be of the same
    73
        /// UDF type (scalar, aggregate or window) for proper linking to
    74
        /// occur
    75
        pub related_udfs: Option<Vec<String>>,
    76
    }
    77
    78
    impl Documentation {
    79
        /// Returns a new [`DocumentationBuilder`] with no options set.
    80
    0
        pub fn builder(
    81
    0
            doc_section: DocSection,
    82
    0
            description: impl Into<String>,
    83
    0
            syntax_example: impl Into<String>,
    84
    0
        ) -> DocumentationBuilder {
    85
    0
            DocumentationBuilder::new_with_details(doc_section, description, syntax_example)
    86
    0
        }
    87
    88
        /// Output the `Documentation` struct in form of custom Rust documentation attributes
    89
        /// It is useful to semi automate during tmigration of UDF documentation
    90
        /// generation from code based to attribute based and can be safely removed after
    91
    0
        pub fn to_doc_attribute(&self) -> String {
    92
    0
            let mut result = String::new();
    93
    94
    0
            result.push_str("#[user_doc(");
    95
            // Doc Section
    96
    0
            result.push_str(
    97
    0
                format!(
    98
    0
                    "\n    doc_section({}label = \"{}\"{}),",
    99
    0
                    if !self.doc_section.include {
    100
    0
                        "include = \"false\", "
    101
                    } else {
    102
    0
                        ""
    103
                    },
    104
                    self.doc_section.label,
    105
    0
                    self.doc_section
    106
    0
                        .description
    107
    0
                        .map(|s| format!(", description = \"{s}\""))
    108
    0
                        .unwrap_or_default(),
    109
                )
    110
    0
                .as_ref(),
    111
            );
    112
    113
            // Description
    114
    0
            result.push_str(format!("\n    description=\"{}\",", self.description).as_ref());
    115
            // Syntax Example
    116
    0
            result.push_str(
    117
    0
                format!("\n    syntax_example=\"{}\",", self.syntax_example).as_ref(),
    118
            );
    119
            // SQL Example
    120
    0
            result.push_str(
    121
    0
                &self
    122
    0
                    .sql_example
    123
    0
                    .clone()
    124
    0
                    .map(|s| format!("\n    sql_example = r#\"{s}\"#,"))
    125
    0
                    .unwrap_or_default(),
    126
            );
    127
    128
    0
            let st_arg_token = " expression to operate on. Can be a constant, column, or function, and any combination of operators.";
    129
            // Standard Arguments
    130
    0
            if let Some(args) = self.arguments.clone() {
    131
    0
                args.iter().for_each(|(name, value)| {
    132
    0
                    if value.contains(st_arg_token) {
    133
    0
                        if name.starts_with("The ") {
    134
    0
                            result.push_str(format!("\n    standard_argument(\n        name = \"{name}\"),").as_ref());
    135
    0
                        } else {
    136
    0
                            result.push_str(format!("\n    standard_argument(\n        name = \"{}\",\n        prefix = \"{}\"\n    ),", name, value.replace(st_arg_token, "")).as_ref());
    137
    0
                        }
    138
    0
                    }
    139
    0
                });
    140
    0
            }
    141
    142
            // Arguments
    143
    0
            if let Some(args) = self.arguments.clone() {
    144
    0
                args.iter().for_each(|(name, value)| {
    145
    0
                    if !value.contains(st_arg_token) {
    146
    0
                        result.push_str(format!("\n    argument(\n        name = \"{name}\",\n        description = \"{value}\"\n    ),").as_ref());
    147
    0
                    }
    148
    0
                });
    149
    0
            }
    150
    151
    0
            if let Some(alt_syntax) = self.alternative_syntax.clone() {
    152
    0
                alt_syntax.iter().for_each(|syntax| {
    153
    0
                    result.push_str(
    154
    0
                        format!("\n    alternative_syntax = \"{syntax}\",").as_ref(),
    155
                    );
    156
    0
                });
    157
    0
            }
    158
    159
            // Related UDFs
    160
    0
            if let Some(related_udf) = self.related_udfs.clone() {
    161
    0
                related_udf.iter().for_each(|udf| {
    162
    0
                    result.push_str(format!("\n    related_udf(name = \"{udf}\"),").as_ref());
    163
    0
                });
    164
    0
            }
    165
    166
    0
            result.push_str("\n)]");
    167
    168
    0
            result
    169
    0
        }
    170
    }
    171
    172
    #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    173
    pub struct DocSection {
    174
        /// True to include this doc section in the public
    175
        /// documentation, false otherwise
    176
        pub include: bool,
    177
        /// A display label for the doc section. For example: "Math Expressions"
    178
        pub label: &'static str,
    179
        /// An optional description for the doc section
    180
        pub description: Option<&'static str>,
    181
    }
    182
    183
    impl Default for DocSection {
    184
        /// Returns a "default" Doc section.
    185
        ///
    186
        /// This is suitable for user defined functions that do not appear in the
    187
        /// DataFusion documentation.
    188
    0
        fn default() -> Self {
    189
    0
            Self {
    190
    0
                include: true,
    191
    0
                label: "Default",
    192
    0
                description: None,
    193
    0
            }
    194
    0
        }
    195
    }
    196
    197
    /// A builder for [`Documentation`]'s.
    198
    ///
    199
    /// Example:
    200
    ///
    201
    /// ```rust
    202
    ///
    203
    /// # fn main() {
    204
    ///     use datafusion_doc::{DocSection, Documentation};
    205
    ///     let doc_section = DocSection {
    206
    ///         include: true,
    207
    ///         label: "Display Label",
    208
    ///         description: None,
    209
    ///     };
    210
    ///
    211
    ///     let documentation = Documentation::builder(doc_section, "Add one to an int32".to_owned(), "add_one(2)".to_owned())
    212
    ///           .with_argument("arg_1", "The int32 number to add one to")
    213
    ///           .build();
    214
    /// # }
    215
    pub struct DocumentationBuilder {
    216
        pub doc_section: DocSection,
    217
        pub description: String,
    218
        pub syntax_example: String,
    219
        pub sql_example: Option<String>,
    220
        pub arguments: Option<Vec<(String, String)>>,
    221
        pub alternative_syntax: Option<Vec<String>>,
    222
        pub related_udfs: Option<Vec<String>>,
    223
    }
    224
    225
    impl DocumentationBuilder {
    226
        /// Creates a new [`DocumentationBuilder`] with all required fields
    227
    0
        pub fn new_with_details(
    228
    0
            doc_section: DocSection,
    229
    0
            description: impl Into<String>,
    230
    0
            syntax_example: impl Into<String>,
    231
    0
        ) -> Self {
    232
    0
            Self {
    233
    0
                doc_section,
    234
    0
                description: description.into(),
    235
    0
                syntax_example: syntax_example.into(),
    236
    0
                sql_example: None,
    237
    0
                arguments: None,
    238
    0
                alternative_syntax: None,
    239
    0
                related_udfs: None,
    240
    0
            }
    241
    0
        }
    242
    243
    0
        pub fn with_doc_section(mut self, doc_section: DocSection) -> Self {
    244
    0
            self.doc_section = doc_section;
    245
    0
            self
    246
    0
        }
    247
    248
        pub fn with_description(mut self, description: impl Into<String>) -> Self {
    249
            self.description = description.into();
    250
            self
    251
        }
    252
    253
        pub fn with_syntax_example(mut self, syntax_example: impl Into<String>) -> Self {
    254
            self.syntax_example = syntax_example.into();
    255
            self
    256
        }
    257
    258
    0
        pub fn with_sql_example(mut self, sql_example: impl Into<String>) -> Self {
    259
    0
            self.sql_example = Some(sql_example.into());
    260
    0
            self
    261
    0
        }
    262
    263
        /// Adds documentation for a specific argument to the documentation.
    264
        ///
    265
        /// Arguments are displayed in the order they are added.
    266
    0
        pub fn with_argument(
    267
    0
            mut self,
    268
    0
            arg_name: impl Into<String>,
    269
    0
            arg_description: impl Into<String>,
    270
    0
        ) -> Self {
    271
    0
            let mut args = self.arguments.unwrap_or_default();
    272
    0
            args.push((arg_name.into(), arg_description.into()));
    273
    0
            self.arguments = Some(args);
    274
    0
            self
    275
    0
        }
    276
    277
        /// Add a standard "expression" argument to the documentation
    278
        ///
    279
        /// The argument is rendered like below if Some() is passed through:
    280
        ///
    281
        /// ```text
    282
        /// <arg_name>:
    283
        ///   <expression_type> expression to operate on. Can be a constant, column, or function, and any combination of operators.
    284
        /// ```
    285
        ///
    286
        /// The argument is rendered like below if None is passed through:
    287
        ///
    288
        ///  ```text
    289
        /// <arg_name>:
    290
        ///   The expression to operate on. Can be a constant, column, or function, and any combination of operators.
    291
        /// ```
    292
    0
        pub fn with_standard_argument(
    293
    0
            self,
    294
    0
            arg_name: impl Into<String>,
    295
    0
            expression_type: Option<&str>,
    296
    0
        ) -> Self {
    297
    0
            let description = format!(
    298
    0
                "{} expression to operate on. Can be a constant, column, or function, and any combination of operators.",
    299
    0
                expression_type.unwrap_or("The")
    300
            );
    301
    0
            self.with_argument(arg_name, description)
    302
    0
        }
    303
    304
    0
        pub fn with_alternative_syntax(mut self, syntax_name: impl Into<String>) -> Self {
    305
    0
            let mut alternative_syntax_array = self.alternative_syntax.unwrap_or_default();
    306
    0
            alternative_syntax_array.push(syntax_name.into());
    307
    0
            self.alternative_syntax = Some(alternative_syntax_array);
    308
    0
            self
    309
    0
        }
    310
    311
    0
        pub fn with_related_udf(mut self, related_udf: impl Into<String>) -> Self {
    312
    0
            let mut related = self.related_udfs.unwrap_or_default();
    313
    0
            related.push(related_udf.into());
    314
    0
            self.related_udfs = Some(related);
    315
    0
            self
    316
    0
        }
    317
    318
        /// Build the documentation from provided components
    319
        ///
    320
        /// Panics if `doc_section`, `description` or `syntax_example` is not set
    321
    0
        pub fn build(self) -> Documentation {
    322
            let Self {
    323
    0
                doc_section,
    324
    0
                description,
    325
    0
                syntax_example,
    326
    0
                sql_example,
    327
    0
                arguments,
    328
    0
                alternative_syntax,
    329
    0
                related_udfs,
    330
    0
            } = self;
    331
    332
    0
            Documentation {
    333
    0
                doc_section,
    334
    0
                description,
    335
    0
                syntax_example,
    336
    0
                sql_example,
    337
    0
                arguments,
    338
    0
                alternative_syntax,
    339
    0
                related_udfs,
    340
    0
            }
    341
    0
        }
    342
    }