mod builder;
mod callbacks;
mod codegen;
mod config;
mod token_gen;
mod types;
pub use builder::Generator;
pub use callbacks::{FieldInfo, ItemInfo, ModuleInfo, ModuleLevel, ParseCallbacks};
pub use codegen::CodeGenerator;
pub use config::{GeneratorConfig, sanitize_rust_identifier};
pub use types::TypeMapper;
use std::path::{Path, PathBuf};
use thiserror::Error;
use crate::idl::IdlError;
use crate::msg::ParseError;
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("File has no extension")]
NoFileExtension,
#[error("Unsupported file extension: {extension}")]
UnsupportedFileExtension {
extension: String,
},
#[error("Cannot extract package name from path: {path}")]
PackageNameExtractionFailed {
path: PathBuf,
},
#[error("Cannot extract module name from path")]
ModuleNameExtractionFailed,
#[error("Output directory is required but not set")]
OutputDirectoryRequired,
#[error("No input files provided to generator")]
NoInputFiles,
}
#[derive(Debug, Error)]
pub enum GenerationError {
#[error("No message found in generated IDL")]
NoMessageInIdl,
#[error("Expected 2 messages (Request/Response) in service IDL, found {found}")]
InvalidServiceIdl {
found: usize,
},
#[error("Expected 3 messages (Goal/Result/Feedback) in action IDL, found {found}")]
InvalidActionIdl {
found: usize,
},
}
#[derive(Debug, Error)]
pub enum GeneratorError {
#[error(transparent)]
MsgParseError(#[from] ParseError),
#[error(transparent)]
IdlParseError(#[from] IdlError),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
ConfigError(#[from] ConfigError),
#[error(transparent)]
GenerationError(#[from] GenerationError),
}
pub type GeneratorResult<T> = Result<T, GeneratorError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FileType {
Message,
Service,
Action,
Idl,
}
impl FileType {
#[must_use]
pub fn extension(&self) -> &'static str {
match self {
Self::Message => "msg",
Self::Service => "srv",
Self::Action => "action",
Self::Idl => "idl",
}
}
#[must_use]
pub fn from_extension(ext: &str) -> Option<Self> {
match ext {
"msg" => Some(Self::Message),
"srv" => Some(Self::Service),
"action" => Some(Self::Action),
"idl" => Some(Self::Idl),
_ => None,
}
}
#[must_use]
pub const fn import_depth(&self) -> usize {
3
}
}
impl std::fmt::Display for FileType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.extension())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InterfaceKind {
Message,
Service,
Action,
}
impl InterfaceKind {
#[must_use]
pub fn dir_name(&self) -> &'static str {
match self {
Self::Message => "msg",
Self::Service => "srv",
Self::Action => "action",
}
}
#[must_use]
pub const fn import_depth(&self) -> usize {
3
}
}
impl std::fmt::Display for InterfaceKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.dir_name())
}
}
#[derive(Debug, Clone)]
pub struct GeneratedCode {
pub code: String,
pub source_file: PathBuf,
pub package_name: String,
pub module_name: String,
pub file_type: FileType,
pub interface_kind: InterfaceKind,
pub dependencies: Vec<String>,
}
impl GeneratedCode {
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> GeneratorResult<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, &self.code)?;
Ok(())
}
#[must_use]
pub fn suggested_filename(&self) -> String {
format!("{}.rs", self.module_name)
}
}