use super::EmitOptions;
use crate::model::Attribute;
use crate::model::Directive;
use crate::model::Docstring;
use crate::model::ExceptionEntry;
use crate::model::FreeSectionKind;
use crate::model::Method;
use crate::model::Parameter;
use crate::model::Reference;
use crate::model::Return;
use crate::model::Section;
use crate::model::SeeAlsoEntry;
pub fn emit_sphinx(doc: &Docstring, options: &EmitOptions) -> String {
let mut out = String::new();
if let Some(ref summary) = doc.summary {
out.push_str(summary);
out.push('\n');
}
if let Some(ref ext) = doc.extended_summary {
out.push('\n');
out.push_str(ext);
out.push('\n');
}
for directive in &doc.directives {
out.push('\n');
emit_directive(&mut out, directive);
}
for section in &doc.sections {
out.push('\n');
emit_section(&mut out, section);
}
if options.base_indent == 0 {
return out;
}
super::indent_lines(&out, options.base_indent)
}
fn emit_section(out: &mut String, section: &Section) {
match section {
Section::Parameters(params)
| Section::KeywordParameters(params)
| Section::OtherParameters(params)
| Section::Receives(params) => {
for p in params {
emit_parameter(out, p);
}
}
Section::Returns(returns) | Section::Yields(returns) => {
for r in returns {
emit_return(out, r);
}
}
Section::Raises(entries) => {
for e in entries {
emit_exception(out, e);
}
}
Section::Warns(entries) => {
for e in entries {
emit_warning(out, e);
}
}
Section::Attributes(attrs) => {
for a in attrs {
emit_attribute(out, a);
}
}
Section::Methods(methods) => {
emit_methods(out, methods);
}
Section::SeeAlso(items) => {
emit_see_also(out, items);
}
Section::References(refs) => {
for r in refs {
emit_reference(out, r);
}
}
Section::FreeText { kind, body } => {
emit_free_text(out, kind, body);
}
}
}
fn emit_parameter(out: &mut String, p: &Parameter) {
let mut desc = p.description.clone().unwrap_or_default();
if let Some(ref dv) = p.default_value {
if desc.is_empty() {
desc.push_str("defaults to ");
} else {
desc.push_str(", defaults to ");
}
desc.push_str(dv);
}
for name in &p.names {
out.push_str(":param ");
out.push_str(name);
out.push(':');
if !desc.is_empty() {
out.push(' ');
emit_multiline(out, &desc, 4);
}
out.push('\n');
if p.type_annotation.is_some() || p.is_optional {
out.push_str(":type ");
out.push_str(name);
out.push_str(": ");
if let Some(ref ty) = p.type_annotation {
out.push_str(ty);
}
if p.is_optional {
if p.type_annotation.is_some() {
out.push_str(", ");
}
out.push_str("optional");
}
out.push('\n');
}
}
}
fn emit_return(out: &mut String, r: &Return) {
out.push_str(":return:");
if let Some(ref desc) = r.description {
out.push(' ');
emit_multiline(out, desc, 4);
}
out.push('\n');
if let Some(ref ty) = r.type_annotation {
out.push_str(":rtype: ");
out.push_str(ty);
out.push('\n');
}
}
fn emit_exception(out: &mut String, e: &ExceptionEntry) {
out.push_str(":raises ");
out.push_str(&e.type_name);
out.push(':');
if let Some(ref desc) = e.description {
out.push(' ');
emit_multiline(out, desc, 4);
}
out.push('\n');
}
fn emit_warning(out: &mut String, e: &ExceptionEntry) {
out.push_str(".. warning::\n\n");
let mut body = e.type_name.clone();
if let Some(ref desc) = e.description {
body.push_str(": ");
body.push_str(desc);
}
emit_indented_body(out, &body, 4);
}
fn emit_attribute(out: &mut String, a: &Attribute) {
for name in &a.names {
out.push_str(":var ");
out.push_str(name);
out.push(':');
if let Some(ref desc) = a.description {
out.push(' ');
emit_multiline(out, desc, 4);
}
out.push('\n');
if let Some(ref ty) = a.type_annotation {
out.push_str(":vartype ");
out.push_str(name);
out.push_str(": ");
out.push_str(ty);
out.push('\n');
}
}
}
fn emit_methods(out: &mut String, methods: &[Method]) {
out.push_str(".. rubric:: Methods\n\n");
for m in methods {
out.push_str("* ");
out.push_str(&m.name);
if let Some(ref ty) = m.type_annotation {
out.push('(');
out.push_str(ty);
out.push(')');
}
if let Some(ref desc) = m.description {
out.push_str(": ");
out.push_str(desc);
}
out.push('\n');
}
}
fn emit_see_also(out: &mut String, items: &[SeeAlsoEntry]) {
out.push_str(".. seealso::\n\n");
for item in items {
let mut body = item.names.join(", ");
if let Some(ref desc) = item.description {
body.push_str(": ");
body.push_str(desc);
}
emit_indented_body(out, &body, 4);
}
}
fn emit_reference(out: &mut String, r: &Reference) {
if let Some(ref label) = r.label {
out.push_str(".. [");
out.push_str(label);
out.push(']');
if let Some(ref content) = r.content {
out.push(' ');
out.push_str(content);
}
} else if let Some(ref content) = r.content {
out.push_str(content);
}
out.push('\n');
}
fn emit_directive(out: &mut String, directive: &Directive) {
out.push_str(".. ");
out.push_str(&directive.name);
out.push_str("::");
if let Some(ref argument) = directive.argument {
out.push(' ');
out.push_str(argument);
}
out.push('\n');
if let Some(ref desc) = directive.description {
emit_indented_body(out, desc, 4);
}
}
fn emit_free_text(out: &mut String, kind: &FreeSectionKind, body: &str) {
match kind {
FreeSectionKind::Examples => {
out.push_str(".. rubric:: Examples\n\n");
emit_indented_body(out, body, 0);
}
FreeSectionKind::Unknown(name) => {
out.push_str(".. admonition:: ");
out.push_str(name);
out.push_str("\n\n");
emit_indented_body(out, body, 4);
}
_ => {
out.push_str(".. ");
out.push_str(admonition_name(kind));
out.push_str("::\n\n");
emit_indented_body(out, body, 4);
}
}
}
fn admonition_name(kind: &FreeSectionKind) -> &str {
match kind {
FreeSectionKind::Notes => "note",
FreeSectionKind::Warnings => "warning",
FreeSectionKind::Todo => "todo",
FreeSectionKind::Attention => "attention",
FreeSectionKind::Caution => "caution",
FreeSectionKind::Danger => "danger",
FreeSectionKind::Error => "error",
FreeSectionKind::Hint => "hint",
FreeSectionKind::Important => "important",
FreeSectionKind::Tip => "tip",
FreeSectionKind::Examples => "rubric",
FreeSectionKind::Unknown(_) => "admonition",
}
}
fn emit_multiline(out: &mut String, text: &str, indent: usize) {
super::emit_multiline_with_indentation(out, text, indent);
}
fn emit_indented_body(out: &mut String, body: &str, indent: usize) {
let prefix = " ".repeat(indent);
for line in body.lines() {
if !line.is_empty() {
out.push_str(&prefix);
out.push_str(line);
}
out.push('\n');
}
}