Skip to content

Commit

Permalink
Fix typos and error handling in code
Browse files Browse the repository at this point in the history
  • Loading branch information
Almaju committed Jan 5, 2024
1 parent e6b6d26 commit fa3ec8a
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 27 deletions.
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
axium:
axum:
cd clients/axum && cargo watch -x run

yew:
cd clients/yew && trunk serve

test:
cargo test
cargo test -- --nocapture

deps:
cargo +nightly udeps
Expand Down
8 changes: 7 additions & 1 deletion application/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@ where
{
async fn execute(&self, runtime: &R) -> AnyResult<()> {
let message = self.try_into()?;

// Pull the current state and apply the message
let todolist = TodoListStore::pull(runtime).await?;
let new_events = todolist.send(&message)?;
let projection = TodoListRepository::fetch(runtime).await?;
TodoListStore::push(runtime, &new_events).await?;

// Save the projection
let mut projection = TodoListRepository::fetch(runtime).await?;
projection.apply(&new_events);
TodoListRepository::save(runtime, &projection).await?;

Ok(())
}
}
32 changes: 32 additions & 0 deletions clients/axum/examples.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
### Retrieve all todolist
GET http://localhost:3000/todolist HTTP/1.1

### Create a new task
POST http://localhost:3000/todolist HTTP/1.1
content-type: application/json

{
"AddTask": {
"name": "Some task"
}
}

### Complete a task
POST http://localhost:3000/todolist HTTP/1.1
content-type: application/json

{
"CompleteTask": {
"index": 0
}
}

### Remove a task
POST http://localhost:3000/todolist HTTP/1.1
content-type: application/json

