Coverage Report

Created: 2024-10-13 08:39

/Users/andrewlamb/Software/datafusion/datafusion/expr/src/udf_docs.rs
Line
Count
Source (jump to first uncovered line)
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
use datafusion_common::exec_err;
19
use datafusion_common::Result;
20
21
/// Documentation for use by [`ScalarUDFImpl`](crate::ScalarUDFImpl),
22
/// [`AggregateUDFImpl`](crate::AggregateUDFImpl) and [`WindowUDFImpl`](crate::WindowUDFImpl) functions
23
/// that will be used to generate public documentation.
24
///
25
/// The name of the udf will be pulled from the [`ScalarUDFImpl::name`](crate::ScalarUDFImpl::name),
26
/// [`AggregateUDFImpl::name`](crate::AggregateUDFImpl::name) or [`WindowUDFImpl::name`](crate::WindowUDFImpl::name)
27
/// function as appropriate.
28
///
29
/// All strings in the documentation are required to be
30
/// in [markdown format](https://www.markdownguide.org/basic-syntax/).
31
///
32
/// Currently, documentation only supports a single language
33
/// thus all text should be in English.
34
#[derive(Debug, Clone)]
35
pub struct Documentation {
36
    /// the section in the documentation where the UDF will be documented
37
    pub doc_section: DocSection,
38
    /// the description for the UDF
39
    pub description: String,
40
    /// a brief example of the syntax. For example "ascii(str)"
41
    pub syntax_example: String,
42
    /// a sql example for the UDF, usually in the form of a sql prompt
43
    /// query and output. It is strongly recommended to provide an
44
    /// example for anything but the most basic UDF's
45
    pub sql_example: Option<String>,
46
    /// arguments for the UDF which will be displayed in array order.
47
    /// Left member of a pair is the argument name, right is a
48
    /// description for the argument
49
    pub arguments: Option<Vec<(String, String)>>,
50
    /// related functions if any. Values should match the related
51
    /// udf's name exactly. Related udf's must be of the same
52
    /// UDF type (scalar, aggregate or window) for proper linking to
53
    /// occur
54
    pub related_udfs: Option<Vec<String>>,
55
}
56
57
impl Documentation {
58
    /// Returns a new [`DocumentationBuilder`] with no options set.
59
0
    pub fn builder() -> DocumentationBuilder {
60
0
        DocumentationBuilder::new()
61
0
    }
62
}
63
64
#[derive(Debug, Clone, PartialEq)]
65
pub struct DocSection {
66
    /// true to include this doc section in the public
67
    /// documentation, false otherwise
68
    pub include: bool,
69
    /// a display label for the doc section. For example: "Math Expressions"
70
    pub label: &'static str,
71
    /// an optional description for the doc section
72
    pub description: Option<&'static str>,
73
}
74
75
/// A builder to be used for building [`Documentation`]'s.
76
///
77
/// Example:
78
///
79
/// ```rust
80
/// # use datafusion_expr::Documentation;
81
/// # use datafusion_expr::scalar_doc_sections::DOC_SECTION_MATH;
82
/// # use datafusion_common::Result;
83
/// #
84
/// # fn main() -> Result<()> {
85
///       let documentation = Documentation::builder()
86
///           .with_doc_section(DOC_SECTION_MATH)
87
///           .with_description("Add one to an int32")
88
///           .with_syntax_example("add_one(2)")
89
///           .with_argument("arg_1", "The int32 number to add one to")
90
///           .build()?;
91
///       Ok(())  
92
/// # }
93
pub struct DocumentationBuilder {
94
    pub doc_section: Option<DocSection>,
95
    pub description: Option<String>,
96
    pub syntax_example: Option<String>,
97
    pub sql_example: Option<String>,
98
    pub arguments: Option<Vec<(String, String)>>,
99
    pub related_udfs: Option<Vec<String>>,
100
}
101
102
impl DocumentationBuilder {
103
0
    pub fn new() -> Self {
104
0
        Self {
105
0
            doc_section: None,
106
0
            description: None,
107
0
            syntax_example: None,
108
0
            sql_example: None,
109
0
            arguments: None,
110
0
            related_udfs: None,
111
0
        }
112
0
    }
113
114
0
    pub fn with_doc_section(mut self, doc_section: DocSection) -> Self {
115
0
        self.doc_section = Some(doc_section);
116
0
        self
117
0
    }
118
119
0
    pub fn with_description(mut self, description: impl Into<String>) -> Self {
120
0
        self.description = Some(description.into());
121
0
        self
122
0
    }
123
124
0
    pub fn with_syntax_example(mut self, syntax_example: impl Into<String>) -> Self {
125
0
        self.syntax_example = Some(syntax_example.into());
126
0
        self
127
0
    }
128
129
0
    pub fn with_sql_example(mut self, sql_example: impl Into<String>) -> Self {
130
0
        self.sql_example = Some(sql_example.into());
131
0
        self
132
0
    }
133
134
    /// Adds documentation for a specific argument to the documentation.
135
    ///
136
    /// Arguments are displayed in the order they are added.
137
0
    pub fn with_argument(
138
0
        mut self,
139
0
        arg_name: impl Into<String>,
140
0
        arg_description: impl Into<String>,
141
0
    ) -> Self {
142
0
        let mut args = self.arguments.unwrap_or_default();
143
0
        args.push((arg_name.into(), arg_description.into()));
144
0
        self.arguments = Some(args);
145
0
        self
146
0
    }
147
148
    /// Add a standard "expression" argument to the documentation
149
    ///
150
    /// This is similar to  [`Self::with_argument`] except that  a standard
151
    /// description is appended to the end: `"Can be a constant, column, or
152
    /// function, and any combination of arithmetic operators."`
153
    ///
154
    /// The argument is rendered like
155
    ///
156
    /// ```text
157
    /// <arg_name>:
158
    ///   <expression_type> expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators.
159
    /// ```
160
0
    pub fn with_standard_argument(
161
0
        self,
162
0
        arg_name: impl Into<String>,
163
0
        expression_type: impl AsRef<str>,
164
0
    ) -> Self {
165
0
        let expression_type = expression_type.as_ref();
166
0
        self.with_argument(arg_name, format!("{expression_type} expression to operate on. Can be a constant, column, or function, and any combination of operators."))
167
0
    }
168
169
0
    pub fn with_related_udf(mut self, related_udf: impl Into<String>) -> Self {
170
0
        let mut related = self.related_udfs.unwrap_or_default();
171
0
        related.push(related_udf.into());
172
0
        self.related_udfs = Some(related);
173
0
        self
174
0
    }
175
176
0
    pub fn build(self) -> Result<Documentation> {
177
0
        let Self {
178
0
            doc_section,
179
0
            description,
180
0
            syntax_example,
181
0
            sql_example,
182
0
            arguments,
183
0
            related_udfs,
184
0
        } = self;
185
0
186
0
        if doc_section.is_none() {
187
0
            return exec_err!("Documentation must have a doc section");
188
0
        }
189
0
        if description.is_none() {
190
0
            return exec_err!("Documentation must have a description");
191
0
        }
192
0
        if syntax_example.is_none() {
193
0
            return exec_err!("Documentation must have a syntax_example");
194
0
        }
195
0
196
0
        Ok(Documentation {
197
0
            doc_section: doc_section.unwrap(),
198
0
            description: description.unwrap(),
199
0
            syntax_example: syntax_example.unwrap(),
200
0
            sql_example,
201
0
            arguments,
202
0
            related_udfs,
203
0
        })
204
0
    }
205
}
206
207
impl Default for DocumentationBuilder {
208
0
    fn default() -> Self {
209
0
        Self::new()
210
0
    }
211
}