From ef697c04c69b1f80d636250d6cfef14126e746d2 Mon Sep 17 00:00:00 2001
From: Quentin Leblanc <quentin.leblanc@etu.hesge.ch>
Date: Thu, 19 Mar 2020 15:42:02 +0100
Subject: [PATCH] TP7

---
 TP3/src/main/scala/7.result.scala     |  83 ++++++++++++++++
 TP3/src/test/scala/7.resultTest.scala | 136 ++++++++++++++++++++++++++
 2 files changed, 219 insertions(+)
 create mode 100644 TP3/src/main/scala/7.result.scala
 create mode 100644 TP3/src/test/scala/7.resultTest.scala

diff --git a/TP3/src/main/scala/7.result.scala b/TP3/src/main/scala/7.result.scala
new file mode 100644
index 0000000..709a94c
--- /dev/null
+++ b/TP3/src/main/scala/7.result.scala
@@ -0,0 +1,83 @@
+package ch.hepia.tpscala.result
+
+
+sealed trait Result[+E,+A] {
+
+  def map[B]( f: A=>B ): Result[E,B] = this match {
+    case OK(value) => OK(f(value))
+    case Err(value) => Err(value)
+  }
+
+  //F >: E, signifie "F est un supertype de E"
+  def flatMap[B, F >:E]( f: A=>Result[F,B] ): Result[F,B] = this match {
+    case OK(value) => f(value)
+    case Err(value) => Err(value)
+  }
+
+  //Retourne le résultat si OK
+  //transforme l'erreur en une valeur valide sinon
+  //B >: A signifie "B est un supertype de A
+  //Une seul implémentation possible !
+  def get[B >: A]( f: E => B ): B = this match {
+    case OK(value) => value
+    case Err(value) => f(value)
+  }
+
+  //Une seul implémentation possible !
+  def fold[B]( f: E => B, g: A => B ): B = this match {
+    case OK(value) => g(value)
+    case Err(value) => f(value)
+  }
+
+  def toOption: Option[A] =
+    fold[Option[A]] (_ => None, Some.apply)
+
+}
+case class OK[A]( value: A ) extends Result[Nothing,A]
+case class Err[E]( value: E ) extends Result[E,Nothing]
+object Result {
+  //Si il y a au moins une erreur dans la liste, le résultat est une erreur
+  //Si il n'y a que des succès, liste les succès
+  def sequence[E,A]( res: List[Result[E,A]] ): Result[E,List[A]] = {
+    def reqSequence[E, A](req: List[Result[E, A]], acc: Result[E, List[A]]): Result[E, List[A]] = req match {
+      case Nil => acc.map(_.reverse)
+      case h :: tail => reqSequence(tail, acc.flatMap( r => h.map( a => a :: r)))
+    }
+    reqSequence(res, OK(Nil))
+  }
+}
+
+
+case class Config( hostname: String, port: Int )
+object Config {
+  private def line2keyValue( line: String ): Result[String,(String,String)] = {
+    val elements = line.split("=")
+    if( elements.size != 2 ) Err("Syntax error: " + line )
+    else OK( (elements(0), elements(1) ) )
+  }
+
+  private def lines2map( lines: List[String] ): Result[String,Map[String,String]] = 
+    Result.sequence( lines.map(line2keyValue) ).map( _.toMap )
+
+  private def mget[K,V](mkv: Map[K,V], key: K): Result[String, V] = mkv.get(key) match {
+    case Some(v) => OK(v)
+    case None => Err("Missing Key: "+key)
+  }
+
+  private def safeInt(str: String): Result[String, Int] =
+    try {
+      OK(str.toInt)
+    }catch {
+      case _ => Err("Cannot convert String :" + str + "  : to Int")
+    }
+
+  //Utilisez lines2map pour parvenir au résultat
+  //Attention au clés manquantes
+  def parse( lines: List[String] ): Result[String,Config] =
+      for {
+        keyVal <- lines2map(lines)
+        h <- mget(keyVal, "hostname")
+        pStr <- mget(keyVal, "port")
+        p <- safeInt(pStr)
+      } yield Config(h, p)
+}
diff --git a/TP3/src/test/scala/7.resultTest.scala b/TP3/src/test/scala/7.resultTest.scala
new file mode 100644
index 0000000..196fe74
--- /dev/null
+++ b/TP3/src/test/scala/7.resultTest.scala
@@ -0,0 +1,136 @@
+package ch.hepia.tpscala
+
+import org.scalatest.funsuite.AnyFunSuite
+
+import result._
+
+/****
+ * 
+ * Dans SBT vous pouvez n'exécuter que ces tests:
+ * 
+ *  > testOnly ch.hepia.tpscala.Result7Suite
+ * 
+ */
+
+class Result7Suite extends AnyFunSuite {
+
+
+  test( "map" ) {
+    val hello: Result[String,String] = OK("hello")
+    val notFound: Result[Int,String] = Err(404)
+
+    assert( hello.map(_.size) == OK(5) )
+    assert( notFound.map(_.toString) == notFound )
+
+    assert( hello.map(identity) == hello )
+    assert( notFound.map(identity) == notFound )
+
+    assert( hello.map(_.size).map( _ > 2) ==
+      hello.map( _.size > 2 ) )
+
+    assert( notFound.map(_ * 2 ).map( _.toString) ==
+      notFound.map( i => (i*2).toString  ) )
+  }
+
+  test( "flatMap" ) {
+    type Res[A] = Result[String,A]
+    val goodHost: Res[String] = OK("hello.com")
+    val badHost: Res[String] = Err("NA")
+    val goodPort: Res[Int] = OK(80)
+    val badPort: Res[Int] = Err("undefined")
+
+    assert(
+      goodHost.flatMap{ host =>
+        goodPort.map{ port =>
+          Config(host,port)
+        }
+      } == OK( Config("hello.com",80) )
+    )
+
+    assert( 
+      goodHost.flatMap{ host =>
+        badPort.map{ port =>
+          Config(host,port)
+        }
+      } == badPort
+    )
+
+    assert( 
+      badHost.flatMap{ host =>
+        goodPort.map{ port =>
+          Config(host,port)
+        }
+      } == badHost
+    )
+
+    assert(
+      badHost.flatMap{ host =>
+        badPort.map{ port =>
+          Config(host,port)
+        }
+      } == badHost
+    )
+  }
+
+  test("get") {
+    val found: Result[String, Long] = OK(665)
+    val notFound: Result[String,Long] = Err("not found")
+
+    assert( found.get( _ => -1 ) == 665 )
+    assert( notFound.get( _ => -1 ) == -1 )
+
+    def str2status( str: String ) = str match {
+      case "not found" => 404
+      case "conflict" => 409
+      case "unauthorized" => 403
+      case _ => 400
+    }
+
+    assert( found.map( _ => 200).get(str2status) == 200 )
+    assert( notFound.map( _ => 200).get(str2status) == 404 )
+  }
+
+
+  test( "parse config" ) {
+    val lines1 = List(
+      "port=8888",
+      "hostname=www.example.com",
+    )
+
+    val lines2 = List(
+      "port: 8888",
+      "hostname: www.example.com",
+    )
+
+    val lines3 = List(
+      "port=8888",
+    )
+
+    val lines4 = List(
+      "port=HTTP",
+      "hostname=www.example.com",
+    )
+
+
+    val default = Config("localhost", 8080 )
+
+    assert( Config.parse( lines1 ).get( _ => default )
+      == Config("www.example.com",8888) )
+
+    assert( Config.parse( lines2 ).get( _ => default )
+      == default )
+
+    assert( Config.parse( lines3 ).get( _ => default )
+      == default )
+
+    assert( Config.parse( lines4 ).get( _ => default )
+      == default )
+
+
+  }
+
+
+
+
+
+}
-- 
GitLab