diff --git a/tools/src/lib.rs b/tools/src/lib.rs index 74b3f6a..09890d9 100644 --- a/tools/src/lib.rs +++ b/tools/src/lib.rs @@ -4,3 +4,7 @@ pub use recipe::Recipe; pub mod repo; pub use repo::Repo; pub use repo::RepoLoadError; + +pub mod recipe_id; +pub use recipe_id::RecipeId; +pub use recipe_id::RecipeIdParseError; diff --git a/tools/src/recipe_id.rs b/tools/src/recipe_id.rs new file mode 100644 index 0000000..82a988c --- /dev/null +++ b/tools/src/recipe_id.rs @@ -0,0 +1,73 @@ +use std::str::FromStr; + +use thiserror::Error; + +#[derive(Debug, PartialEq)] +pub struct RecipeId { + pub repo: Option, + pub recipe: String, +} + +#[derive(Debug, Error, PartialEq)] +pub enum RecipeIdParseError { + #[error("syntax error: {0}")] + SyntaxError(String), +} + +impl FromStr for RecipeId { + type Err = RecipeIdParseError; + fn from_str(recipe_id: &str) -> Result { + let substrings: Vec<&str> = recipe_id.split(':').collect(); + match substrings.len() { + 1 => Ok(RecipeId { + repo: None, + recipe: String::from(recipe_id), + }), + 2 => Ok(RecipeId { + repo: Some(String::from(substrings[0])), + recipe: String::from(substrings[1]), + }), + _ => Err(RecipeIdParseError::SyntaxError(String::from(recipe_id))), + } + } +} + +mod test { + #[cfg(test)] + use crate::recipe_id::{FromStr, RecipeId, RecipeIdParseError}; + + #[test] + fn parse_simple() { + let r = RecipeId::from_str("hello"); + assert_eq!( + r, + Ok(RecipeId { + repo: None, + recipe: "hello".into(), + }) + ); + } + + #[test] + fn parse_with_repo() { + let r = RecipeId::from_str("my_repo:my_recipe"); + assert_eq!( + r, + Ok(RecipeId { + repo: Some("my_repo".into()), + recipe: "my_recipe".into(), + }) + ); + } + + #[test] + fn parse_too_many_colons() { + let r = RecipeId::from_str("my_repo:my_recipe:something_else"); + assert_eq!( + r, + Err(RecipeIdParseError::SyntaxError( + "my_repo:my_recipe:something_else".into() + )) + ); + } +}