{
"RemoveTask": {
"index": 0
}
}
27 changes: 22 additions & 5 deletions clients/axum/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ mod runtime;
use application::{command::Command, port::*, query::GetTodoListQuery};
use axum::{
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
routing::{get, post},
Json, Router,
};
Expand Down Expand Up @@ -39,9 +41,24 @@ async fn get_todolist(
async fn handle_command(
State(state): State<Arc<Runtime>>,
Json(command): Json<Command>,
) -> Result<(), String> {
command
.execute(state.as_ref())
.await
.map_err(|err| err.to_string())
) -> Result<(), AppError> {
command.execute(state.as_ref()).await?;
Ok(())
}

struct AppError(AnyError);

impl IntoResponse for AppError {
fn into_response(self) -> Response {
(StatusCode::BAD_REQUEST, self.0.to_string()).into_response()
}
}

impl<E> From<E> for AppError
where
E: Into<AnyError>,
{
fn from(err: E) -> Self {
Self(err.into())
}
}
2 changes: 1 addition & 1 deletion clients/yew/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ fn App() -> Html {
}).collect::<Html>()}
</ul>
<h2>{ "Completed" }</h2>
{todo_list.clone().completed.iter().map(|(_, task_name)| html! {
{todo_list.clone().completed.values().map(|task_name| html! {
<li><input type="checkbox" checked={true} disabled={true} /> { &task_name }</li>
}).collect::<Html>()}
<input ref={input_ref} type="text" id="task" name="task" />
Expand Down
2 changes: 1 addition & 1 deletion domain/src/todolist_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use framework::*;

use crate::todolist_scalar::TaskIndex;

#[derive(Debug, Error)]
#[derive(Debug, PartialEq, Error)]
pub enum TodoListError {
#[error("Task already completed")]
TaskAlreadyCompleted(TaskIndex),
Expand Down
14 changes: 7 additions & 7 deletions domain/src/todolist_scalar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize, PartialEq, Hash, Eq)]
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub struct TaskIndex(pub usize);

impl From<usize> for TaskIndex {
Expand All @@ -9,9 +9,9 @@ impl From<usize> for TaskIndex {
}
}

impl Into<usize> for TaskIndex {
fn into(self) -> usize {
self.0
impl From<TaskIndex> for usize {
fn from(value: TaskIndex) -> Self {
value.0
}
}

Expand All @@ -36,8 +36,8 @@ impl TryFrom<String> for TaskName {
}
}

impl Into<String> for TaskName {
fn into(self) -> String {
self.0
impl From<TaskName> for String {
fn from(value: TaskName) -> Self {
value.0
}
}
125 changes: 115 additions & 10 deletions domain/src/todolist_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,131 @@ impl State for TodoList {
TodoListMessage::AddTask(name) => {
events.push(TodoListEvent::TaskAdded(self.next_index, name.clone()));
}
TodoListMessage::RemoveTask(index) => {
if !self.tasks.contains_key(index) {
return Err(TodoListError::TaskNotFound(*index));
}
events.push(TodoListEvent::TaskRemoved(*index));
}
TodoListMessage::CompleteTask(index) => {
TodoListMessage::RemoveTask(index) | TodoListMessage::CompleteTask(index) => {
let task = self.tasks.get(index);

match task {
None => return Err(TodoListError::TaskNotFound(*index)),
Some(Completed::Yes) => {
return Err(TodoListError::TaskAlreadyCompleted(*index))
}
Some(Completed::No) => {
events.push(TodoListEvent::TaskCompleted(*index));
}
Some(Completed::No) => match message {
TodoListMessage::RemoveTask(_) => {
events.push(TodoListEvent::TaskRemoved(*index));
}
TodoListMessage::CompleteTask(_) => {
events.push(TodoListEvent::TaskCompleted(*index));
}
_ => {}
},
}
}
}
Ok(events)
}
}

#[cfg(test)]
mod tests {
use crate::todolist_scalar::TaskName;

use super::*;

#[test]
fn task_index_addition() {
let mut index = TaskIndex(5);
index += 3;
assert_eq!(index.0, 8);
}

#[test]
fn task_name_creation() {
let name = "Test Task".to_string();
let task_name = TaskName::try_from(name.clone()).unwrap();
assert_eq!(task_name.0, name);
}

#[test]
fn task_name_creation_failure() {
let name = "".to_string();
assert!(TaskName::try_from(name).is_err());
}

#[test]
fn add_task_to_todo_list() {
let mut todo_list = TodoList::default();
let task_name = "Buy milk".to_string();
let message = TodoListMessage::AddTask(TaskName(task_name));
let events = todo_list.send(&message).unwrap();
todo_list.apply(&events);

assert_eq!(todo_list.tasks.len(), 1);
assert_eq!(todo_list.tasks[&TaskIndex(0)], Completed::No);
}

#[test]
fn complete_task_in_todo_list() {
let mut todo_list = TodoList::default();
let task_name = "Buy milk".to_string();
let add_message = TodoListMessage::AddTask(TaskName(task_name));
let add_events = todo_list.send(&add_message).unwrap();
todo_list.apply(&add_events);

let complete_message = TodoListMessage::CompleteTask(TaskIndex(0));
let complete_events = todo_list.send(&complete_message).unwrap();
todo_list.apply(&complete_events);

assert_eq!(todo_list.tasks[&TaskIndex(0)], Completed::Yes);
}

#[test]
fn remove_task_from_todo_list() {
let mut todo_list = TodoList::default();

let task_name = "Buy milk".to_string();
let add_message = TodoListMessage::AddTask(TaskName(task_name));
let add_events = todo_list.send(&add_message).unwrap();
todo_list.apply(&add_events);

let remove_message = TodoListMessage::RemoveTask(TaskIndex(0));
let remove_events = todo_list.send(&remove_message).unwrap();
todo_list.apply(&remove_events);

assert!(todo_list.tasks.is_empty());
}

#[test]
fn error_on_completing_nonexistent_task() {
let todo_list = TodoList::default();
let message = TodoListMessage::CompleteTask(TaskIndex(10)); // Assuming no tasks have been added yet
let result = todo_list.send(&message);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
TodoListError::TaskNotFound(TaskIndex(10))
);
}

#[test]
fn error_on_completing_already_completed_task() {
let mut todo_list = TodoList::default();
let task_name = "Buy milk".to_string();
let add_message = TodoListMessage::AddTask(TaskName(task_name));
let add_events = todo_list.send(&add_message).unwrap();
todo_list.apply(&add_events);

let complete_message = TodoListMessage::CompleteTask(TaskIndex(0));
let complete_events = todo_list.send(&complete_message).unwrap();
todo_list.apply(&complete_events);

let complete_again_message = TodoListMessage::CompleteTask(TaskIndex(0));
let result = todo_list.send(&complete_again_message);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
TodoListError::TaskAlreadyCompleted(TaskIndex(0))
);
}

// Add more tests to cover additional edge cases and scenarios as needed
}

0 comments on commit fa3ec8a

Please sign in to comment.