use std::path::{Component, Path, PathBuf};
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct PackageName(String);
impl PackageName {
pub fn new(name: String) -> Result<Self> {
let mut components = Path::new(&name).components();
let single_segment =
matches!(components.next(), Some(Component::Normal(_))) && components.next().is_none();
if single_segment && !name.contains(['/', '\\']) {
Ok(Self(name))
} else {
Err(Error::InvalidPackageName { name })
}
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for PackageName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone)]
pub struct GenerateConfig {
pub spec: PathBuf,
pub output: PathBuf,
pub package_name: PackageName,
pub project_name: String,
pub client_class_name: Option<String>,
pub extra_fields: crate::settings::ExtraFields,
pub layout: crate::settings::Layout,
pub enum_type: crate::settings::EnumType,
pub default_max_retries: u32,
}
impl GenerateConfig {
pub fn new(
spec: PathBuf,
output: PathBuf,
package_name: Option<String>,
project_name: Option<String>,
client_class_name: Option<String>,
extra_fields: crate::settings::ExtraFields,
title: &str,
) -> Result<Self> {
let package_name = match package_name {
Some(name) => PackageName::new(name)?,
None => PackageName::new(default_package_name(title))?,
};
let project_name = project_name.unwrap_or_else(|| package_name.as_str().to_string());
Ok(Self {
spec,
output,
package_name,
project_name,
client_class_name,
extra_fields,
layout: crate::settings::Layout::default(),
enum_type: crate::settings::EnumType::default(),
default_max_retries: crate::settings::DEFAULT_MAX_RETRIES,
})
}
}
#[must_use]
pub fn default_client_class_name(package_name: &str) -> String {
format!("{}Api", crate::naming::to_pascal_case(package_name))
}
fn default_package_name(title: &str) -> String {
let snake = crate::naming::prose_identifier(title);
if snake.is_empty() {
"client".to_string()
} else {
snake
}
}