use crate::code_block::Arg;
use crate::lang::CodeLang;
use crate::type_name::TypeName;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct WhereConstraint {
pub(crate) subject: TypeName,
pub(crate) bounds: Vec<TypeName>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum WhereClauseStyle {
Inline,
WhereBlock,
SeparateWhere,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum TypeParamKind {
Constructor1,
Constructor2,
Raw(String),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TypeParamSpec {
pub(crate) name: String,
pub(crate) bounds: Vec<TypeName>,
#[serde(default)]
pub(crate) kind: Option<TypeParamKind>,
#[serde(default)]
pub(crate) is_lifetime: bool,
#[serde(default)]
pub(crate) context_bounds: Vec<TypeName>,
}
impl TypeParamSpec {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
bounds: Vec::new(),
kind: None,
is_lifetime: false,
context_bounds: Vec::new(),
}
}
pub fn lifetime(name: &str) -> Self {
Self {
name: name.to_string(),
bounds: Vec::new(),
kind: None,
is_lifetime: true,
context_bounds: Vec::new(),
}
}
pub fn with_bound(mut self, bound: TypeName) -> Self {
self.bounds.push(bound);
self
}
pub fn with_kind(mut self, kind: TypeParamKind) -> Self {
self.kind = Some(kind);
self
}
pub fn with_context_bound(mut self, bound: TypeName) -> Self {
self.context_bounds.push(bound);
self
}
}
pub fn render_type_params(
params: &[TypeParamSpec],
lang: &dyn CodeLang,
args: &mut Vec<Arg>,
) -> String {
if params.is_empty() {
return String::new();
}
let generic = lang.generic_syntax();
let constraint_kw = generic.constraint_keyword;
let constraint_sep = generic.constraint_separator;
let mut fmt = String::from(generic.open);
let mut first = true;
for tp in params.iter().filter(|p| p.is_lifetime) {
if !first {
fmt.push_str(", ");
}
fmt.push_str(&tp.name);
if !tp.bounds.is_empty() {
fmt.push_str(constraint_kw);
for (j, bound) in tp.bounds.iter().enumerate() {
if j > 0 {
fmt.push_str(constraint_sep);
}
fmt.push_str("%T");
args.push(Arg::TypeName(bound.clone()));
}
}
first = false;
}
for tp in params.iter().filter(|p| !p.is_lifetime) {
if !first {
fmt.push_str(", ");
}
fmt.push_str(&tp.name);
if let Some(ref kind) = tp.kind {
fmt.push_str(&lang.render_type_param_kind(kind));
}
if !tp.bounds.is_empty() {
fmt.push_str(constraint_kw);
for (j, bound) in tp.bounds.iter().enumerate() {
if j > 0 {
fmt.push_str(constraint_sep);
}
fmt.push_str("%T");
args.push(Arg::TypeName(bound.clone()));
}
}
let ctx_kw = generic.context_bound_keyword;
for ctx_bound in &tp.context_bounds {
fmt.push_str(ctx_kw);
fmt.push_str("%T");
args.push(Arg::TypeName(ctx_bound.clone()));
}
first = false;
}
fmt.push_str(generic.close);
fmt
}
pub(crate) fn emit_where_block(
fmt: &mut String,
args: &mut Vec<Arg>,
constraints: &[WhereConstraint],
lang: &dyn CodeLang,
) {
let generic = lang.generic_syntax();
let constraint_sep = generic.constraint_separator;
let indent = lang.block_syntax().indent_unit;
fmt.push_str("\nwhere\n");
for (i, wc) in constraints.iter().enumerate() {
if i > 0 {
fmt.push('\n');
}
fmt.push_str(indent);
fmt.push_str("%T");
args.push(Arg::TypeName(wc.subject.clone()));
fmt.push_str(lang.generic_syntax().constraint_keyword);
for (j, bound) in wc.bounds.iter().enumerate() {
if j > 0 {
fmt.push_str(constraint_sep);
}
fmt.push_str("%T");
args.push(Arg::TypeName(bound.clone()));
}
fmt.push(',');
}
}
pub(crate) fn emit_separate_where_block(
fmt: &mut String,
args: &mut Vec<Arg>,
constraints: &[WhereConstraint],
lang: &dyn CodeLang,
) {
let generic = lang.generic_syntax();
let indent = lang.block_syntax().indent_unit;
for wc in constraints {
fmt.push('\n');
fmt.push_str(indent);
fmt.push_str("where %T");
args.push(Arg::TypeName(wc.subject.clone()));
fmt.push_str(generic.constraint_keyword);
for (j, bound) in wc.bounds.iter().enumerate() {
if j > 0 {
fmt.push_str(generic.constraint_separator);
}
fmt.push_str("%T");
args.push(Arg::TypeName(bound.clone()));
}
}
}