diff --git a/haskell/.gitignore b/Bindings/Haskell/.gitignore similarity index 100% rename from haskell/.gitignore rename to Bindings/Haskell/.gitignore diff --git a/haskell/README.md b/Bindings/Haskell/README.md similarity index 100% rename from haskell/README.md rename to Bindings/Haskell/README.md diff --git a/haskell/cabal.project b/Bindings/Haskell/cabal.project similarity index 100% rename from haskell/cabal.project rename to Bindings/Haskell/cabal.project diff --git a/haskell/dev_run.sh b/Bindings/Haskell/dev_run.sh similarity index 100% rename from haskell/dev_run.sh rename to Bindings/Haskell/dev_run.sh diff --git a/Bindings/Haskell/flake.lock b/Bindings/Haskell/flake.lock new file mode 100644 index 0000000..b20190e --- /dev/null +++ b/Bindings/Haskell/flake.lock @@ -0,0 +1,149 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gomod2nix": { + "inputs": { + "flake-utils": [ + "p2prc", + "flake-utils" + ], + "nixpkgs": [ + "p2prc", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733668782, + "narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "514283ec89c39ad0079ff2f3b1437404e4cba608", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 0, + "narHash": "sha256-m/lh6hYMIWDYHCAsn81CDAiXoT3gmxXI9J987W5tZrE=", + "path": "/nix/store/wj2qla569hnxwqfc26imv5hqbxc1rc27-source", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1735834308, + "narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6df24922a1400241dae323af55f30e4318a6ca65", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "p2prc": { + "inputs": { + "flake-utils": "flake-utils", + "gomod2nix": "gomod2nix", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 0, + "narHash": "sha256-f4e4JcQzBS+R+8HqJbQVeecInm2ggInJuSSymj6gtQo=", + "path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source", + "type": "path" + }, + "original": { + "path": "/nix/store/hmk8v0lxifsscll054qsdnzxqfql1998-source", + "type": "path" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "p2prc": "p2prc", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/Bindings/Haskell/flake.nix b/Bindings/Haskell/flake.nix new file mode 100644 index 0000000..eababdb --- /dev/null +++ b/Bindings/Haskell/flake.nix @@ -0,0 +1,24 @@ +{ + description = "Nix flake for P2PRC Haskell library"; + + inputs = { + p2prc.url = "../../"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, utils, p2prc }: utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + cabal-install + haskell.compiler.ghc96 + zlib.dev + p2prc.outputs.packages.${system}.default + ]; + }; + } + ); +} diff --git a/Bindings/Haskell/gen_docs.sh b/Bindings/Haskell/gen_docs.sh new file mode 100755 index 0000000..44ad4dc --- /dev/null +++ b/Bindings/Haskell/gen_docs.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rm -rf dist-newstyle/ + +cabal haddock + +rm -rf ../../Docs/haskell + +cp -r \ + ./dist-newstyle/build/x86_64-linux/ghc-9.6.6/p2prc-0.1.0.0/doc/html/p2prc/ \ + ../../Docs/haskell diff --git a/Bindings/Haskell/lib/API.hs b/Bindings/Haskell/lib/API.hs new file mode 100644 index 0000000..d82fbb1 --- /dev/null +++ b/Bindings/Haskell/lib/API.hs @@ -0,0 +1,151 @@ + +module API + ( P2PRCapi(..) + , MapPortRequest(..) + , p2prcAPI + ) + where + + +import System.Directory ( getCurrentDirectory ) + +import System.Process ( ProcessHandle ) + +import Data.Aeson ( FromJSON ) + +import Error + ( IOEitherError + ) + +import JSON + ( IPAddressTable(..) + , MapPortResponse(..) + , P2prcConfig + ) + +import CLI + ( StdInput(..) + , CLIOpt(..) + , eitherErrDecode + , p2PrcCmdName + , eitherExecProcess + , eitherExecProcessParser + , spawnProcP2Prc + ) + + + +-- | Lower level P2PRC Haskell api that exposes basic functionality necessary to joint the network. + +data P2PRCapi + = MkP2PRCapi + { startServer :: IOEitherError ProcessHandle + -- ^ Start server + , execInitConfig :: IOEitherError P2prcConfig + -- ^ Instantiate server configuration + , execListServers :: IOEitherError IPAddressTable + -- ^ List servers in network + , execMapPort :: MapPortRequest -> IOEitherError MapPortResponse + -- ^ Exposes and associates a local TCP port with a remote DNS address + } + + +-- | This defines the request required to create an association between a TCP socket port and a DNS server in the network. If successful, it makes a resource available in the network. +data MapPortRequest = + MkMapPortRequest -- ^ P2PRC's port allocation request value + Int -- ^ TCP socket number + String -- ^ Network domain name + + +{-| + This function intiates a pure P2PRC runtime state and builds up a 'P2PRCapi' API instance. It allows a developer to create computing orchestration algorithms using the API primitives. + + ==== __Example__ + + The following example show how this function can be used to expose the runtime functionalities: + + @ +module Main where + +import P2PRC + ( p2prcAPI + , P2PRCapi(..) + ) + +main :: IO () +main = + + print "Hello P2PRC" + + -- your code logic goes here + + where + + MkP2PRCapi + { startServer=startServer + , execMapPort=execMapPort + , execListServers=execListServers + , execInitConfig=execInitConfig + } = p2prcAPI + @ +-} + +{-# WARNING p2prcAPI "This function is currently unstable because the configuration reading is dependent on the following issue: https://github.com/Akilan1999/p2p-rendering-computation/issues/120" #-} +p2prcAPI :: P2PRCapi +p2prcAPI = + MkP2PRCapi + { startServer = spawnProcP2Prc p2PrcCmdName [ MkOptAtomic "--s" ] + + , execListServers = + execProcP2PrcParser [ MkOptAtomic "--ls" ] MkEmptyStdInput + + , execMapPort = + \ (MkMapPortRequest portNumber domainName) -> + execProcP2PrcParser + [ MkOptTuple + ( "--mp" + , show portNumber + ) + , MkOptTuple + ( "--dn" + , domainName + ) + ] + MkEmptyStdInput + + , execInitConfig = do + + confInitRes <- execProcP2Prc [ MkOptAtomic "--dc" ] MkEmptyStdInput + + case confInitRes of + (Right _) -> do + + -- TODO: get config file name dynamically + -- + currDirectory <- getCurrentDirectory + + -- TODO: change values before loading file + let fname = currDirectory ++ "/config.json" :: FilePath + + + -- TODO: read config check if file exists + configContent <- readFile fname + + pure $ eitherErrDecode configContent + + (Left err) -> pure $ Left err + + } + + + where + + execProcP2PrcParser :: + FromJSON a => + [CLIOpt] -> StdInput -> IOEitherError a + + execProcP2PrcParser = eitherExecProcessParser p2PrcCmdName + -- TODO: GHC question, why does it scope down instead staying generic + + execProcP2Prc = eitherExecProcess p2PrcCmdName + diff --git a/haskell/lib/CLI.hs b/Bindings/Haskell/lib/CLI.hs similarity index 78% rename from haskell/lib/CLI.hs rename to Bindings/Haskell/lib/CLI.hs index 6a1afc2..ff0b69e 100644 --- a/haskell/lib/CLI.hs +++ b/Bindings/Haskell/lib/CLI.hs @@ -23,8 +23,6 @@ import Data.Aeson , eitherDecode ) -import qualified Data.Text as T - import qualified Data.ByteString.Lazy.Char8 as LBC8 @@ -79,7 +77,6 @@ eitherExecProcess cmd opts input = ExitFailure i -> Left $ MkCLISystemError i cmd err _ -> Right out - optsToCLI :: [CLIOpt] -> CLIOptsInput optsToCLI = concatMap _optToCLI where @@ -116,25 +113,8 @@ eitherErrorDecode esa = (Right v) -> Right v -getP2PrcCmd :: IOEitherError String -getP2PrcCmd = do - - -- assumes the program is ran inside the haskell module in p2prc's repo - -- assumes that last path segment is "haskell" and that p2prc binary's name is "p2p-rendering-computation" - - let trimString = T.unpack . T.strip . T.pack - - eitherErrPwd <- eitherExecProcess "pwd" [MkEmptyOpts] MkEmptyStdInput - - case eitherErrPwd of - - (Right pwdOut) -> - eitherExecProcess - "sed" - [ MkOptAtomic "s/haskell/p2p-rendering-computation/" ] - $ MkStdInputVal - $ trimString pwdOut - - err -> pure err - +-- assumes the program is ran inside the haskell module in p2prc's repo +-- assumes that last path segment is "haskell" and that p2prc binary's name is "p2p-rendering-computation" +p2PrcCmdName :: String +p2PrcCmdName = "p2p-rendering-computation" diff --git a/haskell/lib/Engine.hs b/Bindings/Haskell/lib/Engine.hs similarity index 89% rename from haskell/lib/Engine.hs rename to Bindings/Haskell/lib/Engine.hs index 15838df..4a78e77 100644 --- a/haskell/lib/Engine.hs +++ b/Bindings/Haskell/lib/Engine.hs @@ -18,7 +18,7 @@ import System.Process ( terminateProcess ) import API ( P2PRCapi(..) , MapPortRequest(..) - , getP2prcAPI + , p2prcAPI ) @@ -60,7 +60,13 @@ import API runP2PRC :: MapPortRequest -- ^ TCP Port Request -> IO () -runP2PRC (MkMapPortRequest portNumber domainName) = do +runP2PRC + ( MkMapPortRequest + portNumber + domainName + ) + = + let -- -- TODO: add quickcheck testing (quickchecking-dynamic) @@ -87,19 +93,15 @@ runP2PRC (MkMapPortRequest portNumber domainName) = do -- TODO: Error -- assign error: should parse other error + ( MkP2PRCapi + { startServer = startServer + , execInitConfig = execInitConfig + , execListServers = execListServers + , execMapPort = execMapPort + } + ) = p2prcAPI - - eitherP2prcAPI <- getP2prcAPI - - case eitherP2prcAPI of - (Right - ( MkP2PRCapi - { startServer = startServer - , execInitConfig = execInitConfig - , execListServers = execListServers - , execMapPort = execMapPort - } - )) -> do + in do let @@ -147,7 +149,6 @@ runP2PRC (MkMapPortRequest portNumber domainName) = do (Left err) -> print err - (Left err) -> print err where diff --git a/haskell/lib/Error.hs b/Bindings/Haskell/lib/Error.hs similarity index 100% rename from haskell/lib/Error.hs rename to Bindings/Haskell/lib/Error.hs diff --git a/haskell/lib/JSON.hs b/Bindings/Haskell/lib/JSON.hs similarity index 100% rename from haskell/lib/JSON.hs rename to Bindings/Haskell/lib/JSON.hs diff --git a/haskell/lib/P2PRC.hs b/Bindings/Haskell/lib/P2PRC.hs similarity index 98% rename from haskell/lib/P2PRC.hs rename to Bindings/Haskell/lib/P2PRC.hs index 72e3f11..1fb78c0 100644 --- a/haskell/lib/P2PRC.hs +++ b/Bindings/Haskell/lib/P2PRC.hs @@ -40,7 +40,7 @@ module P2PRC It is intended this way to give freedom to the developer to implement their own orchestration strategies. -} runP2PRC - , getP2prcAPI + , p2prcAPI -- * Data Types {- | This section describes and explains the library's type system, more specifically, the interfaces and primitive types. @@ -82,7 +82,7 @@ import JSON import API ( MapPortRequest(..) , P2PRCapi(..) - , getP2prcAPI + , p2prcAPI ) diff --git a/haskell/p2prc.cabal b/Bindings/Haskell/p2prc.cabal similarity index 97% rename from haskell/p2prc.cabal rename to Bindings/Haskell/p2prc.cabal index 55af357..3d188de 100644 --- a/haskell/p2prc.cabal +++ b/Bindings/Haskell/p2prc.cabal @@ -60,7 +60,6 @@ library other-modules: API , Engine , CLI - , Environment , JSON , Error diff --git a/haskell/src/Main.hs b/Bindings/Haskell/src/Main.hs similarity index 100% rename from haskell/src/Main.hs rename to Bindings/Haskell/src/Main.hs diff --git a/Docs/haskell/P2PRC.html b/Docs/haskell/P2PRC.html index 9c67729..606121b 100644 --- a/Docs/haskell/P2PRC.html +++ b/Docs/haskell/P2PRC.html @@ -9,28 +9,32 @@ main :: IO () main = runP2PRC ( MkMapPortRequest 8080 "jose.akilan.io" - )
These are the available functions available to interact with the P2Prc environment, at a lower level of abstraction. - It is intended this way to give freedom to the developer to implement their own orchestration strategies.
Arguments
| :: MapPortRequest | TCP Port Request |
| -> IO () |
This function starts and bootstraps the P2PRC runtime that associates the a specific host's machine port to a DNS address to expose a certain application to the P2PRC network. You will only need to also import the MkMapPortRequest data constructor to represent the this port request.
This example demonstrates how it can be ran on the IO context:
example :: IO () + )
These are the available functions available to interact with the P2Prc environment, at a lower level of abstraction. + It is intended this way to give freedom to the developer to implement their own orchestration strategies.
Arguments
| :: MapPortRequest | TCP Port Request |
| -> IO () |
This function starts and bootstraps the P2PRC runtime that associates the a specific host's machine port to a DNS address to expose a certain application to the P2PRC network. You will only need to also import the MkMapPortRequest data constructor to represent the this port request.
This example demonstrates how it can be ran on the IO context:
example :: IO ()
example = do
runP2PRC
( MkMapPortRequest 8080 "jose.akilan.io"
)
- getP2prcAPI :: IOEitherError P2PRCapi #
Warning: This function is currently unstable since it assumes that the Haskell program is executed from the P2PRC "haskell" subfolder and the "p2prc" executable in the the root folder.
This function cleans the previous running state (ensuring a pure P2PRC runtime state) and builds up a conditional P2PRCapi instance.
The following example show how this function can be used to expose the runtime functionalities:
example :: IOEitherError P2PRCapi - example = do +
Warning: This function is currently unstable because the configuration reading is dependent on the following issue: https://github.com/Akilan1999/p2p-rendering-computation/issues/120
This function intiates a pure P2PRC runtime state and builds up a P2PRCapi API instance. It allows a developer to create computing orchestration algorithms using the API primitives.
The following example show how this function can be used to expose the runtime functionalities:
module Main where
- eitherP2prcAPI <- getP2prcAPI
+import P2PRC
+ ( p2prcAPI
+ , P2PRCapi(..)
+ )
- case eitherP2prcAPI of
- ( Right
- ( MkP2PRCapi
- { startServer = startServer
- , execInitConfig = execInitConfig
- , execListServers = execListServers
- , execMapPort = execMapPort
- }
- )) -> do
+main :: IO ()
+main =
- -- Your code logic
+ print "Hello P2PRC"
- errValue -> errValue
- This section describes and explains the library's type system, more specifically, the interfaces and primitive types.
This section gives an overview on the runtime and host machine interfaces.
Lower level P2PRC Haskell api that exposes basic functionality necessary to joint the network.
Constructors
| MkP2PRCapi | |
Fields
| |
newtype P2prcConfig #
Warning: This type is unstable at the moment due to the P2PRC's library error handling bug. For more information visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114#issuecomment-2474737015
This represents the server configuration that defines its attributes and behaviours in the network, as well as, the location of the runtime persistence artifacts.
Constructors
| MkP2prConfig | |
Fields
| |
| FromJSON P2prcConfig # | |
Defined in JSON | |
| Show P2prcConfig # | |
Defined in JSON Methods showsPrec :: Int -> P2prcConfig -> ShowS # show :: P2prcConfig -> String # showList :: [P2prcConfig] -> ShowS # | |
data MapPortRequest #
This defines the request required to create an association between a TCP socket port and a DNS server in the network. If successful, it makes a resource available in the network.
Constructors
| MkMapPortRequest | P2PRC's port allocation request value |
newtype MapPortResponse #
Warning: This newtype is unstable at the moment due to the P2PRC's library error handling bug. For more information visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114#issuecomment-2474737015
This represents P2PRC's response to the TCP port and DNS address allocation. This value will confirm the successful allocation and return information about it.
Constructors
| MkMapPortResponse | Allocation information value |
Fields
| |
| FromJSON MapPortResponse # | |
Defined in JSON Methods parseJSON :: Value -> Parser MapPortResponse # parseJSONList :: Value -> Parser [MapPortResponse] # | |
| Show MapPortResponse # | |
Defined in JSON Methods showsPrec :: Int -> MapPortResponse -> ShowS # show :: MapPortResponse -> String # showList :: [MapPortResponse] -> ShowS # | |
These types represent the core data that is communicated between requests and the runtime.
newtype IPAddressTable #
Warning: This newtype is highly unstable due to undergoing work on improving P2PRC's server api. For more information, visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114
This is a wrapper value that parses a json key value from the list of ip addresses in the network.
Constructors
| MkIPAddressTable | Wrapping constructor |
Fields
| |
| FromJSON IPAddressTable # | |
Defined in JSON Methods parseJSON :: Value -> Parser IPAddressTable # parseJSONList :: Value -> Parser [IPAddressTable] # | |
| Show IPAddressTable # | |
Defined in JSON Methods showsPrec :: Int -> IPAddressTable -> ShowS # show :: IPAddressTable -> String # showList :: [IPAddressTable] -> ShowS # | |
data ServerInfo #
Warning: This type is highly unstable due to undergoing work on improving P2PRC's server api. For more information, visit: https://github.com/Akilan1999/p2p-rendering-computation/issues/114
This is a record that keeps track of the current state of every node in the network. It is crucial information required for orchestration strategies.
Constructors
| MkServerInfo | |
Fields
| |
| FromJSON ServerInfo # | |
Defined in JSON | |
| Show ServerInfo # | |
Defined in JSON Methods showsPrec :: Int -> ServerInfo -> ShowS # show :: ServerInfo -> String # showList :: [ServerInfo] -> ShowS # | |
This is a simple representation of the IP address of nodes in the network.
Haskell-side Error value. This type is designed to parse and track System and P2PRC's error signals in a safe and effective manner.
It does have an MkUnknownError value which is meant to be warn about new kinds of error not yet accounted in this client. Github issues and pull requests are very welcome to improve error handling by parsing more types of errors.
Constructors
| MkCLISystemError | This is a CLI System Error |
| MkErrorSpawningProcess | Spawing process error |
Fields
| |
| MkUnknownError | This is an unparsed P2PRC's error |
Fields
| |
This section is reserved to some useful type synonyms that add significant ergonomics.
type IOEitherError a = IO (Either Error a) #
Type synonym for an IO action with either returns an Error or a parsed value