Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GSoC Rough plan #62

Open
TonioGela opened this issue Jun 1, 2024 · 4 comments
Open

GSoC Rough plan #62

TonioGela opened this issue Jun 1, 2024 · 4 comments

Comments

@TonioGela
Copy link
Contributor

TonioGela commented Jun 1, 2024

As discussed with Gabriel, Rishad, Micheal and Than, we will probably organise this work by roughly splitting it into three big chunks:

The idea is to begin importing what's present in the fs2-io's Files type class first, then in the Process one, and re-expose the stricter version of their methods.

I'll begin opening a specific issue to track the progress and ideas about Files, and I'll link it here.

@TonioGela TonioGela pinned this issue Jun 1, 2024
@TonioGela
Copy link
Contributor Author

Something that might end up in that interoperability ticket is mimicking this:

// Find and concatenate all .txt files directly in the working directory using `cat`
os.proc("cat", os.list(wd).filter(_.ext == "txt")).call(stdout = wd / "all.txt")

os.read(wd / "all.txt") ==>
  """I am cowI am cow
    |Hear me moo
    |I weigh twice as much as you
    |And I look good on the barbecue""".stripMargin

more or less in this way:

for {
 textFiles <- wd.listAll.filter(_.ext == "txt")
 stdout <- Process.run("cat", textFiles).stdout
 _ <- (wd / "all.txt").writeUtf8(stdout)
 output <- (wd / "all.txt").readUtf8
} yield output

@TonioGela
Copy link
Contributor Author

Adding another (completely insane) suggestion here. That's what I currently use locally to auto-commit my dotfiles each time a file gets changed (the "watching" functionality is implemented calling this via a launchd macos daemon btw).

Ideally (IMHO), with this lib, it should be possible to rewrite it all in 10/15 lines top.

//> using scala 3.4.0
//> using platform native
//> using nativeGc none
//> using nativeMode release-full
//> using toolkit typelevel::latest
//> using dep org.typelevel::cats-time::0.5.1
//> using packaging.output dotfiles-watcher

import cats.effect.*
import cats.syntax.all.*
import org.typelevel.cats.time._
import fs2.io.file.*
import fs2.io.process.*

object Main extends IOApp:
    def run(args: List[String]): IO[ExitCode] =
      git(args.head)("status","--porcelain")
        .map(_.isEmpty).ifM(IO.unit, logic(args.head)).as(ExitCode.Success)
        .recoverWith {
            case ExitException(code, message) => reportToDesktop(code, message)
            case t: Throwable => reportToDesktop(1, t.getMessage)
        }

def logic(folder: String): IO[Unit] = for {
    prevProfile <- ghProfile("show").map(_.trim)
    _ <- ghProfile("switch", "personal")
    _ <- git(folder)("add", ".")
    date <- IO.realTimeInstant.map(_.show)
    _ <- git(folder)("-c", "commit.gpgsign=false", "commit", "-m", date)
    _ <- git(folder)("push")
    _ <- ghProfile("switch", prevProfile)
  } yield ()

def ghProfile(command: String*) = 
    run("/opt/homebrew/bin/gh", ("profile" +: command)*)

def git(folder: String)(command: String*) = 
    run("/usr/bin/git", ("-C" +: folder +: command)*)

def reportToDesktop(exitCode:Int, message: String): IO[ExitCode] =
    fs2.Stream.emit(message).through(
      Files[IO].writeUtf8(Path("/Users/toniogela/Desktop/dotfiles_watcher_log"))
    ).compile.drain.as(ExitCode(exitCode))

def run(command: String, args:String*): IO[String] =
    ProcessBuilder(command, args.toList).spawn[IO].use(p =>
        (
          p.exitValue,
          p.stdout.through(fs2.text.utf8.decode).compile.string,
          p.stderr.through(fs2.text.utf8.decode).compile.string
        ).parFlatMapN {
          case (0, stdout, stdErr) => IO(stdout)
          case (exitCode, stdout, stdErr) =>
            val errorMessage: String = List(
              Option(stdout).filter(_.nonEmpty).map(s => s"[STDOUT]: $s"),
              Option(stdErr).filter(_.nonEmpty).map(s => s"[STDERR]: $s")
            ).foldLeft(
              s"Non zero exit code ($exitCode) for `$command ${args.mkString(" ")}`"
            ) {
              case (summary, Some(err)) => s"$summary\n$err"
              case (summary, None)      => summary
            }

            IO.raiseError(new ExitException(exitCode, errorMessage))
        }
    )

case class ExitException(exitCode:Int, message:String) extends Throwable

@Hombre-x
Copy link
Contributor

Hombre-x commented Jun 4, 2024

Should we implement the library abstract on F[_] on use plain IO? I think most of the people using tagless in their projects would benefit more with the former, also it is not much more difficult doing it that way as the majority of shellfish is already written like that 🤔

@TonioGela
Copy link
Contributor Author

Should we implement the library abstract on F[_] on use plain IO? I think most of the people using tagless in their projects would benefit more with the former, also it is not much more difficult doing it that way as the majority of shellfish is already written like that 🤔

I just wanted to let you know I haven't answered you here because I did in person. This library aims to make the learning curve less steep and to make life easier for people who want to manipulate files and run external processes, w/o having to worry about tagless final or Stream. That's why we will write it using concrete IO and "strict" methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants