init: archlens — architecture diagram generator
Some checks failed
CI / Check / Test (push) Failing after 1m24s
Some checks failed
CI / Check / Test (push) Failing after 1m24s
Hex arch + DDD, tree-sitter parsing, Mermaid/ASCII output. Supports Rust + Python. 92 tests. CI, diff, --check for staleness detection.
This commit is contained in:
78
crates/domain/src/aggregates/code_graph.rs
Normal file
78
crates/domain/src/aggregates/code_graph.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{CodeElement, ModuleName, Relationship};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CodeGraph {
|
||||
elements: Vec<CodeElement>,
|
||||
relationships: Vec<Relationship>,
|
||||
}
|
||||
|
||||
impl Default for CodeGraph {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeGraph {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
elements: Vec::new(),
|
||||
relationships: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_element(&mut self, element: CodeElement) {
|
||||
self.elements.push(element);
|
||||
}
|
||||
|
||||
pub fn add_relationship(&mut self, relationship: Relationship) {
|
||||
self.relationships.push(relationship);
|
||||
}
|
||||
|
||||
pub fn elements(&self) -> &[CodeElement] {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
pub fn relationships(&self) -> &[Relationship] {
|
||||
&self.relationships
|
||||
}
|
||||
|
||||
pub fn modules(&self) -> Vec<ModuleName> {
|
||||
let mut seen = HashSet::new();
|
||||
let mut modules = Vec::new();
|
||||
|
||||
for element in &self.elements {
|
||||
if let Some(module) = element.module()
|
||||
&& seen.insert(module.as_str().to_string())
|
||||
{
|
||||
modules.push(module.clone());
|
||||
}
|
||||
}
|
||||
|
||||
modules
|
||||
}
|
||||
|
||||
pub fn subgraph_by_module(&self, module: &ModuleName) -> CodeGraph {
|
||||
let filtered_elements: Vec<CodeElement> = self
|
||||
.elements
|
||||
.iter()
|
||||
.filter(|e| e.module().is_some_and(|m| m == module))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let element_names: HashSet<&str> = filtered_elements.iter().map(|e| e.name()).collect();
|
||||
|
||||
let filtered_relationships: Vec<Relationship> = self
|
||||
.relationships
|
||||
.iter()
|
||||
.filter(|r| element_names.contains(r.source()) && element_names.contains(r.target()))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
CodeGraph {
|
||||
elements: filtered_elements,
|
||||
relationships: filtered_relationships,
|
||||
}
|
||||
}
|
||||
}
|
||||
3
crates/domain/src/aggregates/mod.rs
Normal file
3
crates/domain/src/aggregates/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
mod code_graph;
|
||||
|
||||
pub use code_graph::CodeGraph;
|
||||
111
crates/domain/src/entities/code_element.rs
Normal file
111
crates/domain/src/entities/code_element.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use crate::{CodeElementKind, DomainError, FilePath, ModuleName, Visibility};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CodeElement {
|
||||
name: String,
|
||||
kind: CodeElementKind,
|
||||
file_path: FilePath,
|
||||
line: usize,
|
||||
visibility: Visibility,
|
||||
module: Option<ModuleName>,
|
||||
generics: Vec<String>,
|
||||
attributes: Vec<String>,
|
||||
fields: Vec<String>,
|
||||
methods: Vec<String>,
|
||||
}
|
||||
|
||||
impl CodeElement {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
kind: CodeElementKind,
|
||||
file_path: FilePath,
|
||||
line: usize,
|
||||
) -> Result<Self, DomainError> {
|
||||
let trimmed = name.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err(DomainError::EmptyValue("CodeElement name"));
|
||||
}
|
||||
Ok(Self {
|
||||
name: trimmed.to_string(),
|
||||
kind,
|
||||
file_path,
|
||||
line,
|
||||
visibility: Visibility::Public,
|
||||
module: None,
|
||||
generics: Vec::new(),
|
||||
attributes: Vec::new(),
|
||||
fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_visibility(mut self, visibility: Visibility) -> Self {
|
||||
self.visibility = visibility;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_module(mut self, module: ModuleName) -> Self {
|
||||
self.module = Some(module);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_generics(mut self, generics: Vec<String>) -> Self {
|
||||
self.generics = generics;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_attributes(mut self, attributes: Vec<String>) -> Self {
|
||||
self.attributes = attributes;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> CodeElementKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn file_path(&self) -> &FilePath {
|
||||
&self.file_path
|
||||
}
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
|
||||
pub fn visibility(&self) -> Visibility {
|
||||
self.visibility
|
||||
}
|
||||
|
||||
pub fn module(&self) -> Option<&ModuleName> {
|
||||
self.module.as_ref()
|
||||
}
|
||||
|
||||
pub fn generics(&self) -> &[String] {
|
||||
&self.generics
|
||||
}
|
||||
|
||||
pub fn attributes(&self) -> &[String] {
|
||||
&self.attributes
|
||||
}
|
||||
|
||||
pub fn with_fields(mut self, fields: Vec<String>) -> Self {
|
||||
self.fields = fields;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_methods(mut self, methods: Vec<String>) -> Self {
|
||||
self.methods = methods;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &[String] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
pub fn methods(&self) -> &[String] {
|
||||
&self.methods
|
||||
}
|
||||
}
|
||||
5
crates/domain/src/entities/mod.rs
Normal file
5
crates/domain/src/entities/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
mod code_element;
|
||||
mod relationship;
|
||||
|
||||
pub use code_element::CodeElement;
|
||||
pub use relationship::Relationship;
|
||||
49
crates/domain/src/entities/relationship.rs
Normal file
49
crates/domain/src/entities/relationship.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use crate::{DomainError, FilePath, RelationshipKind};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Relationship {
|
||||
source: String,
|
||||
target: String,
|
||||
kind: RelationshipKind,
|
||||
source_file: Option<FilePath>,
|
||||
}
|
||||
|
||||
impl Relationship {
|
||||
pub fn new(source: &str, target: &str, kind: RelationshipKind) -> Result<Self, DomainError> {
|
||||
let source = source.trim();
|
||||
let target = target.trim();
|
||||
if source.is_empty() {
|
||||
return Err(DomainError::EmptyValue("Relationship source"));
|
||||
}
|
||||
if target.is_empty() {
|
||||
return Err(DomainError::EmptyValue("Relationship target"));
|
||||
}
|
||||
Ok(Self {
|
||||
source: source.to_string(),
|
||||
target: target.to_string(),
|
||||
kind,
|
||||
source_file: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_source_file(mut self, file: FilePath) -> Self {
|
||||
self.source_file = Some(file);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(&self) -> &str {
|
||||
&self.source
|
||||
}
|
||||
|
||||
pub fn target(&self) -> &str {
|
||||
&self.target
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> RelationshipKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn source_file(&self) -> Option<&FilePath> {
|
||||
self.source_file.as_ref()
|
||||
}
|
||||
}
|
||||
14
crates/domain/src/error.rs
Normal file
14
crates/domain/src/error.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DomainError {
|
||||
#[error("{0} cannot be empty")]
|
||||
EmptyValue(&'static str),
|
||||
|
||||
#[error("failed to analyze: {0}")]
|
||||
AnalysisError(String),
|
||||
|
||||
#[error("IO error: {0}")]
|
||||
IoError(String),
|
||||
|
||||
#[error("config error: {0}")]
|
||||
ConfigError(String),
|
||||
}
|
||||
14
crates/domain/src/lib.rs
Normal file
14
crates/domain/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
mod error;
|
||||
|
||||
pub mod aggregates;
|
||||
pub mod entities;
|
||||
pub mod ports;
|
||||
pub mod value_objects;
|
||||
|
||||
pub use aggregates::CodeGraph;
|
||||
pub use entities::{CodeElement, Relationship};
|
||||
pub use error::DomainError;
|
||||
pub use value_objects::analysis::{AnalysisConfig, AnalysisResult, AnalysisWarning};
|
||||
pub use value_objects::graph::{CodeElementKind, RelationshipKind, Visibility};
|
||||
pub use value_objects::output::{DiagramLevel, OutputConfig, RenderOutput, RenderedFile};
|
||||
pub use value_objects::source::{FilePath, Language, ModuleName, SourceFile};
|
||||
6
crates/domain/src/ports/config_loader.rs
Normal file
6
crates/domain/src/ports/config_loader.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use crate::{AnalysisConfig, DomainError, OutputConfig};
|
||||
|
||||
pub trait ConfigLoader {
|
||||
fn load_analysis_config(&self) -> Result<AnalysisConfig, DomainError>;
|
||||
fn load_output_config(&self) -> Result<OutputConfig, DomainError>;
|
||||
}
|
||||
5
crates/domain/src/ports/diagram_renderer.rs
Normal file
5
crates/domain/src/ports/diagram_renderer.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use crate::{CodeGraph, DomainError, RenderOutput};
|
||||
|
||||
pub trait DiagramRenderer {
|
||||
fn render(&self, graph: &CodeGraph) -> Result<RenderOutput, DomainError>;
|
||||
}
|
||||
9
crates/domain/src/ports/file_discovery.rs
Normal file
9
crates/domain/src/ports/file_discovery.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use crate::{AnalysisConfig, DomainError, SourceFile};
|
||||
|
||||
pub trait FileDiscovery {
|
||||
fn discover(
|
||||
&self,
|
||||
root: &std::path::Path,
|
||||
config: &AnalysisConfig,
|
||||
) -> Result<Vec<SourceFile>, DomainError>;
|
||||
}
|
||||
13
crates/domain/src/ports/mod.rs
Normal file
13
crates/domain/src/ports/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
mod config_loader;
|
||||
mod diagram_renderer;
|
||||
mod file_discovery;
|
||||
mod output_writer;
|
||||
mod project_analyzer;
|
||||
mod source_analyzer;
|
||||
|
||||
pub use config_loader::ConfigLoader;
|
||||
pub use diagram_renderer::DiagramRenderer;
|
||||
pub use file_discovery::FileDiscovery;
|
||||
pub use output_writer::OutputWriter;
|
||||
pub use project_analyzer::ProjectAnalyzer;
|
||||
pub use source_analyzer::SourceAnalyzer;
|
||||
5
crates/domain/src/ports/output_writer.rs
Normal file
5
crates/domain/src/ports/output_writer.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use crate::{DomainError, RenderOutput};
|
||||
|
||||
pub trait OutputWriter {
|
||||
fn write(&self, output: &RenderOutput) -> Result<(), DomainError>;
|
||||
}
|
||||
7
crates/domain/src/ports/project_analyzer.rs
Normal file
7
crates/domain/src/ports/project_analyzer.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{CodeGraph, DomainError};
|
||||
|
||||
pub trait ProjectAnalyzer {
|
||||
fn analyze(&self, root: &Path) -> Result<CodeGraph, DomainError>;
|
||||
}
|
||||
5
crates/domain/src/ports/source_analyzer.rs
Normal file
5
crates/domain/src/ports/source_analyzer.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use crate::{AnalysisResult, DomainError, SourceFile};
|
||||
|
||||
pub trait SourceAnalyzer: Send + Sync {
|
||||
fn analyze_file(&self, file: &SourceFile) -> Result<AnalysisResult, DomainError>;
|
||||
}
|
||||
60
crates/domain/src/value_objects/analysis/analysis_config.rs
Normal file
60
crates/domain/src/value_objects/analysis/analysis_config.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::DiagramLevel;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnalysisConfig {
|
||||
excludes: Vec<String>,
|
||||
level: DiagramLevel,
|
||||
module_mappings: HashMap<String, String>,
|
||||
scope: Option<String>,
|
||||
}
|
||||
|
||||
impl AnalysisConfig {
|
||||
pub fn with_excludes(mut self, excludes: Vec<String>) -> Self {
|
||||
self.excludes = excludes;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_level(mut self, level: DiagramLevel) -> Self {
|
||||
self.level = level;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_module_mappings(mut self, mappings: HashMap<String, String>) -> Self {
|
||||
self.module_mappings = mappings;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn excludes(&self) -> &[String] {
|
||||
&self.excludes
|
||||
}
|
||||
|
||||
pub fn level(&self) -> DiagramLevel {
|
||||
self.level
|
||||
}
|
||||
|
||||
pub fn with_scope(mut self, scope: String) -> Self {
|
||||
self.scope = Some(scope);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn module_mappings(&self) -> &HashMap<String, String> {
|
||||
&self.module_mappings
|
||||
}
|
||||
|
||||
pub fn scope(&self) -> Option<&str> {
|
||||
self.scope.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AnalysisConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
excludes: Vec::new(),
|
||||
level: DiagramLevel::Module,
|
||||
module_mappings: HashMap::new(),
|
||||
scope: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
42
crates/domain/src/value_objects/analysis/analysis_result.rs
Normal file
42
crates/domain/src/value_objects/analysis/analysis_result.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::{AnalysisWarning, CodeElement, Relationship};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnalysisResult {
|
||||
elements: Vec<CodeElement>,
|
||||
relationships: Vec<Relationship>,
|
||||
warnings: Vec<AnalysisWarning>,
|
||||
}
|
||||
|
||||
impl AnalysisResult {
|
||||
pub fn new(
|
||||
elements: Vec<CodeElement>,
|
||||
relationships: Vec<Relationship>,
|
||||
warnings: Vec<AnalysisWarning>,
|
||||
) -> Self {
|
||||
Self {
|
||||
elements,
|
||||
relationships,
|
||||
warnings,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
elements: Vec::new(),
|
||||
relationships: Vec::new(),
|
||||
warnings: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elements(&self) -> &[CodeElement] {
|
||||
&self.elements
|
||||
}
|
||||
|
||||
pub fn relationships(&self) -> &[Relationship] {
|
||||
&self.relationships
|
||||
}
|
||||
|
||||
pub fn warnings(&self) -> &[AnalysisWarning] {
|
||||
&self.warnings
|
||||
}
|
||||
}
|
||||
34
crates/domain/src/value_objects/analysis/analysis_warning.rs
Normal file
34
crates/domain/src/value_objects/analysis/analysis_warning.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use crate::{DomainError, FilePath};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AnalysisWarning {
|
||||
file_path: FilePath,
|
||||
line: usize,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl AnalysisWarning {
|
||||
pub fn new(file_path: FilePath, line: usize, message: &str) -> Result<Self, DomainError> {
|
||||
let message = message.trim();
|
||||
if message.is_empty() {
|
||||
return Err(DomainError::EmptyValue("AnalysisWarning message"));
|
||||
}
|
||||
Ok(Self {
|
||||
file_path,
|
||||
line,
|
||||
message: message.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file_path(&self) -> &FilePath {
|
||||
&self.file_path
|
||||
}
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
7
crates/domain/src/value_objects/analysis/mod.rs
Normal file
7
crates/domain/src/value_objects/analysis/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod analysis_config;
|
||||
mod analysis_result;
|
||||
mod analysis_warning;
|
||||
|
||||
pub use analysis_config::AnalysisConfig;
|
||||
pub use analysis_result::AnalysisResult;
|
||||
pub use analysis_warning::AnalysisWarning;
|
||||
@@ -0,0 +1,9 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum CodeElementKind {
|
||||
Class,
|
||||
Struct,
|
||||
Trait,
|
||||
Interface,
|
||||
Enum,
|
||||
Project,
|
||||
}
|
||||
7
crates/domain/src/value_objects/graph/mod.rs
Normal file
7
crates/domain/src/value_objects/graph/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod code_element_kind;
|
||||
mod relationship_kind;
|
||||
mod visibility;
|
||||
|
||||
pub use code_element_kind::CodeElementKind;
|
||||
pub use relationship_kind::RelationshipKind;
|
||||
pub use visibility::Visibility;
|
||||
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum RelationshipKind {
|
||||
Inheritance,
|
||||
Composition,
|
||||
Import,
|
||||
}
|
||||
6
crates/domain/src/value_objects/graph/visibility.rs
Normal file
6
crates/domain/src/value_objects/graph/visibility.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Visibility {
|
||||
Public,
|
||||
Private,
|
||||
Internal,
|
||||
}
|
||||
4
crates/domain/src/value_objects/mod.rs
Normal file
4
crates/domain/src/value_objects/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod analysis;
|
||||
pub mod graph;
|
||||
pub mod output;
|
||||
pub mod source;
|
||||
6
crates/domain/src/value_objects/output/diagram_level.rs
Normal file
6
crates/domain/src/value_objects/output/diagram_level.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum DiagramLevel {
|
||||
Project,
|
||||
Module,
|
||||
Type,
|
||||
}
|
||||
9
crates/domain/src/value_objects/output/mod.rs
Normal file
9
crates/domain/src/value_objects/output/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod diagram_level;
|
||||
mod output_config;
|
||||
mod render_output;
|
||||
mod rendered_file;
|
||||
|
||||
pub use diagram_level::DiagramLevel;
|
||||
pub use output_config::OutputConfig;
|
||||
pub use render_output::RenderOutput;
|
||||
pub use rendered_file::RenderedFile;
|
||||
25
crates/domain/src/value_objects/output/output_config.rs
Normal file
25
crates/domain/src/value_objects/output/output_config.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OutputConfig {
|
||||
split_by_module: bool,
|
||||
output_path: Option<String>,
|
||||
}
|
||||
|
||||
impl OutputConfig {
|
||||
pub fn with_split_by_module(mut self, split: bool) -> Self {
|
||||
self.split_by_module = split;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_output_path(mut self, path: String) -> Self {
|
||||
self.output_path = Some(path);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn split_by_module(&self) -> bool {
|
||||
self.split_by_module
|
||||
}
|
||||
|
||||
pub fn output_path(&self) -> Option<&str> {
|
||||
self.output_path.as_deref()
|
||||
}
|
||||
}
|
||||
20
crates/domain/src/value_objects/output/render_output.rs
Normal file
20
crates/domain/src/value_objects/output/render_output.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use crate::RenderedFile;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderOutput {
|
||||
files: Vec<RenderedFile>,
|
||||
}
|
||||
|
||||
impl RenderOutput {
|
||||
pub fn new(files: Vec<RenderedFile>) -> Self {
|
||||
Self { files }
|
||||
}
|
||||
|
||||
pub fn single(file: RenderedFile) -> Self {
|
||||
Self { files: vec![file] }
|
||||
}
|
||||
|
||||
pub fn files(&self) -> &[RenderedFile] {
|
||||
&self.files
|
||||
}
|
||||
}
|
||||
32
crates/domain/src/value_objects/output/rendered_file.rs
Normal file
32
crates/domain/src/value_objects/output/rendered_file.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use crate::DomainError;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderedFile {
|
||||
name: String,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl RenderedFile {
|
||||
pub fn new(name: &str, content: &str) -> Result<Self, DomainError> {
|
||||
let name = name.trim();
|
||||
let content = content.trim();
|
||||
if name.is_empty() {
|
||||
return Err(DomainError::EmptyValue("RenderedFile name"));
|
||||
}
|
||||
if content.is_empty() {
|
||||
return Err(DomainError::EmptyValue("RenderedFile content"));
|
||||
}
|
||||
Ok(Self {
|
||||
name: name.to_string(),
|
||||
content: content.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn content(&self) -> &str {
|
||||
&self.content
|
||||
}
|
||||
}
|
||||
18
crates/domain/src/value_objects/source/file_path.rs
Normal file
18
crates/domain/src/value_objects/source/file_path.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::DomainError;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FilePath(String);
|
||||
|
||||
impl FilePath {
|
||||
pub fn new(value: &str) -> Result<Self, DomainError> {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err(DomainError::EmptyValue("FilePath"));
|
||||
}
|
||||
Ok(Self(trimmed.to_string()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
16
crates/domain/src/value_objects/source/language.rs
Normal file
16
crates/domain/src/value_objects/source/language.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Language {
|
||||
Rust,
|
||||
CSharp,
|
||||
Python,
|
||||
}
|
||||
|
||||
impl Language {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Rust => "Rust",
|
||||
Self::CSharp => "CSharp",
|
||||
Self::Python => "Python",
|
||||
}
|
||||
}
|
||||
}
|
||||
9
crates/domain/src/value_objects/source/mod.rs
Normal file
9
crates/domain/src/value_objects/source/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
mod file_path;
|
||||
mod language;
|
||||
mod module_name;
|
||||
mod source_file;
|
||||
|
||||
pub use file_path::FilePath;
|
||||
pub use language::Language;
|
||||
pub use module_name::ModuleName;
|
||||
pub use source_file::SourceFile;
|
||||
18
crates/domain/src/value_objects/source/module_name.rs
Normal file
18
crates/domain/src/value_objects/source/module_name.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::DomainError;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ModuleName(String);
|
||||
|
||||
impl ModuleName {
|
||||
pub fn new(value: &str) -> Result<Self, DomainError> {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err(DomainError::EmptyValue("ModuleName"));
|
||||
}
|
||||
Ok(Self(trimmed.to_string()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
21
crates/domain/src/value_objects/source/source_file.rs
Normal file
21
crates/domain/src/value_objects/source/source_file.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use crate::{FilePath, Language};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SourceFile {
|
||||
path: FilePath,
|
||||
language: Language,
|
||||
}
|
||||
|
||||
impl SourceFile {
|
||||
pub fn new(path: FilePath, language: Language) -> Self {
|
||||
Self { path, language }
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &FilePath {
|
||||
&self.path
|
||||
}
|
||||
|
||||
pub fn language(&self) -> Language {
|
||||
self.language
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user