Skip to content

Commit 39a9742

Browse files
committed
Initial commit
0 parents  commit 39a9742

85 files changed

Lines changed: 3577 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target/
2+
local/
3+
_site/
4+
result

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2013 Paul Chiusano, and respective contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Emil - Email without a
2+
3+
… because, E-Mail is still important :wink:
4+
5+
<a href="https://xkcd.com/970/">
6+
<img height="300" align="right" style="float:right" src="https://imgs.xkcd.com/comics/the_important_field.png">
7+
</a>
8+
9+
Emil is a library for dealing with E-Mail in Scala. The api is based
10+
on [Cats](https://github.com/typelevel/cats) and
11+
[FS2](https://github.com/functional-streams-for-scala/fs2). It comes
12+
with a backend implementation that is based on the well known [Java
13+
Mail](https://github.com/eclipse-ee4j/mail) library. As such it is just
14+
another wrapper library, but also a bit different.
15+
16+
17+
Please see the [microsite](https://eikek.github.io/emil/) for more
18+
information.

build.sbt

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import com.typesafe.sbt.SbtGit.GitKeys._
2+
3+
val scala212 = "2.12.10"
4+
val scala213 = "2.13.1"
5+
6+
val sharedSettings = Seq(
7+
organization := "com.github.eikek",
8+
scalaVersion := scala212,
9+
scalacOptions ++=
10+
Seq("-feature",
11+
"-deprecation",
12+
"-unchecked",
13+
"-encoding", "UTF-8",
14+
"-language:higherKinds") ++
15+
(if (scalaBinaryVersion.value.startsWith("2.12"))
16+
List("-Xfatal-warnings", // fail when there are warnings
17+
"-Xlint",
18+
"-Yno-adapted-args",
19+
"-Ywarn-dead-code",
20+
"-Ywarn-unused-import",
21+
"-Ypartial-unification",
22+
"-Ywarn-value-discard")
23+
else if (scalaBinaryVersion.value.startsWith("2.13"))
24+
List("-Werror"
25+
, "-Wdead-code"
26+
, "-Wunused"
27+
, "-Wvalue-discard")
28+
else
29+
Nil
30+
),
31+
crossScalaVersions := Seq(scala212, scala213),
32+
scalacOptions in Test := Seq(),
33+
scalacOptions in (Compile, console) := Seq(),
34+
licenses := Seq("MIT" -> url("http://spdx.org/licenses/MIT")),
35+
homepage := Some(url("https://github.com/eikek/bitpeace"))
36+
)
37+
38+
val testSettings = Seq(
39+
testFrameworks += new TestFramework("minitest.runner.Framework"),
40+
libraryDependencies ++=
41+
(Dependencies.miniTest ++
42+
Dependencies.logback ++
43+
Dependencies.greenmail).
44+
map(_ % Test)
45+
)
46+
47+
val buildInfoSettings = Seq(
48+
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion, gitHeadCommit, gitHeadCommitDate, gitUncommittedChanges, gitDescribedVersion),
49+
buildInfoOptions += BuildInfoOption.ToJson,
50+
buildInfoOptions += BuildInfoOption.BuildTime
51+
)
52+
53+
54+
lazy val common = project.in(file("modules/common")).
55+
settings(sharedSettings).
56+
settings(testSettings).
57+
settings(
58+
name := "emil-common",
59+
libraryDependencies ++=
60+
Dependencies.fs2 ++
61+
Dependencies.fs2io
62+
)
63+
64+
lazy val javamail = project.in(file("modules/javamail")).
65+
settings(sharedSettings).
66+
settings(testSettings).
67+
settings(
68+
name := "emil-javamail",
69+
libraryDependencies ++=
70+
Dependencies.fs2 ++
71+
Dependencies.fs2io ++
72+
Dependencies.javaxMail ++
73+
Dependencies.loggingApi
74+
).
75+
dependsOn(common % "compile->compile;test->test")
76+
77+
78+
lazy val microsite = project.in(file("modules/microsite")).
79+
enablePlugins(MicrositesPlugin).
80+
settings(sharedSettings).
81+
settings(
82+
name := "emil-microsite",
83+
crossScalaVersions := Seq(),
84+
publishArtifact := false,
85+
skip in publish := true,
86+
scalaVersion := scala212,
87+
micrositeFooterText := Some(
88+
"""
89+
|<p>&copy; 2019 <a href="https://github.com/eikek/emil">Emil, v{{site.version}}</a></p>
90+
|""".stripMargin
91+
),
92+
micrositeName := "Emil",
93+
micrositeDescription := "Emil – E-Mail library for Scala",
94+
micrositeBaseUrl := "/emil",
95+
micrositeAuthor := "eikek",
96+
micrositeGithubOwner := "eikek",
97+
micrositeGithubRepo := "emil",
98+
micrositeGitterChannel := false,
99+
micrositeShareOnSocial := false,
100+
fork in tut := true,
101+
scalacOptions in Tut ++=
102+
Seq("-feature",
103+
"-deprecation",
104+
"-unchecked",
105+
"-encoding", "UTF-8",
106+
"-language:higherKinds"),
107+
micrositeCompilingDocsTool := WithTut
108+
).
109+
dependsOn(common, javamail)
110+
111+
val root = project.in(file(".")).
112+
settings(sharedSettings).
113+
settings(
114+
name := "emil-root"
115+
).
116+
aggregate(common, javamail)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package emil
2+
3+
import cats.{Applicative, FlatMap}
4+
5+
trait Access[F[_], C <: Connection] {
6+
7+
def getInbox: MailOp[F, C, MailFolder]
8+
9+
def createFolder(parent: Option[MailFolder], name: String): MailOp[F, C, MailFolder]
10+
11+
def findFolder(parent: Option[MailFolder], name: String): MailOp[F, C, Option[MailFolder]]
12+
13+
def getOrCreateFolder(parent: Option[MailFolder], name: String)
14+
(implicit ev0: FlatMap[F], ev1: Applicative[F]): MailOp[F, C, MailFolder] =
15+
findFolder(parent, name).flatMap {
16+
case Some(mf) => MailOp.pure(mf)
17+
case None => createFolder(parent, name)
18+
}
19+
20+
def getMessageCount(folder: MailFolder): MailOp[F, C, Int]
21+
22+
def search(folder: MailFolder, max: Int)(query: SearchQuery): MailOp[F, C, SearchResult[MailHeader]]
23+
24+
def searchAndLoad(folder: MailFolder, max: Int)(query: SearchQuery): MailOp[F, C, SearchResult[Mail[F]]]
25+
26+
def loadMail(mh: MailHeader): MailOp[F, C, Option[Mail[F]]]
27+
28+
def moveMail(mh: MailHeader, target: MailFolder): MailOp[F, C, Unit]
29+
30+
def copyMail(mh: MailHeader, target: MailFolder): MailOp[F, C, Unit]
31+
32+
def putMail(mh: Mail[F], target: MailFolder): MailOp[F, C, Unit]
33+
34+
def deleteMails(mhs: Seq[MailHeader]): MailOp[F, C, DeleteResult]
35+
36+
def deleteMail(mh: MailHeader*): MailOp[F, C, DeleteResult] =
37+
deleteMails(mh)
38+
39+
def searchDelete(folder: MailFolder, max: Int)(query: SearchQuery)(implicit ev: FlatMap[F]): MailOp[F, C, DeleteResult] =
40+
search(folder, max)(query).
41+
flatMap(result => deleteMails(result.mails))
42+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package emil
2+
3+
import java.nio.charset.StandardCharsets
4+
5+
import cats.implicits._
6+
import cats.Applicative
7+
import cats.effect.Sync
8+
import fs2.Chunk.ByteVectorChunk
9+
import fs2.Stream
10+
import scodec.bits.ByteVector
11+
12+
final case class Attachment[F[_]]( filename: Option[String]
13+
, mimeType: MimeType
14+
, content: Stream[F, Byte]
15+
, length: F[Long]) {
16+
17+
def withMimeType(mt: MimeType): Attachment[F] =
18+
copy(mimeType = mt)
19+
20+
def withFilename(name: String): Attachment[F] =
21+
copy(filename = Some(name))
22+
23+
def withLength(flen: F[Long]): Attachment[F] =
24+
copy(length = flen)
25+
26+
def withLength(len: Long)(implicit ev: Applicative[F]): Attachment[F] =
27+
withLength(len.pure[F])
28+
}
29+
30+
object Attachment {
31+
32+
def apply[F[_]: Sync](filename: Option[String], mimeType: MimeType, content: Stream[F, Byte]): Attachment[F] = {
33+
val len: F[Long] = content.compile.foldChunks(0L)((n, ch) => n + ch.size)
34+
Attachment(filename, mimeType, content, len)
35+
}
36+
37+
def text[F[_]: Applicative](text: String): Attachment[F] = {
38+
val bytes = text.getBytes(StandardCharsets.UTF_8)
39+
Attachment(None,
40+
MimeType.textPlain,
41+
Stream.chunk(ByteVectorChunk(ByteVector.view(bytes))),
42+
bytes.length.toLong.pure[F])
43+
}
44+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package emil
2+
3+
final case class Attachments[F[_]](all: Vector[Attachment[F]]) {
4+
5+
def add(attach: Attachment[F]): Attachments[F] =
6+
Attachments(attach +: all)
7+
8+
def isEmpty: Boolean = all.isEmpty
9+
10+
def nonEmpty: Boolean = !isEmpty
11+
12+
def size: Int = all.size
13+
14+
def ++ (next: Attachments[F]): Attachments[F] =
15+
Attachments(all ++ next.all)
16+
}
17+
18+
object Attachments {
19+
def empty[F[_]]: Attachments[F] = Attachments(Vector.empty)
20+
def apply[F[_]](a0: Attachment[F], as: Attachment[F]*): Attachments[F] =
21+
Attachments(a0 +: as.toVector)
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package emil
2+
3+
trait Connection {
4+
5+
def config: MailConfig
6+
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package emil
2+
3+
case class DeleteResult(count: Int) {
4+
5+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package emil
2+
3+
import cats.effect.{Bracket, Resource}
4+
5+
trait Emil[F[_], C <: Connection] { self =>
6+
7+
def connection(mc: MailConfig): Resource[F, C]
8+
9+
def sender: Send[F, C]
10+
11+
def access: Access[F, C]
12+
13+
def apply(mc: MailConfig)(implicit F: Bracket[F, Throwable]): Emil.Run[F, C] =
14+
new Emil.Run[F, C] {
15+
def run[A](op: MailOp[F, C, A]): F[A] =
16+
self.connection(mc).use(op.run)
17+
18+
def send(mails: Mail[F]*): F[Unit] =
19+
run(sender.sendMails(mails))
20+
}
21+
}
22+
23+
object Emil {
24+
25+
trait Run[F[_], C <: Connection] {
26+
def run[A](op: MailOp[F, C, A]): F[A]
27+
28+
def send(mails: Mail[F]*): F[Unit]
29+
}
30+
}

0 commit comments

Comments
 (0)