simplify (maybe?) registry
change xyzmszksxyltryuvvsxvupykvpoxtwst
commit b23b48c4c4b7c324fb7b1ae760cd63f483cfb1f5
author Alpha Chen <alpha@kejadlen.dev>
date
parent rqvktkry
diff --git a/src/assertions.rs b/src/assertions.rs
index 0da4315..8485d50 100644
--- a/src/assertions.rs
+++ b/src/assertions.rs
@@ -8,6 +8,35 @@ use tracing::{debug, error, info};
 use crate::errors::FrorkError;
 use crate::utils::Utils;
 
+pub trait AssertionTypeFactory {
+    fn create(&self, lua: &Lua, args: LuaMultiValue) -> Result<Box<dyn AssertionType>>;
+}
+
+pub struct TypedFactory<T>(std::marker::PhantomData<T>);
+
+impl<T> TypedFactory<T> {
+    pub fn new() -> Self {
+        Self(std::marker::PhantomData)
+    }
+}
+
+impl<T> Default for TypedFactory<T> {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl<T> AssertionTypeFactory for TypedFactory<T>
+where
+    T: AssertionType + FromLuaMulti + 'static,
+{
+    fn create(&self, lua: &Lua, args: LuaMultiValue) -> Result<Box<dyn AssertionType>> {
+        T::from_lua_multi(args, lua)
+            .map(|t| Box::new(t) as Box<dyn AssertionType>)
+            .map_err(|e| eyre!("Failed to create assertion type: {}", e))
+    }
+}
+
 #[derive(Debug, Deserialize)]
 pub struct Conflict {
     pub expected: String,
@@ -305,4 +334,3 @@ impl AssertionType for LuaAssertion {
         Ok(())
     }
 }
-
diff --git a/src/main.rs b/src/main.rs
index e48f5ed..e74ce86 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,8 @@ mod errors;
 mod utils;
 
 use assertions::{
-    AssertionType, Debug, Directory, LuaAssertion, LuaAssertionType, Status, Symlink,
+    AssertionType, AssertionTypeFactory, Debug, Directory, LuaAssertion, LuaAssertionType, Status,
+    Symlink, TypedFactory,
 };
 use clap::{Parser, Subcommand};
 use color_eyre::{Result, eyre::eyre};
@@ -31,6 +32,21 @@ enum Commands {
     Satisfy { script: String },
 }
 
+struct LuaAssertionFactory {
+    assertion_type: String,
+    lua_assertion_type: LuaAssertionType,
+}
+
+impl AssertionTypeFactory for LuaAssertionFactory {
+    fn create(&self, _lua: &Lua, args: LuaMultiValue) -> Result<Box<dyn AssertionType>> {
+        Ok(Box::new(LuaAssertion::new(
+            &self.assertion_type,
+            args,
+            self.lua_assertion_type.clone(),
+        )))
+    }
+}
+
 #[derive(Default)]
 struct Registry {
     lua_assertion_types: HashMap<String, LuaAssertionType>,
@@ -42,32 +58,20 @@ impl Registry {
             .insert(name.to_string(), lua_assertion_type);
     }
 
-    fn create(
-        &self,
-        assertion_type: &str,
-        args: LuaMultiValue,
-        lua: &Lua,
-    ) -> Result<Box<dyn AssertionType>> {
+    fn get_factory(&self, assertion_type: &str) -> Result<Box<dyn AssertionTypeFactory>> {
         // Check Lua assertions first
         if let Some(lua_assertion) = self.lua_assertion_types.get(assertion_type) {
-            return Ok(Box::new(LuaAssertion::new(
-                assertion_type,
-                args,
-                lua_assertion.clone(),
-            )));
+            return Ok(Box::new(LuaAssertionFactory {
+                assertion_type: assertion_type.to_string(),
+                lua_assertion_type: lua_assertion.clone(),
+            }));
         }
 
-        // Fall back to built-in types
+        // Return factory for built-in types
         match assertion_type {
-            "debug" => Debug::from_lua_multi(args, lua)
-                .map(|d| Box::new(d) as Box<dyn AssertionType>)
-                .map_err(|e| FrorkError::from(e).into()),
-            "directory" => Directory::from_lua_multi(args, lua)
-                .map(|d| Box::new(d) as Box<dyn AssertionType>)
-                .map_err(|e| FrorkError::from(e).into()),
-            "symlink" => Symlink::from_lua_multi(args, lua)
-                .map(|s| Box::new(s) as Box<dyn AssertionType>)
-                .map_err(|e| FrorkError::from(e).into()),
+            "debug" => Ok(Box::new(TypedFactory::<Debug>::new())),
+            "directory" => Ok(Box::new(TypedFactory::<Directory>::new())),
+            "symlink" => Ok(Box::new(TypedFactory::<Symlink>::new())),
             _ => Err(FrorkError::UnknownAssertionType {
                 assertion_type: assertion_type.to_string(),
             }
@@ -146,10 +150,13 @@ where
 
         let assertion_args: LuaMultiValue = args_iter.collect();
 
-        let assertion = self
+        let factory = self
             .registry
             .borrow()
-            .create(&assertion_type, assertion_args, &self.lua)
+            .get_factory(&assertion_type)
+            .map_err(LuaError::external)?;
+        let assertion = factory
+            .create(&self.lua, assertion_args)
             .map_err(LuaError::external)?;
         let status = assertion.status().map_err(LuaError::external)?;
 
@@ -230,6 +237,7 @@ fn status(status: &Status, assertion: &dyn AssertionType) -> Result<()> {
     match status {
         Status::Ok => println!("ok: {}", assertion),
         Status::Missing => println!("missing: {}", assertion),
+        // TODO show a nicer diff?
         Status::ConflictUpgrade(conflict) => {
             println!("conflict (upgradable): {}", assertion);
             println!("  expected: {}", conflict.expected);
@@ -248,11 +256,26 @@ fn satisfy(status: &Status, assertion: &dyn AssertionType) -> Result<()> {
             println!("ok: {}", assertion);
         }
         Status::ConflictUpgrade(conflict) => {
-            todo!(
-                "Handle conflict upgrade status in satisfy: expected: {}, actual: {}",
-                conflict.expected,
-                conflict.actual
-            );
+            println!("conflict (upgradable): {}", assertion);
+            println!("  expected: {}", conflict.expected);
+            println!("    actual: {}", conflict.actual);
+
+            use std::io::{self, Write};
+            print!("Upgrade? [y/N]: ");
+            io::stdout().flush()?;
+
+            let mut input = String::new();
+            io::stdin().read_line(&mut input)?;
+
+            match input.trim().to_lowercase().as_str() {
+                "y" | "yes" => {
+                    assertion.install()?;
+                    println!("ok: {}", assertion);
+                }
+                _ => {
+                    println!("skipped: {}", assertion);
+                }
+            }
         }
     }
     Ok(())