From 0927e14e45d86dab7515aa2b66c6fe3cdf07df8d Mon Sep 17 00:00:00 2001 From: David Peklak Date: Fri, 18 Mar 2016 13:30:07 +0100 Subject: [PATCH] refactor for simplicity no Kleisli no StateT no Future compiles. test not refactored yet. --- src/main/scala/smt/ConnectionHandling.scala | 162 +++++++------- src/main/scala/smt/Handling.scala | 197 ++++++------------ src/main/scala/smt/SMTImpl.scala | 31 +-- src/main/scala/smt/Writing.scala | 36 +++- src/main/scala/smt/db/Connection.scala | 4 + src/main/scala/smt/db/ConnectionAction.scala | 53 ----- src/main/scala/smt/db/DbAction.scala | 16 -- src/main/scala/smt/db/LockAction.scala | 5 - .../scala/smt/describe/DescribeAction.scala | 23 +- .../scala/smt/report/ReporterAction.scala | 35 ---- .../scala/smt/report/ReportersAction.scala | 10 + src/main/scala/smt/util/ActionTypes.scala | 79 ------- src/main/scala/smt/util/EitherHaerte.scala | 42 ++++ src/main/scala/smt/util/EitherTHaerte.scala | 33 --- src/main/scala/smt/util/KleisliStack.scala | 104 --------- src/main/scala/smt/util/SeqHaerte.scala | 42 ++++ src/main/scala/smt/util/Util.scala | 2 +- 17 files changed, 296 insertions(+), 578 deletions(-) delete mode 100644 src/main/scala/smt/db/ConnectionAction.scala delete mode 100644 src/main/scala/smt/db/DbAction.scala delete mode 100644 src/main/scala/smt/db/LockAction.scala delete mode 100644 src/main/scala/smt/report/ReporterAction.scala create mode 100644 src/main/scala/smt/report/ReportersAction.scala delete mode 100644 src/main/scala/smt/util/ActionTypes.scala create mode 100644 src/main/scala/smt/util/EitherHaerte.scala delete mode 100644 src/main/scala/smt/util/EitherTHaerte.scala delete mode 100644 src/main/scala/smt/util/KleisliStack.scala create mode 100644 src/main/scala/smt/util/SeqHaerte.scala diff --git a/src/main/scala/smt/ConnectionHandling.scala b/src/main/scala/smt/ConnectionHandling.scala index f39c270..ebc35dc 100644 --- a/src/main/scala/smt/ConnectionHandling.scala +++ b/src/main/scala/smt/ConnectionHandling.scala @@ -6,18 +6,18 @@ import UpMoveState._ import DownMoveState._ import NamedMoveStates._ import scalaz.Scalaz._ -import smt.util.TraverseStackSafeSyntax -import TraverseStackSafeSyntax._ import smt.db._ import smt.migration._ import smt.MigrationHandling._ -import smt.migration.Group -import smt.migration.MigrationInfo -import scala.Some -import smt.migration.Script -import smt.migration.Migration +import sbt.Logger +import smt.util.SeqHaerte.SeqSyntax._ +import smt.util.EitherHaerte.EitherSyntax._ -trait ConnectionHandling[T] extends ConnectionAction[T] { +import scalaz.{\/-, -\/, \/} + +case class MigrationInfoWithDowns(mi: MigrationInfo, downs: Seq[Script]) + +object ConnectionHandling { def now: Date = new Date @@ -36,7 +36,7 @@ trait ConnectionHandling[T] extends ConnectionAction[T] { common: Seq[Common], diffOnDb: Seq[MigrationInfo], diffOnRepo: Seq[(Migration, Seq[Byte])] - ) + ) def common2(mis: Seq[MigrationInfo], ms: HashedMigrationSeq): CommonMigrations = { val misInit = mis.drop(ms.initMig) @@ -54,121 +54,127 @@ trait ConnectionHandling[T] extends ConnectionAction[T] { ) } - def latestCommon(mhs: HashedMigrationSeq): EDKleisli[Option[Common]] = { - state().map(latestCommon2(_, mhs)) + def latestCommon(mhs: HashedMigrationSeq)(connection: Connection, logger: Logger): String \/ Option[Common] = { + connection.state(logger).map(latestCommon2(_, mhs)) } - def common(mhs: HashedMigrationSeq): EDKleisli[CommonMigrations] = { - state().map(common2(_, mhs)) + def common(mhs: HashedMigrationSeq)(connection: Connection, logger: Logger): String \/ CommonMigrations = { + connection.state(logger).map(common2(_, mhs)) } - case class MigrationInfoWithDowns(mi: MigrationInfo, downs: Seq[Script]) - - - def migrationsToRevert(latestCommon: Option[Seq[Byte]]): EDKleisli[Seq[MigrationInfo]] = { - state().map(_.reverse.takeWhile(mi => !latestCommon.exists(_ == mi.hash))) + def migrationsToRevert(latestCommon: Option[Seq[Byte]])(connection: Connection, logger: Logger): String \/ Seq[MigrationInfo] = { + connection.state(logger).map(_.reverse.takeWhile(mi => !latestCommon.exists(_ == mi.hash))) } - def enrichMigrationWithDowns(mi: MigrationInfo): EDKleisli[MigrationInfoWithDowns] = { - downs(mi.hash).map(MigrationInfoWithDowns(mi, _)) + def enrichMigrationWithDowns(mi: MigrationInfo)(connection: Connection, logger: Logger): String \/ MigrationInfoWithDowns = { + connection.downs(logger)(mi.hash).map(MigrationInfoWithDowns(mi, _)) } - def testMigration(m: Migration): EDKleisli[Unit] = { - m.tests.toList.traverse__(doTest) + def testMigration(m: Migration)(connection: Connection, logger: Logger): String \/ Unit = { + m.tests.travE_(_.run(connection)(logger)) } def migrationsToApply(mhs: HashedMigrationSeq, latestCommon: Option[Seq[Byte]]): HashedMigrationSeq = { mhs.copy(migs = mhs.migs.reverse.takeWhile(mh => !latestCommon.exists(_ == mh._2)).reverse) } - - def applyGroup(group: Group): upMoveTypes.EWDKleisli[Unit] = { - import upMoveTypes._ - import EWSyntax._ - - def apl(up: Script): upMoveTypes.EWDKleisli[Unit] = { - liftE(applyScript(up, Up)) :-\/++> crashedUp(up) :\/-++> appliedUp(up) - } - - group.ups.toList.traverse__(apl) :\/-++> (downsToApply(group.downs.toList) ⊹ appliedUpsWithDowns(group.ups.toList)) - } } -trait AddHandling[T] extends AddAction[T] with ConnectionHandling[T] { +object AddHandling { - def rewriteMigration(mid: MigrationInfoWithDowns, dms: DownMoveState, hash: Seq[Byte]): EDKleisli[Unit] = { + def rewriteMigration(mid: MigrationInfoWithDowns, dms: DownMoveState, hash: Seq[Byte], user: String, remark: String)(connection: Connection, logger: Logger): String \/ Unit = { val downsToWrite = mid.downs.reverse.map(Some(_)).zipAll(dms.appliedDowns.map(Some(_)) :+ dms.crashedDown, None, None) .dropWhile(t => t._1 == t._2).map(_._1.toSeq).flatten.reverse - addDowns(hash, downsToWrite) >> add(mid.mi.name, hash, now) + connection.addDowns(logger)(hash, downsToWrite) >> + connection.add(logger, mid.mi.name, hash, ConnectionHandling.now, user, remark) } - def revertMigration(mid: MigrationInfoWithDowns): downMoveTypes.EWDKleisli[Unit] = { - import downMoveTypes._ - import EWSyntax._ + def revertMigration(mid: MigrationInfoWithDowns, user: String, remark: String)(connection: Connection, logger: Logger, dms: DownMoveStateHolder): String \/ Unit = { - def applyScriptAndWrite(down: Script): EWDKleisli[Unit] = { - liftE(applyScript(down, Down)) :-\/++> crashedDown(down) :\/-++> appliedDown(down) + def applyScriptAndWrite(down: Script): String \/ Unit = { + val r = connection.applyScript(logger)(down, Down) + r match { + case -\/(_) => dms.add(crashedDown(down)) + case \/-(()) => dms.add(appliedDown(down)) + } + r } - liftE(removeDowns(mid.mi.hash) >> remove(mid.mi.hash)) >> - mid.downs.reverse.toList.traverse__(applyScriptAndWrite).recover((dms, f) => { - liftE(rewriteMigration(mid, dms, failHash(f)) >> failure(f)) - }) - } + val r = connection.removeDowns(logger)(mid.mi.hash) >> + connection.remove(logger)(mid.mi.hash) >> + mid.downs.reverse.travE_(applyScriptAndWrite) - def revertToLatestCommon(latestCommon: Option[Seq[Byte]], arb: Boolean): namedMoveTypes.EWDKleisli[Unit] = { - import namedMoveTypes._ - import downMoveTypes.EWSyntax._ + r.onLeftDo(f => rewriteMigration(mid, dms.dms, failHash(f), user, remark)(connection, logger)) + } + def revertToLatestCommon(latestCommon: Option[Seq[Byte]], arb: Boolean, user: String, remark: String)(connection: Connection, logger: Logger, nms: NamedMoveStatesHolder): String \/ Unit = { for { - mis <- liftE(migrationsToRevert(latestCommon)) - mids <- liftE(mis.toList.traverse(enrichMigrationWithDowns)) + mis <- ConnectionHandling.migrationsToRevert(latestCommon)(connection, logger) + mids <- mis.travE(mi => ConnectionHandling.enrichMigrationWithDowns(mi)(connection, logger)) _ <- { - if (mids.isEmpty || arb) mids.toList.traverse[EWDKleisli, Unit](mid => revertMigration(mid).mapWritten(namedMoveState(mid.mi.name))) - else liftE(failure("Will not roll back migrations " + mids.map(_.mi.name).mkString(", ") + ", because allow-rollback is set to false")) + if (mids.isEmpty || arb) mids.travE_(mid => nms.addDownMoveStateOf(mid.mi.name)(dms => revertMigration(mid, user, remark)(connection, logger, dms))) + else -\/("Will not roll back migrations " + mids.map(_.mi.name).mkString(", ") + ", because allow-rollback is set to false") } } yield () - } - def applyMigrations(ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean): namedMoveTypes.EWDKleisli[Unit] = { - import namedMoveTypes._ - + def applyMigrations(ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean, user: String, remark: String)(connection: Connection, logger: Logger, nms: NamedMoveStatesHolder): String \/ Unit = { val mhs = hashMigrations(ms, imo) for { - lcho <- liftE(latestCommon(mhs).map(_.map(_.db.hash))) - _ <- revertToLatestCommon(lcho, arb) - _ <- applyMigrations(mhs, lcho, runTests) + lcho <- ConnectionHandling.latestCommon(mhs)(connection, logger).map(_.map(_.db.hash)) + _ <- revertToLatestCommon(lcho, arb, user, remark)(connection, logger, nms) + _ <- doApplyMigrations(mhs, lcho, runTests, user, remark)(connection, logger, nms) } yield () } - def applyMigrations(mhs: HashedMigrationSeq, latestCommon: Option[Seq[Byte]], runTests: Boolean): namedMoveTypes.EWDKleisli[Unit] = { - import namedMoveTypes._ - import upMoveTypes.EWSyntax._ - - migrationsToApply(mhs, latestCommon).migs.toList.traverse__ { - case (m, h) => + def doApplyMigrations(mhs: HashedMigrationSeq, latestCommon: Option[Seq[Byte]], runTests: Boolean, user: String, remark: String)(connection: Connection, logger: Logger, nms: NamedMoveStatesHolder): String \/ Unit = { + ConnectionHandling.migrationsToApply(mhs, latestCommon).migs.travE_{ + case (m, h) => nms.addUpMoveStateOf(m.name)(ums => { if (runTests) for { - _ <- applyMigration(m, h).mapWritten(namedMoveState(m.name)) - _ <- liftE(testMigration(m)) + _ <- applyMigration(m, h, user, remark)(connection, logger, ums) + _ <- ConnectionHandling.testMigration(m)(connection, logger) } yield () - else applyMigration(m, h).mapWritten(namedMoveState(m.name)) + else applyMigration(m, h, user, remark)(connection, logger, ums) + }) } } - def applyMigration(m: Migration, hash: Seq[Byte]): upMoveTypes.EWDKleisli[Unit] = { - import upMoveTypes._ - import EWSyntax._ + def applyMigration(m: Migration, hash: Seq[Byte], user: String, remark: String)(connection: Connection, logger: Logger, ums: UpMoveStateHolder): String \/ Unit = { + def finalize(downs: List[Script], hash: Seq[Byte]): String \/ Unit = { + connection.addDowns(logger)(hash, downs) >> + connection.add(logger, m.name, hash, ConnectionHandling.now, user, remark) + } + + val r = m.groups.travE_(g => applyGroup(g)(connection, logger, ums)) - def finalize(downs: List[Script], hash: Seq[Byte]): EDKleisli[Unit] = { - addDowns(hash, downs) >> add(m.name, hash, now) + r match { + case err@ -\/(f) => { + finalize(ums.ums.downsToApply, failHash(f)) >> + err + } + case \/-(()) => finalize(ums.ums.downsToApply, hash) + } + } + + def applyGroup(group: Group)(connection: Connection, logger: Logger, ums: UpMoveStateHolder): String \/ Unit = { + def apl(up: Script): String \/ Unit = { + val r = connection.applyScript(logger)(up, Up) + r match { + case -\/(_) => ums.add(crashedUp(up)) + case \/-(()) => ums.add(appliedUp(up)) + } + r } - m.groups.toList.traverse__(applyGroup).conclude { - (ums, f) => liftE(finalize(ums.downsToApply, failHash(f)) >> failure(f)) - } { - (ums, _) => liftE(finalize(ums.downsToApply, hash)) + val r = group.ups.travE_(apl) + r match { + case -\/(_) => {} + case \/-(()) => { + ums.add(downsToApply(group.downs.toList)) + ums.add(appliedUpsWithDowns(group.ups.toList)) + } } + r } } diff --git a/src/main/scala/smt/Handling.scala b/src/main/scala/smt/Handling.scala index d697f40..d3c8b34 100644 --- a/src/main/scala/smt/Handling.scala +++ b/src/main/scala/smt/Handling.scala @@ -5,166 +5,101 @@ import smt.report.{ReportersAction, Reporter} import scalaz.Scalaz._ import smt.describe.DescribeAction import sbt.Logger -import scalaz.{-\/, EitherT} +import scalaz.{\/, -\/} import smt.migration.{HashedMigrationSeq, MigrationInfo, Migration, Up} -import smt.db.ConnectionAction.HasConnection -import smt.db.LockAction.HasLock -import smt.db.AddAction.{HasUser, HasRemark} -import smt.describe.DescribeAction.HasLogger +import smt.util.EitherHaerte.EitherSyntax._ -case class HandlingDep(db: Database, rps: List[Reporter], logger: Logger, user: String, remark: Option[String]) +/** + * uses functions of ConnectionHandling, + * wraps around connection opening, locking and closing + */ +object StateHandling { -case class ConnectionDep(c: Connection, logger: Logger) - -trait StateHandling[T] extends DbAction[T] { - - lazy val connectionHandling = new ConnectionHandling[ConnectionDep] { - lazy val hasConnection: HasConnection[ConnectionDep] = _.c - lazy val hasLogger: HasLogger[ConnectionDep] = _.logger - } - - lazy val connectionLockHandling = new ConnectionHandling[(ConnectionDep, String)] { - lazy val hasConnection: HasConnection[(ConnectionDep, String)] = _._1.c - lazy val hasLogger: HasLogger[(ConnectionDep, String)] = _._1.logger - lazy val hasLock: HasLock[(ConnectionDep, String)] = _._2 + def withConnection[U](action: (Connection, Logger) => String \/ U)(db: Database, logger: Logger): String \/ U = { + for { + c <- db.connection() + r <- { + c.init(logger) >> + action(c, logger) + }.andFinally(c.close(logger)) + } yield r } - def openConnection: EDKleisli[ConnectionDep] = { + def withLock[U](action: (Connection, Logger) => String \/ U)(c: Connection, logger: Logger): String \/ U = { for { - c <- connection() - l <- eAsk.map(hasLogger) - } yield ConnectionDep(c, l) + lock <- c.acquireLock(logger) + r <- { + action(c, logger) + }.andFinally(c.releaseLock(logger)(lock)) + } yield r } - def withConnection[U](action: connectionHandling.EDKleisli[U]): EDKleisli[U] = { - import ekSyntax._ - import connectionHandling.eSyntax._ - - openConnection >=> ((connectionHandling.init() >> action) andFinally connectionHandling.close()) + def withLockedConnection[U](action: (Connection, Logger) => String \/ U)(db: Database, logger: Logger): String \/ U = { + withConnection((c, l) => { + withLock(action)(c, l) + })(db, logger) } - def acqLock: connectionHandling.EDKleisli[(ConnectionDep, String)] = { - for { - t <- connectionHandling.eAsk - l <- connectionHandling.acquireLock() - } yield (t, l) + def state(db: Database, logger: Logger): String \/ Seq[MigrationInfo] = { + withConnection((c, l) => c.state(l))(db, logger) } - def rlsLock: connectionLockHandling.EDKleisli[Unit] = { - for { - t <- connectionLockHandling.eAsk - _ <- connectionLockHandling.releaseLock(t._2) - } yield () + def latestCommon(mhs: HashedMigrationSeq)(db: Database, logger: Logger): String \/ Option[ConnectionHandling.Common] = { + withConnection((c, l) => ConnectionHandling.latestCommon(mhs)(c, l))(db, logger) } - def withLock[U](action: connectionLockHandling.EDKleisli[U]): connectionHandling.EDKleisli[U] = { - import connectionHandling._ - import connectionHandling.ekSyntax._ - - acqLock >=> { - import connectionLockHandling._ - import connectionLockHandling.eSyntax._ - action andFinally rlsLock - } + def common(mhs: HashedMigrationSeq)(db: Database, logger: Logger): String \/ ConnectionHandling.CommonMigrations = { + withConnection((c, l) => ConnectionHandling.common(mhs)(c, l))(db, logger) } - def state(): EDKleisli[Seq[MigrationInfo]] = withConnection(connectionHandling.state()) - - def latestCommon(mhs: HashedMigrationSeq): EDKleisli[Option[connectionHandling.Common]] = withConnection(connectionHandling.latestCommon(mhs)) - - def common(mhs: HashedMigrationSeq): EDKleisli[connectionHandling.CommonMigrations] = withConnection(connectionHandling.common(mhs)) - - def applyScript(scr: migration.Script): EDKleisli[Unit] = { - import ekSyntax._ - import connectionHandling.eSyntax._ - - openConnection >=> (withLock(connectionLockHandling.applyScript(scr, Up)) andFinally connectionHandling.close()) + def applyScript(scr: migration.Script)(db: Database, logger: Logger): String \/ Unit = { + withLockedConnection((c, l) => c.applyScript(l)(scr, Up))(db, logger) } } -trait Handling[T] extends DbAction[T] with DescribeAction[T] with ReportersAction[T] { - - handling => +/** + * uses functions of AddHandling, + * wraps around connection opening, locking and closing + */ +object Handling { - val hasUser: HasUser[T] - val hasRemark: HasRemark[T] - - lazy val connectionHandling = new AddHandling[(T, Connection)] { - lazy val hasConnection: HasConnection[(T, Connection)] = _._2 - lazy val hasLogger: HasLogger[(T, Connection)] = t => handling.hasLogger(t._1) - lazy val hasUser: HasUser[(T, Connection)] = t => handling.hasUser(t._1) - lazy val hasRemark: HasRemark[(T, Connection)] = t => handling.hasRemark(t._1) + def withConnection[U](action: (Connection, Logger, NamedMoveStatesHolder) => String \/ U)(db: Database, logger: Logger, nms: NamedMoveStatesHolder): String \/ U = { + for { + c <- db.connection() + r <- { + c.init(logger) >> + action(c, logger, nms) + }.andFinally(c.close(logger)) + } yield r } - lazy val connectionLockHandling = new AddHandling[(T, Connection, String)] { - lazy val hasConnection: HasConnection[(T, Connection, String)] = _._2 - lazy val hasLogger: HasLogger[(T, Connection, String)] = t => handling.hasLogger(t._1) - lazy val hasUser: HasUser[(T, Connection, String)] = t => handling.hasUser(t._1) - lazy val hasRemark: HasRemark[(T, Connection, String)] = t => handling.hasRemark(t._1) - lazy val hasLock: HasLock[(T, Connection, String)] = _._3 + def withLock[U](action: (Connection, Logger, NamedMoveStatesHolder) => String \/ U)(c: Connection, logger: Logger, nms: NamedMoveStatesHolder): String \/ U = { + for { + lock <- c.acquireLock(logger) + r <- { + action(c, logger, nms) + }.andFinally(c.releaseLock(logger)(lock)) + } yield r } - def applyMigrationsAndReport(ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean): EDKleisli[Unit] = { - - def openConnection: EDKleisli[(T, Connection)] = { - for { - t <- eAsk - c <- connection() - } yield Tuple2(t, c) - } - - def withConnection[U](action: connectionHandling.namedMoveTypes.EWDKleisli[U]): namedMoveTypes.EWDKleisli[U] = { - import namedMoveTypes._ - import namedMoveTypes.ewSyntax._ - - namedMoveTypes.liftE(openConnection) >=> { - import connectionHandling.namedMoveTypes._ - import connectionHandling.namedMoveTypes.ewSyntax2._ - (liftE(connectionHandling.init()) >> action) andFinally liftE(connectionHandling.close()) - } - } + def withLockedConnection[U](action: (Connection, Logger, NamedMoveStatesHolder) => String \/ U)(db: Database, logger: Logger, nms: NamedMoveStatesHolder): String \/ U = { + withConnection((c, l, s) => { + withLock(action)(c, l, s) + })(db, logger, nms) + } - def acqLock: connectionHandling.EDKleisli[(T, Connection, String)] = { - for { - t <- connectionHandling.eAsk - l <- connectionHandling.acquireLock() - } yield (t._1, t._2, l) - } + def applyMigrationsAndReport(ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean, user: String, remark: String)(db: Database, logger: Logger, reporters: List[Reporter], nms: NamedMoveStatesHolder): String \/ Unit = { - def rlsLock: connectionLockHandling.EDKleisli[Unit] = { - for { - t <- connectionLockHandling.eAsk - _ <- connectionLockHandling.releaseLock(t._3) - } yield () - } + val e = withLockedConnection((c, l, s) => AddHandling.applyMigrations(ms, imo, arb, runTests, user, remark)(c, l, s))(db, logger, nms) - def withLock[U](action: connectionLockHandling.namedMoveTypes.EWDKleisli[U]): connectionHandling.namedMoveTypes.EWDKleisli[U] = { - import connectionHandling.namedMoveTypes._ - import connectionHandling.namedMoveTypes.ewSyntax._ - - connectionHandling.namedMoveTypes.liftE(acqLock) >=> { - import connectionLockHandling.namedMoveTypes._ - import connectionLockHandling.namedMoveTypes.ewSyntax2._ - action andFinally liftE(rlsLock) - } + (nms.nms.actions.lastOption, e) match { + case (Some(ms), -\/(f)) => DescribeAction.describe(ms._1, ms._2, f)(logger) + case (None, -\/(f)) => DescribeAction.describe(f)(logger) + case _ => () } - EitherT[DKleisli, String, Unit]( - for { - nmse <- withConnection(withLock(connectionLockHandling.applyMigrations(ms, imo, arb, runTests))).run.run - - (nms, e) = nmse - - _ <- { - (nms.actions.lastOption, e) match { - case (Some(ms), -\/(f)) => describe(ms._1, ms._2, f) - case (None, -\/(f)) => describe(f) - case _ => point(()) - } - } + ReportersAction.reportToAll(nms.nms)(reporters) - _ <- reportToAll(nms) - } yield e - ) + e } } diff --git a/src/main/scala/smt/SMTImpl.scala b/src/main/scala/smt/SMTImpl.scala index 7668c3b..9f5fcb0 100644 --- a/src/main/scala/smt/SMTImpl.scala +++ b/src/main/scala/smt/SMTImpl.scala @@ -7,10 +7,6 @@ import report.Reporter import java.io.File import smt.migration.FileSplitters._ import smt.migration.Migration -import smt.db.DbAction.HasDb -import smt.report.ReportersAction.HasReporters -import smt.describe.DescribeAction.HasLogger -import smt.db.AddAction.{HasUser, HasRemark} import scalaz.\/ object SMTImpl { @@ -22,24 +18,9 @@ object SMTImpl { private def throwLeft[T](s: TaskStreams)(te: String \/ T): T = te.fold[T]((e: String) => failException(s)(e), identity) - case class StateHandlingDep(db: Database, logger: Logger) - - val stateHandling = new StateHandling[StateHandlingDep] { - lazy val hasDb: HasDb[StateHandlingDep] = _.db - lazy val hasLogger: HasLogger[StateHandlingDep] = _.logger - } - - val handling = new Handling[HandlingDep] { - lazy val hasDb: HasDb[HandlingDep] = _.db - lazy val hasLogger: HasLogger[HandlingDep] = _.logger - lazy val hasReporters: HasReporters[HandlingDep] = _.rps - lazy val hasUser: HasUser[HandlingDep] = _.user - lazy val hasRemark: HasRemark[HandlingDep] = _.remark - } - def showDbState(db: Database, ms: Seq[Migration], imo: Option[(Int, String)], s: TaskStreams): Unit = { - val result = stateHandling.common(MigrationHandling.hashMigrations(ms, imo)).run(StateHandlingDep(db, s.log)).run + val result = StateHandling.common(MigrationHandling.hashMigrations(ms, imo))(db, s.log) result.foreach(co => { co.common.foreach(st => s.log.info(st.toString)) @@ -49,7 +30,7 @@ object SMTImpl { } def showLatestCommon(db: Database, ms: Seq[Migration], imo: Option[(Int, String)], s: TaskStreams): Unit = { - val result = stateHandling.latestCommon(MigrationHandling.hashMigrations(ms, imo)).run(StateHandlingDep(db, s.log)).run + val result = StateHandling.latestCommon(MigrationHandling.hashMigrations(ms, imo))(db, s.log) result.foreach(lco => s.log.info(lco.map(_.toString).getOrElse("None"))) throwLeft(s)(result) @@ -64,9 +45,8 @@ object SMTImpl { } def doApplyMigrations(db: Database, ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean, rs: Seq[Reporter], user: String, remark: Option[String], s: TaskStreams): Unit = { - val action = handling.applyMigrationsAndReport(ms, imo, arb, runTests) - val dep = HandlingDep(db, rs.toList, s.log, user, remark) - throwLeft(s)(action.run(dep).run) + val result = Handling.applyMigrationsAndReport(ms, imo, arb, runTests, user, remark.getOrElse(""))(db, s.log, rs.toList, new NamedMoveStatesHolder()) + throwLeft(s)(result) } def migrateTo(args: Seq[String], db: Database, ms: Seq[Migration], imo: Option[(Int, String)], arb: Boolean, runTests: Boolean, rs: Seq[Reporter], user: String, s: TaskStreams) { @@ -96,8 +76,7 @@ object SMTImpl { val relPath = IO.pathSplit(dir).toSeq val fullPath = relPath.foldLeft[File](sourceDir)((p, s) => p / s) val script = OneFileOneScriptSplitter(fullPath).head - val action = stateHandling.applyScript(script) - val result = action.run(StateHandlingDep(db, s.log)).run + val result = StateHandling.applyScript(script)(db, s.log) throwLeft(s)(result) } case Seq() => throw new Exception("Path expected.") diff --git a/src/main/scala/smt/Writing.scala b/src/main/scala/smt/Writing.scala index 7aac5a7..87e8262 100644 --- a/src/main/scala/smt/Writing.scala +++ b/src/main/scala/smt/Writing.scala @@ -1,6 +1,6 @@ package smt -import scalaz.{Scalaz, Monoid} +import scalaz.{\/, Scalaz, Monoid} import Scalaz._ import smt.migration.Script @@ -39,6 +39,13 @@ object UpMoveState { def downsToApply(downs: List[Script]): UpMoveState = UpMoveState(downsToApply = downs) } +class UpMoveStateHolder { + var ums = UpMoveState() + def add(u: UpMoveState): Unit = { + ums = ums ⊹ u + } +} + case class DownMoveState( appliedDowns: List[Script] = Nil, crashedDown: Option[Script] = None @@ -57,7 +64,14 @@ object DownMoveState { def appliedDown(down: Script): DownMoveState = DownMoveState(appliedDowns = List(down)) - def crashedDown(up: Script): DownMoveState = DownMoveState(crashedDown = Some(up)) + def crashedDown(down: Script): DownMoveState = DownMoveState(crashedDown = Some(down)) +} + +class DownMoveStateHolder { + var dms = DownMoveState() + def add(d: DownMoveState): Unit = { + dms = dms ⊹ d + } } case class NamedMoveStates(actions: List[(String, MoveState)]) @@ -72,4 +86,22 @@ object NamedMoveStates { } def namedMoveState(name: String)(ms: MoveState): NamedMoveStates = NamedMoveStates(List((name, ms))) +} + +class NamedMoveStatesHolder { + var nms = NamedMoveStates.sg.zero + + def addDownMoveStateOf(name: String)(f: DownMoveStateHolder => String \/ Unit): String \/ Unit = { + val dms = new DownMoveStateHolder() + val r = f(dms) + nms = nms ⊹ NamedMoveStates(List((name, dms.dms))) + r + } + + def addUpMoveStateOf(name: String)(f: UpMoveStateHolder => String \/ Unit): String \/ Unit = { + val ums = new UpMoveStateHolder() + val r = f(ums) + nms = nms ⊹ NamedMoveStates(List((name, ums.ums))) + r + } } \ No newline at end of file diff --git a/src/main/scala/smt/db/Connection.scala b/src/main/scala/smt/db/Connection.scala index e42ddf1..cfee8c4 100644 --- a/src/main/scala/smt/db/Connection.scala +++ b/src/main/scala/smt/db/Connection.scala @@ -3,6 +3,7 @@ package smt.db import smt.migration.{Direction, Script, MigrationInfo} import scalaz.\/ import sbt.Logger +import java.util.Date trait Connection { @@ -29,4 +30,7 @@ trait Connection { def releaseLock(logger: Logger)(lock: String): String \/ Unit def close(logger: Logger)(): String \/ Unit + + def add(logger: Logger, name: String, hash: Seq[Byte], dateTime: Date, user: String, remark: String): String \/ Unit = add(logger)(new MigrationInfo(name, hash, dateTime, Some(user), Some(remark))) + } diff --git a/src/main/scala/smt/db/ConnectionAction.scala b/src/main/scala/smt/db/ConnectionAction.scala deleted file mode 100644 index 6eaccf4..0000000 --- a/src/main/scala/smt/db/ConnectionAction.scala +++ /dev/null @@ -1,53 +0,0 @@ -package smt.db - -import smt.migration.{Test, Direction, Script, MigrationInfo} -import smt.util.ActionTypes -import java.util.Date -import smt.describe.DescribeAction - -object ConnectionAction { - type HasConnection[α] = α => Connection -} - -trait ConnectionAction[T] extends ActionTypes[T] { - - val hasConnection: ConnectionAction.HasConnection[T] - val hasLogger: DescribeAction.HasLogger[T] - - def init(): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).init(hasLogger(d))) - - def acquireLock(): EDKleisli[String] = EDKleisli(d => hasConnection(d).acquireLock(hasLogger(d))) - - def state(): EDKleisli[Seq[MigrationInfo]] = EDKleisli(d => hasConnection(d).state(hasLogger(d))) - - def downs(hash: Seq[Byte]): EDKleisli[Seq[Script]] = EDKleisli(d => hasConnection(d).downs(hasLogger(d))(hash)) - - def add(migrationInfo: MigrationInfo): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).add(hasLogger(d))(migrationInfo)) - - def addDowns(migHash: Seq[Byte], downs: Seq[Script]): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).addDowns(hasLogger(d))(migHash, downs)) - - def remove(hash: Seq[Byte]): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).remove(hasLogger(d))(hash)) - - def removeDowns(migHash: Seq[Byte]): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).removeDowns(hasLogger(d))(migHash)) - - def applyScript(script: Script, direction: Direction): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).applyScript(hasLogger(d))(script, direction)) - - def doTest(test: Test): EDKleisli[Unit] = EDKleisli(d => test.run(hasConnection(d))(hasLogger(d))) - - def releaseLock(lock: String): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).releaseLock(hasLogger(d))(lock)) - - def close(): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).close(hasLogger(d))()) -} - -object AddAction { - type HasUser[α] = α => String - - type HasRemark[α] = α => Option[String] -} - -trait AddAction[T] extends ConnectionAction[T] { - val hasUser: AddAction.HasUser[T] - val hasRemark: AddAction.HasRemark[T] - - def add(name: String, hash: Seq[Byte], dateTime: Date): EDKleisli[Unit] = EDKleisli(d => hasConnection(d).add(hasLogger(d))(new MigrationInfo(name, hash, dateTime, Some(hasUser(d)), hasRemark(d)))) -} \ No newline at end of file diff --git a/src/main/scala/smt/db/DbAction.scala b/src/main/scala/smt/db/DbAction.scala deleted file mode 100644 index f9b7555..0000000 --- a/src/main/scala/smt/db/DbAction.scala +++ /dev/null @@ -1,16 +0,0 @@ -package smt.db - -import smt.util.ActionTypes -import smt.describe.DescribeAction - -object DbAction { - type HasDb[α] = α => Database -} - -trait DbAction[T] extends ActionTypes[T] { - - val hasDb: DbAction.HasDb[T] - val hasLogger: DescribeAction.HasLogger[T] - - def connection(): EDKleisli[Connection] = EDKleisli(hasDb(_).connection()) -} diff --git a/src/main/scala/smt/db/LockAction.scala b/src/main/scala/smt/db/LockAction.scala deleted file mode 100644 index 80e5f92..0000000 --- a/src/main/scala/smt/db/LockAction.scala +++ /dev/null @@ -1,5 +0,0 @@ -package smt.db - -object LockAction { - type HasLock[α] = α => String -} diff --git a/src/main/scala/smt/describe/DescribeAction.scala b/src/main/scala/smt/describe/DescribeAction.scala index 964a6df..9516de2 100644 --- a/src/main/scala/smt/describe/DescribeAction.scala +++ b/src/main/scala/smt/describe/DescribeAction.scala @@ -1,26 +1,19 @@ package smt.describe -import smt.util.ActionTypes import smt.{UpMoveState, DownMoveState, MoveState} import sbt.Logger object DescribeAction { - type HasLogger[α] = α => Logger -} - -trait DescribeAction[T] extends ActionTypes[T] { - - val hasLogger: DescribeAction.HasLogger[T] - - def describe(f: String): DKleisli[Unit] = { - DKleisli(t => hasLogger(t).warn(f)) + def describe(f: String)(logger: Logger): Unit = { + logger.warn(f) } - def describe(migName: String, ms: MoveState, f: String): DKleisli[Unit] = { - DKleisli(t => ms match { - case dms: DownMoveState => describeDms(migName, dms, f).foreach(l => hasLogger(t).warn(l)) - case ums: UpMoveState => describeUms(migName, ums, f).foreach(l => hasLogger(t).warn(l)) - }) + + def describe(migName: String, ms: MoveState, f: String)(logger: Logger): Unit = { + ms match { + case dms: DownMoveState => describeDms(migName, dms, f).foreach(l => logger.warn(l)) + case ums: UpMoveState => describeUms(migName, ums, f).foreach(l => logger.warn(l)) + } } private def describeDms(migName: String, dms: DownMoveState, f: String): Seq[String] = { diff --git a/src/main/scala/smt/report/ReporterAction.scala b/src/main/scala/smt/report/ReporterAction.scala deleted file mode 100644 index 6272851..0000000 --- a/src/main/scala/smt/report/ReporterAction.scala +++ /dev/null @@ -1,35 +0,0 @@ -package smt.report - -import smt.NamedMoveStates -import smt.util.ActionTypes -import scalaz.Kleisli -import scalaz.concurrent.Future -import scalaz.Scalaz._ - -object ReporterAction { - type HasReporter[α] = α => Reporter -} - -trait ReporterAction[T] extends ActionTypes[T] { - - val hasReporter: ReporterAction.HasReporter[T] - - def report(nms: NamedMoveStates): DKleisli[Unit] = DKleisli(hasReporter(_).report(nms).getOrElse(())) -} - -object ReportersAction { - type HasReporters[α] = α => List[Reporter] -} - -trait ReportersAction[T] extends ActionTypes[T] { - - val hasReporters: ReportersAction.HasReporters[T] - - lazy val reporterAction = new ReporterAction[Reporter] { - lazy val hasReporter: ReporterAction.HasReporter[Reporter] = identity - } - - def reportToAll(nms: NamedMoveStates): Kleisli[Future, T, Unit] = { - Kleisli[Future, T, Unit](hasReporters(_).traverse_(reporterAction.report(nms).run)) - } -} diff --git a/src/main/scala/smt/report/ReportersAction.scala b/src/main/scala/smt/report/ReportersAction.scala new file mode 100644 index 0000000..c9422ad --- /dev/null +++ b/src/main/scala/smt/report/ReportersAction.scala @@ -0,0 +1,10 @@ +package smt.report + +import smt.NamedMoveStates + +object ReportersAction { + + def reportToAll(nms: NamedMoveStates)(reporters: List[Reporter]): Unit = { + reporters.foreach(_.report(nms)) + } +} diff --git a/src/main/scala/smt/util/ActionTypes.scala b/src/main/scala/smt/util/ActionTypes.scala deleted file mode 100644 index 17906f9..0000000 --- a/src/main/scala/smt/util/ActionTypes.scala +++ /dev/null @@ -1,79 +0,0 @@ -package smt.util - -import scalaz._ -import scalaz.concurrent.Future -import scalaz.\/- -import smt.{NamedMoveStates, DownMoveState, UpMoveState} - -trait ActionTypes[D] { - - type SE[A] = (String \/ A) - - type DKleisli[+A] = Kleisli[Future, D, A] - - def DKleisli[A](f: D => A): DKleisli[A] = Kleisli[Future, D, A](D => Future.delay(f(D))) - - type EDKleisli[+A] = EitherT[DKleisli, String, A] - - def EDKleisli[A](a: DKleisli[SE[A]]): EDKleisli[A] = EitherT[DKleisli, String, A](a) - - def EDKleisli[A](f: D => SE[A]): EDKleisli[A] = EDKleisli(DKleisli(f)) - - def point[A](a: A): DKleisli[A] = DKleisli(_ => a) - - def ePoint[A](a: A): EDKleisli[A] = EDKleisli(DKleisli(_ => \/-(a))) - - def ask: DKleisli[D] = Kleisli.ask[Future, D] - - def eAsk: EDKleisli[D] = EDKleisli(ask.map(\/-(_))) - - def failure(f: String): EDKleisli[Nothing] = EDKleisli(DKleisli(_ => -\/(f))) - - trait WriterTypes[W] { - - implicit val wMonoid: Monoid[W] - - type WDKleisli[+A] = WriterT[DKleisli, W, A] - - def WDKleisli[A](a: DKleisli[(W, A)]): WDKleisli[A] = WriterT[DKleisli, W, A](a) - - type EWDKleisli[+A] = EitherT[WDKleisli, String, A] - - def EWDKleisli[A](wa: WDKleisli[SE[A]]) = EitherT[WDKleisli, String, A](wa) - - def lift[A](dKleisli: DKleisli[A]): WDKleisli[A] = WriterT.writerTMonadTrans[W].liftM(dKleisli) - - def liftE[A](edKleisli: EDKleisli[A]): EWDKleisli[A] = EWDKleisli(lift(edKleisli.run)) - - def putE[A](edKleisli: EDKleisli[A])(w: W): EWDKleisli[A] = EitherT[WDKleisli, String, A](WriterT.put(edKleisli.run)(w)) - - lazy val EWSyntax = EitherTWriterT.eitherTWriterTSyntax[DKleisli, String, W] - - lazy val ewSyntax = KleisliStack.EitherTWriterTKleisli[String, W].tKleisliSyntax[Future, D] - - lazy val ewSyntax2 = EitherTHaerte.eitherTSyntax[WDKleisli, String] - - // the implicits are lazy, otherwise I get NullPointerExceptions - implicit lazy val wInstance = WriterT.writerTMonad[DKleisli, W] - - implicit lazy val ewInstance = EitherT.eitherTMonad[WDKleisli, String] - - implicit lazy val wUnstackedInstance = WriterT.writerTMonad[Future, W] - - implicit lazy val ewUnstackedInstance = EitherT.eitherTMonad[({type λ[+α] = WriterT[Future, W, α]})#λ, String] - } - - def writerTypes[S : Monoid]: WriterTypes[S] = new WriterTypes[S] { - lazy val wMonoid = implicitly[Monoid[S]] - } - - val upMoveTypes = writerTypes[UpMoveState] - - val downMoveTypes = writerTypes[DownMoveState] - - val namedMoveTypes = writerTypes[NamedMoveStates] - - val ekSyntax = KleisliStack.EitherTKleisli[String].tKleisliSyntax[Future, D] - - val eSyntax = EitherTHaerte.eitherTSyntax[DKleisli, String] -} diff --git a/src/main/scala/smt/util/EitherHaerte.scala b/src/main/scala/smt/util/EitherHaerte.scala new file mode 100644 index 0000000..91574a4 --- /dev/null +++ b/src/main/scala/smt/util/EitherHaerte.scala @@ -0,0 +1,42 @@ +package smt.util + +import scalaz.syntax.Ops +import scalaz._ +import scalaz.-\/ +import scalaz.Scalaz._ + +object EitherHaerte { + + trait EitherOps[B] extends Ops[String \/ B] { + def andFinally(f: => String \/ Unit): String \/ B = { + (self, f) match { + case (-\/(a), -\/(ra)) => -\/(a + ra) + case (-\/(a), \/-(_)) => -\/(a) + case (\/-(b), -\/(ra)) => -\/(ra) + case (\/-(b), \/-(_)) => \/-(b) + } + } + + def onLeftDo(f: String => String \/ Unit): String \/ B = { + self match { + case err@ -\/(e) => f(e) >> err + case b@ \/-(_) => b + } + } + + /*def recover[BB >: B](r: (W, A) => EitherTWriterT[F, A, BB, W])(implicit F: Monad[F], W: Semigroup[W]): EitherTWriterT[F, A, BB, W] = { + val f: F[(W, A \/ BB)] = F.bind(self.run.run) { + case (w, -\/(a)) => F.map(r(w, a).run.run)(wabb2 => (W.append(w, wabb2._1), wabb2._2)) + case (w, rb@ \/-(_)) => F.point((w, rb)) + } + + EitherT[({type λ[+α] = WriterT[F, W, α]})#λ, A, BB](WriterT[F, W, A \/ BB](f)) + }*/ + } + + object EitherSyntax { + implicit def toEitherOps[B](v: String \/ B): EitherOps[B] = new EitherOps[B] { + val self = v + } + } +} diff --git a/src/main/scala/smt/util/EitherTHaerte.scala b/src/main/scala/smt/util/EitherTHaerte.scala deleted file mode 100644 index 550d734..0000000 --- a/src/main/scala/smt/util/EitherTHaerte.scala +++ /dev/null @@ -1,33 +0,0 @@ -package smt.util - -import scalaz.syntax.Ops -import scalaz._ -import scalaz.-\/ - -object EitherTHaerte { - - trait EitherTOps[F[+ _], A, B] extends Ops[EitherT[F, A, B]] { - def andFinally(f: EitherT[F, A, Unit])(implicit F: Bind[F], A: Semigroup[A]): EitherT[F, A, B] = { - EitherT( - F.bind(self.run)(r1 => F.map(f.run)(r2 => { - (r1, r2) match { - case (-\/(a), -\/(ra)) => -\/(A.append(a, ra)) - case (-\/(a),\/-(_)) => -\/(a) - case (\/-(b), -\/(ra)) => -\/(ra) - case (\/-(b), \/-(_)) => \/-(b) - } - })) - ) - } - } - - trait EitherTSyntax[F[+ _], A] { - implicit def toEitherTOps[B](v: EitherT[F, A, B]): EitherTOps[F, A, B] = new EitherTOps[F, A, B] { - val self = v - } - } - - def eitherTSyntax[F[+ _], A]: EitherTSyntax[F, A] = new EitherTSyntax[F, A] {} - - -} diff --git a/src/main/scala/smt/util/KleisliStack.scala b/src/main/scala/smt/util/KleisliStack.scala deleted file mode 100644 index 7b43216..0000000 --- a/src/main/scala/smt/util/KleisliStack.scala +++ /dev/null @@ -1,104 +0,0 @@ -package smt.util - -import scalaz.syntax.Ops -import scalaz._ -import Kleisli._ - -object KleisliStack { - - /** - * a typeclass that provides functions to stack a monad-transformer - * and to unstack a monad-transformer - * Data: the value type that the transformer expects - * @tparam Data the expected data type of the monad transformer - * @tparam Stacked the type that results when applying the monad transformer to a monad - */ - trait TStacking[Data[+ _], Stacked[_[+ _], + _]] { - - def stack[M[+ _], A](m: M[Data[A]]): Stacked[M, A] - - def unstack[M[+ _], A](s: Stacked[M, A]): M[Data[A]] - } - - class TKleisli[Data[+ _], Stacked[_[+ _], + _]](S: TStacking[Data, Stacked]) { - - type TKleisli[M[+ _], D, +A] = Stacked[({type λ[+α] = Kleisli[M, D, α]})#λ, A] - - trait TKleisliOps[M[+ _], D, A] extends Ops[TKleisli[M, D, A]] { - - tKlseisliOps => - - def runE: D => Stacked[M, A] = (d: D) => S.stack(S.unstack[({type λ[+α] = Kleisli[M, D, α]})#λ, A](self)(d)) - - def swapStack: Kleisli[({type λ[+α] = Stacked[M, α]})#λ, D, A] = { - val un: Kleisli[M, D, Data[A]] = S.unstack[({type λ[+α] = Kleisli[M, D, α]})#λ, A](self) - un.mapK[({type λ[+α] = Stacked[M, α]})#λ, A]((m: M[Data[A]]) => S.stack(m)) - } - - def swapStackBack[B](swapped: Kleisli[({type λ[+α] = Stacked[M, α]})#λ, D, B]): TKleisli[M, D, B] = { - val un: Kleisli[M, D, Data[B]] = swapped.mapK[M, Data[B]]((m: Stacked[M, B]) => S.unstack(m)) - S.stack[({type λ[+α] = Kleisli[M, D, α]})#λ, B](un) - } - - def >=>[B](k: TKleisli[M, A, B])(implicit eb: Bind[({type λ[+α] = Stacked[M, α]})#λ]): TKleisli[M, D, B] = { - val syn = tKleisliSyntax[M, A] - import syn._ - - swapStackBack(this.swapStack >=> k.swapStack) - } - } - - trait TKleisliSyntax[M[+ _], D] { - implicit def toTKleisliOps[A](v: TKleisli[M, D, A]): TKleisliOps[M, D, A] = new TKleisliOps[M, D, A] { - val self = v - } - } - - def tKleisliSyntax[M[+ _], D]: TKleisliSyntax[M, D] = new TKleisliSyntax[M, D] {} - } - - trait EitherTStackingTypes[E] { - type Data[+A] = E \/ A - - type Stacked[M[+ _], +A] = EitherT[M, E, A] - } - - def EitherTStacking[E]: TStacking[EitherTStackingTypes[E]#Data, EitherTStackingTypes[E]#Stacked] = new TStacking[EitherTStackingTypes[E]#Data, EitherTStackingTypes[E]#Stacked] { - - def stack[M[+ _], A](m: M[EitherTStackingTypes[E]#Data[A]]): EitherTStackingTypes[E]#Stacked[M, A] = EitherT[M, E, A](m) - - def unstack[M[+ _], A](s: EitherTStackingTypes[E]#Stacked[M, A]): M[EitherTStackingTypes[E]#Data[A]] = s.run - } - - def EitherTKleisli[E] = new TKleisli[EitherTStackingTypes[E]#Data, EitherTStackingTypes[E]#Stacked](EitherTStacking[E]) - - trait WriterTStackingTypes[W] { - type Data[+A] = (W, A) - - type Stacked[M[+ _], +A] = WriterT[M, W, A] - } - - def WriterTStacking[W]: TStacking[WriterTStackingTypes[W]#Data, WriterTStackingTypes[W]#Stacked] = new TStacking[WriterTStackingTypes[W]#Data, WriterTStackingTypes[W]#Stacked] { - - def stack[M[+ _], A](m: M[WriterTStackingTypes[W]#Data[A]]): WriterTStackingTypes[W]#Stacked[M, A] = WriterT[M, W, A](m) - - def unstack[M[+ _], A](s: WriterTStackingTypes[W]#Stacked[M, A]): M[WriterTStackingTypes[W]#Data[A]] = s.run - } - - def WriterTKleisli[W] = new TKleisli[WriterTStackingTypes[W]#Data, WriterTStackingTypes[W]#Stacked](WriterTStacking[W]) - - trait EitherTWriterTStackingTypes[E, W] { - type Data[+A] = (W, E \/ A) - - type Stacked[M[+ _], +A] = EitherT[({type λ[+α] = WriterT[M, W, α]})#λ, E, A] - } - - def EitherTWriterTStacking[E, W]: TStacking[EitherTWriterTStackingTypes[E, W]#Data, EitherTWriterTStackingTypes[E, W]#Stacked] = new TStacking[EitherTWriterTStackingTypes[E, W]#Data, EitherTWriterTStackingTypes[E, W]#Stacked] { - - def stack[M[+ _], A](m: M[EitherTWriterTStackingTypes[E, W]#Data[A]]): EitherTWriterTStackingTypes[E, W]#Stacked[M, A] = EitherT[({type λ[+α] = WriterT[M, W, α]})#λ, E, A](WriterT[M, W, E \/ A](m)) - - def unstack[M[+ _], A](s: EitherTWriterTStackingTypes[E, W]#Stacked[M, A]): M[EitherTWriterTStackingTypes[E, W]#Data[A]] = s.run.run - } - - def EitherTWriterTKleisli[E, W] = new TKleisli[EitherTWriterTStackingTypes[E, W]#Data, EitherTWriterTStackingTypes[E, W]#Stacked](EitherTWriterTStacking[E, W]) -} diff --git a/src/main/scala/smt/util/SeqHaerte.scala b/src/main/scala/smt/util/SeqHaerte.scala new file mode 100644 index 0000000..298cef0 --- /dev/null +++ b/src/main/scala/smt/util/SeqHaerte.scala @@ -0,0 +1,42 @@ +package smt.util + +import scala.annotation.tailrec +import scalaz.{\/, -\/, \/-} +import scalaz.syntax.Ops + +object SeqHaerte { + + trait SeqOps[B] extends Ops[Seq[B]] { + def travE[C](f: B => String \/ C): String \/ List[C] = { + @tailrec + def go(l: List[B], acc: List[C]): String \/ List [C] = l match { + case h :: t => f(h) match { + case -\/(err) => -\/(err) + case \/-(c) => go(t, c :: acc) + } + case Nil => \/-(acc) + } + + go(self.toList, Nil).map(_.reverse) + } + + def travE_(f: B => String \/ Unit): String \/ Unit = { + @tailrec + def go(l: List[B]): String \/ Unit = l match { + case h :: t => f(h) match { + case -\/(err) => -\/(err) + case \/-(()) => go(t) + } + case Nil => \/-() + } + + go(self.toList) + } + } + + object SeqSyntax { + implicit def toSeqOps[B](l: Seq[B]): SeqOps[B] = new SeqOps[B] { + val self = l + } + } +} diff --git a/src/main/scala/smt/util/Util.scala b/src/main/scala/smt/util/Util.scala index bae929e..2a1059a 100644 --- a/src/main/scala/smt/util/Util.scala +++ b/src/main/scala/smt/util/Util.scala @@ -11,4 +11,4 @@ object Util { def stringToBytes(s: String): Seq[Byte] = s.getBytes def bytesToString(bs: Seq[Byte]): String = new String(bs.toArray) -} \ No newline at end of file +}