mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-17 13:27:30 +03:00
Merge branch 'trunk' into work/arrays
This commit is contained in:
commit
aef728992b
@ -68,3 +68,4 @@ The format for this list: name, GitHub handle
|
||||
* Harald Gliebe (@hagl)
|
||||
* Phil de Joux (@philderbeast)
|
||||
* Travis Staton (@tstat)
|
||||
* Dan Freeman (@dfreeman)
|
||||
|
56
codebase2/codebase-sqlite-hashing-v2/package.yaml
Normal file
56
codebase2/codebase-sqlite-hashing-v2/package.yaml
Normal file
@ -0,0 +1,56 @@
|
||||
name: unison-codebase-sqlite-hashing-v2
|
||||
github: unisonweb/unison
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
|
||||
ghc-options: -Wall
|
||||
|
||||
dependencies:
|
||||
- base
|
||||
- bytes
|
||||
- bytestring
|
||||
- containers
|
||||
- lens
|
||||
- text
|
||||
- unison-codebase
|
||||
- unison-codebase-sqlite
|
||||
- unison-core
|
||||
- unison-core1
|
||||
- unison-hashing-v2
|
||||
- unison-prelude
|
||||
- unison-sqlite
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
- unison-util-term
|
||||
- vector
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_codebase_sqlite_hashing_v2
|
||||
|
||||
default-extensions:
|
||||
- ApplicativeDo
|
||||
- BangPatterns
|
||||
- BlockArguments
|
||||
- DeriveAnyClass
|
||||
- DeriveFunctor
|
||||
- DeriveGeneric
|
||||
- DeriveTraversable
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
- LambdaCase
|
||||
- MultiParamTypeClasses
|
||||
- NamedFieldPuns
|
||||
- OverloadedStrings
|
||||
- PatternSynonyms
|
||||
- RecordWildCards
|
||||
- RankNTypes
|
||||
- ScopedTypeVariables
|
||||
- TupleSections
|
||||
- TypeApplications
|
||||
- ViewPatterns
|
@ -0,0 +1,24 @@
|
||||
module U.Codebase.Sqlite.V2.Decl
|
||||
( saveDeclComponent,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified U.Codebase.Decl as V2
|
||||
import U.Codebase.Sqlite.DbId (ObjectId)
|
||||
import qualified U.Codebase.Sqlite.Queries as U.Sqlite
|
||||
import U.Codebase.Sqlite.Symbol (Symbol)
|
||||
import U.Codebase.Sqlite.V2.HashHandle
|
||||
import U.Util.Hash (Hash)
|
||||
import Unison.Prelude
|
||||
import Unison.Sqlite
|
||||
|
||||
saveDeclComponent ::
|
||||
-- | The serialized decl component if we already have it e.g. via sync
|
||||
Maybe ByteString ->
|
||||
-- | decl component hash
|
||||
Hash ->
|
||||
-- | decl component
|
||||
[V2.Decl Symbol] ->
|
||||
Transaction ObjectId
|
||||
saveDeclComponent =
|
||||
U.Sqlite.saveDeclComponent v2HashHandle
|
@ -0,0 +1,19 @@
|
||||
module U.Codebase.Sqlite.V2.HashHandle
|
||||
( v2HashHandle,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified Data.Set as Set
|
||||
import U.Codebase.Sqlite.HashHandle
|
||||
import U.Util.Type (removeAllEffectVars)
|
||||
import Unison.Hashing.V2.Convert2 (h2ToV2Reference, v2ToH2Type, v2ToH2TypeD)
|
||||
import qualified Unison.Hashing.V2.Type as H2
|
||||
|
||||
v2HashHandle :: HashHandle
|
||||
v2HashHandle =
|
||||
HashHandle
|
||||
{ toReference = h2ToV2Reference . H2.toReference . v2ToH2Type . removeAllEffectVars,
|
||||
toReferenceMentions = Set.map h2ToV2Reference . H2.toReferenceMentions . v2ToH2Type . removeAllEffectVars,
|
||||
toReferenceDecl = \h -> h2ToV2Reference . H2.toReference . v2ToH2TypeD h . removeAllEffectVars,
|
||||
toReferenceDeclMentions = \h -> Set.map h2ToV2Reference . H2.toReferenceMentions . v2ToH2TypeD h . removeAllEffectVars
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
module U.Codebase.Sqlite.V2.SyncEntity where
|
||||
|
||||
import qualified U.Codebase.Sqlite.DbId as Db
|
||||
import U.Codebase.Sqlite.Entity (SyncEntity)
|
||||
import qualified U.Codebase.Sqlite.Queries as Q
|
||||
import U.Codebase.Sqlite.V2.HashHandle
|
||||
import U.Util.Hash32 (Hash32)
|
||||
import Unison.Sqlite
|
||||
|
||||
saveSyncEntity :: Hash32 -> SyncEntity -> Transaction (Either Db.CausalHashId Db.ObjectId)
|
||||
saveSyncEntity = Q.saveSyncEntity v2HashHandle
|
@ -0,0 +1,24 @@
|
||||
module U.Codebase.Sqlite.V2.Term
|
||||
( saveTermComponent,
|
||||
)
|
||||
where
|
||||
|
||||
import U.Codebase.Sqlite.DbId (ObjectId)
|
||||
import qualified U.Codebase.Sqlite.Queries as U.Sqlite
|
||||
import U.Codebase.Sqlite.Symbol (Symbol)
|
||||
import U.Codebase.Sqlite.V2.HashHandle
|
||||
import qualified U.Codebase.Term as V2
|
||||
import U.Util.Hash (Hash)
|
||||
import Unison.Prelude
|
||||
import Unison.Sqlite
|
||||
|
||||
saveTermComponent ::
|
||||
-- | The serialized term component if we already have it e.g. via sync
|
||||
Maybe ByteString ->
|
||||
-- | term component hash
|
||||
Hash ->
|
||||
-- | term component
|
||||
[(V2.Term Symbol, V2.Type Symbol)] ->
|
||||
Transaction ObjectId
|
||||
saveTermComponent =
|
||||
U.Sqlite.saveTermComponent v2HashHandle
|
@ -0,0 +1,142 @@
|
||||
-- | Description: Converts V2 types to the V2 hashing types
|
||||
module Unison.Hashing.V2.Convert2
|
||||
( v2ToH2Term,
|
||||
v2ToH2Type,
|
||||
v2ToH2TypeD,
|
||||
h2ToV2Reference,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified U.Codebase.Kind as V2
|
||||
import qualified U.Codebase.Reference as V2
|
||||
import qualified U.Codebase.Referent as V2.Referent
|
||||
import qualified U.Codebase.Term as V2 (F, F' (..), MatchCase (..), Pattern (..), SeqOp (..), TermRef, TypeRef)
|
||||
import qualified U.Codebase.Type as V2.Type
|
||||
import qualified U.Core.ABT as V2
|
||||
import qualified U.Core.ABT as V2.ABT
|
||||
import qualified U.Util.Hash as V2 (Hash)
|
||||
import qualified Unison.ABT as H2 (transform)
|
||||
import qualified Unison.ABT as V1.ABT
|
||||
import qualified Unison.Hashing.V2.Kind as H2
|
||||
import qualified Unison.Hashing.V2.Pattern as H2.Pattern
|
||||
import qualified Unison.Hashing.V2.Reference as H2
|
||||
import qualified Unison.Hashing.V2.Referent as H2.Referent
|
||||
import qualified Unison.Hashing.V2.Term as H2
|
||||
import qualified Unison.Hashing.V2.Type as H2.Type
|
||||
import Unison.Prelude
|
||||
|
||||
-- | Delete me ASAP. I am defined elsewhere.
|
||||
abt2to1 :: Functor f => V2.ABT.Term f v a -> V1.ABT.Term f v a
|
||||
abt2to1 (V2.ABT.Term fv a out) = V1.ABT.Term fv a (go out)
|
||||
where
|
||||
go = \case
|
||||
V2.ABT.Cycle body -> V1.ABT.Cycle (abt2to1 body)
|
||||
V2.ABT.Abs v body -> V1.ABT.Abs v (abt2to1 body)
|
||||
V2.ABT.Var v -> V1.ABT.Var v
|
||||
V2.ABT.Tm tm -> V1.ABT.Tm (abt2to1 <$> tm)
|
||||
|
||||
v2ToH2Term :: forall v. Ord v => V2.Hash -> V2.Term (V2.F v) v () -> H2.Term v ()
|
||||
v2ToH2Term thisTermComponentHash = H2.transform convertF . abt2to1
|
||||
where
|
||||
convertF :: forall x. V2.F v x -> H2.F v () () x
|
||||
convertF = \case
|
||||
V2.Int x -> H2.Int x
|
||||
V2.Nat x -> H2.Nat x
|
||||
V2.Float x -> H2.Float x
|
||||
V2.Boolean x -> H2.Boolean x
|
||||
V2.Text x -> H2.Text x
|
||||
V2.Char x -> H2.Char x
|
||||
V2.Ref x -> H2.Ref (convertTermRef thisTermComponentHash x)
|
||||
V2.Constructor a b -> H2.Constructor (convertReference a) b
|
||||
V2.Request a b -> H2.Request (convertReference a) b
|
||||
V2.Handle a b -> H2.Handle a b
|
||||
V2.App a b -> H2.App a b
|
||||
V2.Ann a b -> H2.Ann a (v2ToH2Type b)
|
||||
V2.List a -> H2.List a
|
||||
V2.If a b c -> H2.If a b c
|
||||
V2.And a b -> H2.And a b
|
||||
V2.Or a b -> H2.Or a b
|
||||
V2.Lam a -> H2.Lam a
|
||||
V2.LetRec a b -> H2.LetRec a b
|
||||
V2.Let a b -> H2.Let a b
|
||||
V2.Match a b -> H2.Match a (map convertMatchCase b)
|
||||
V2.TermLink a -> H2.TermLink (convertReferent thisTermComponentHash a)
|
||||
V2.TypeLink a -> H2.TypeLink (convertReference a)
|
||||
|
||||
convertMatchCase :: V2.MatchCase Text V2.TypeRef x -> H2.MatchCase () x
|
||||
convertMatchCase (V2.MatchCase pat a b) = H2.MatchCase (convertPattern pat) a b
|
||||
|
||||
convertPattern :: V2.Pattern Text V2.TypeRef -> H2.Pattern.Pattern ()
|
||||
convertPattern = \case
|
||||
V2.PUnbound -> H2.Pattern.Unbound ()
|
||||
V2.PVar -> H2.Pattern.Var ()
|
||||
V2.PBoolean a -> H2.Pattern.Boolean () a
|
||||
V2.PInt a -> H2.Pattern.Int () a
|
||||
V2.PNat a -> H2.Pattern.Nat () a
|
||||
V2.PFloat a -> H2.Pattern.Float () a
|
||||
V2.PText a -> H2.Pattern.Text () a
|
||||
V2.PChar a -> H2.Pattern.Char () a
|
||||
V2.PConstructor a b c -> H2.Pattern.Constructor () (convertReference a) b (map convertPattern c)
|
||||
V2.PAs a -> H2.Pattern.As () (convertPattern a)
|
||||
V2.PEffectPure a -> H2.Pattern.EffectPure () (convertPattern a)
|
||||
V2.PEffectBind a b c d -> H2.Pattern.EffectBind () (convertReference a) b (map convertPattern c) (convertPattern d)
|
||||
V2.PSequenceLiteral a -> H2.Pattern.SequenceLiteral () (map convertPattern a)
|
||||
V2.PSequenceOp a b c -> H2.Pattern.SequenceOp () (convertPattern a) (convertSeqOp b) (convertPattern c)
|
||||
where
|
||||
convertSeqOp = \case
|
||||
V2.PCons -> H2.Pattern.Cons
|
||||
V2.PSnoc -> H2.Pattern.Snoc
|
||||
V2.PConcat -> H2.Pattern.Concat
|
||||
|
||||
convertReferent ::
|
||||
V2.Hash ->
|
||||
V2.Referent.Referent' (V2.Reference' Text (Maybe V2.Hash)) (V2.Reference' Text V2.Hash) ->
|
||||
H2.Referent.Referent
|
||||
convertReferent defaultHash = \case
|
||||
V2.Referent.Ref x -> H2.Referent.Ref (convertTermRef defaultHash x)
|
||||
V2.Referent.Con x cid -> H2.Referent.Con (convertReference x) cid
|
||||
|
||||
convertId :: V2.Hash -> V2.Id' (Maybe V2.Hash) -> H2.Id
|
||||
convertId defaultHash = \case
|
||||
V2.Id m p -> H2.Id (fromMaybe defaultHash m) p
|
||||
|
||||
convertReference :: V2.Reference -> H2.Reference
|
||||
convertReference = convertReference' (\(V2.Id a b) -> H2.Id a b)
|
||||
|
||||
convertTermRef :: V2.Hash -> V2.TermRef -> H2.Reference
|
||||
convertTermRef = convertReference' . convertId
|
||||
|
||||
convertReference' :: (V2.Id' hash -> H2.Id) -> V2.Reference' Text hash -> H2.Reference
|
||||
convertReference' idConv = \case
|
||||
V2.ReferenceBuiltin x -> H2.Builtin x
|
||||
V2.ReferenceDerived x -> H2.DerivedId (idConv x)
|
||||
|
||||
v2ToH2Type :: forall v. Ord v => V2.Type.TypeR V2.TypeRef v -> H2.Type.Type v ()
|
||||
v2ToH2Type = v2ToH2Type' convertReference
|
||||
|
||||
v2ToH2TypeD :: forall v. Ord v => V2.Hash -> V2.Type.TypeD v -> H2.Type.Type v ()
|
||||
v2ToH2TypeD defaultHash = v2ToH2Type' (convertReference' (convertId defaultHash))
|
||||
|
||||
v2ToH2Type' :: forall r v. Ord v => (r -> H2.Reference) -> V2.Type.TypeR r v -> H2.Type.Type v ()
|
||||
v2ToH2Type' mkReference = H2.transform convertF . abt2to1
|
||||
where
|
||||
convertF :: forall a. V2.Type.F' r a -> H2.Type.F a
|
||||
convertF = \case
|
||||
V2.Type.Ref x -> H2.Type.Ref (mkReference x)
|
||||
V2.Type.Arrow a b -> H2.Type.Arrow a b
|
||||
V2.Type.Ann a k -> H2.Type.Ann a (convertKind k)
|
||||
V2.Type.App a b -> H2.Type.App a b
|
||||
V2.Type.Effect a b -> H2.Type.Effect a b
|
||||
V2.Type.Effects a -> H2.Type.Effects a
|
||||
V2.Type.Forall a -> H2.Type.Forall a
|
||||
V2.Type.IntroOuter a -> H2.Type.IntroOuter a
|
||||
|
||||
convertKind :: V2.Kind -> H2.Kind
|
||||
convertKind = \case
|
||||
V2.Star -> H2.Star
|
||||
V2.Arrow a b -> H2.Arrow (convertKind a) (convertKind b)
|
||||
|
||||
h2ToV2Reference :: H2.Reference -> V2.Reference
|
||||
h2ToV2Reference = \case
|
||||
H2.Builtin txt -> V2.ReferenceBuiltin txt
|
||||
H2.DerivedId (H2.Id x y) -> V2.ReferenceDerived (V2.Id x y)
|
@ -0,0 +1,71 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.7.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: unison-codebase-sqlite-hashing-v2
|
||||
version: 0.0.0
|
||||
homepage: https://github.com/unisonweb/unison#readme
|
||||
bug-reports: https://github.com/unisonweb/unison/issues
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
build-type: Simple
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/unisonweb/unison
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
U.Codebase.Sqlite.V2.Decl
|
||||
U.Codebase.Sqlite.V2.HashHandle
|
||||
U.Codebase.Sqlite.V2.SyncEntity
|
||||
U.Codebase.Sqlite.V2.Term
|
||||
Unison.Hashing.V2.Convert2
|
||||
hs-source-dirs:
|
||||
src
|
||||
default-extensions:
|
||||
ApplicativeDo
|
||||
BangPatterns
|
||||
BlockArguments
|
||||
DeriveAnyClass
|
||||
DeriveFunctor
|
||||
DeriveGeneric
|
||||
DeriveTraversable
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
LambdaCase
|
||||
MultiParamTypeClasses
|
||||
NamedFieldPuns
|
||||
OverloadedStrings
|
||||
PatternSynonyms
|
||||
RecordWildCards
|
||||
RankNTypes
|
||||
ScopedTypeVariables
|
||||
TupleSections
|
||||
TypeApplications
|
||||
ViewPatterns
|
||||
ghc-options: -Wall
|
||||
build-depends:
|
||||
base
|
||||
, bytes
|
||||
, bytestring
|
||||
, containers
|
||||
, lens
|
||||
, text
|
||||
, unison-codebase
|
||||
, unison-codebase-sqlite
|
||||
, unison-core
|
||||
, unison-core1
|
||||
, unison-hashing-v2
|
||||
, unison-prelude
|
||||
, unison-sqlite
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
, unison-util-term
|
||||
, vector
|
||||
default-language: Haskell2010
|
@ -1,7 +1,9 @@
|
||||
module U.Codebase.Sqlite.Branch.Format
|
||||
( BranchFormat (..),
|
||||
BranchLocalIds (..),
|
||||
SyncBranchFormat (..),
|
||||
BranchLocalIds,
|
||||
BranchLocalIds' (..),
|
||||
SyncBranchFormat,
|
||||
SyncBranchFormat' (..),
|
||||
localToDbBranch,
|
||||
localToDbDiff,
|
||||
-- dbToLocalDiff,
|
||||
@ -35,17 +37,52 @@ data BranchFormat
|
||||
--
|
||||
-- For example, a @branchTextLookup@ vector of @[50, 74]@ means "local id 0 corresponds to database text id 50, and
|
||||
-- local id 1 corresponds to database text id 74".
|
||||
data BranchLocalIds = LocalIds
|
||||
{ branchTextLookup :: Vector TextId,
|
||||
branchDefnLookup :: Vector ObjectId,
|
||||
branchPatchLookup :: Vector PatchObjectId,
|
||||
branchChildLookup :: Vector (BranchObjectId, CausalHashId)
|
||||
type BranchLocalIds = BranchLocalIds' TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)
|
||||
|
||||
-- temp_entity
|
||||
-- branch #foo
|
||||
--
|
||||
-- temp_entity_missing_dependency
|
||||
-- #foo depends on causal #bar
|
||||
-- #foo depends on namespace #baz
|
||||
--
|
||||
-- 1. store causal #bar, go to flush dependencies like normal
|
||||
-- 2. ... oh this case is different than the others - we don't want to delete that row
|
||||
|
||||
----
|
||||
-- can't simply treat causal's value hash as a mandatory dependency because we can't be sure
|
||||
-- that the causal doesn't already exist in the target codebase without the value.
|
||||
-- it probably does exist together with the value (though we found cases in the past where it didn't
|
||||
-- due to race conditions, but we fixed that and added transactions and it shouldn't happen again?),
|
||||
-- but it's not enforced at the schema level.
|
||||
-- to enforce it at the schema level, we'd have to do something like store namespace_object_id instead
|
||||
-- of value_hash in causal, which would require a db migration for a thing we don't necessarily even want
|
||||
-- long term.
|
||||
-- so, we can't simply "require" the value hash as a dependency of the causals and expect things to work smoothly
|
||||
-- without relying on prayer.
|
||||
--
|
||||
-- temp_entity
|
||||
-- branch #foo
|
||||
-- causal #bar
|
||||
|
||||
-- temp_entity_missing_dependency
|
||||
-- #foo depends on causal #bar
|
||||
-- #bar depends on namespace #baz
|
||||
--
|
||||
|
||||
data BranchLocalIds' t d p c = LocalIds
|
||||
{ branchTextLookup :: Vector t,
|
||||
branchDefnLookup :: Vector d,
|
||||
branchPatchLookup :: Vector p,
|
||||
branchChildLookup :: Vector c
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
data SyncBranchFormat
|
||||
= SyncFull BranchLocalIds ByteString
|
||||
| SyncDiff BranchObjectId BranchLocalIds ByteString
|
||||
data SyncBranchFormat' parent text defn patch child
|
||||
= SyncFull (BranchLocalIds' text defn patch child) ByteString
|
||||
| SyncDiff parent (BranchLocalIds' text defn patch child) ByteString
|
||||
|
||||
type SyncBranchFormat = SyncBranchFormat' BranchObjectId TextId ObjectId PatchObjectId (BranchObjectId, CausalHashId)
|
||||
|
||||
localToDbBranch :: BranchLocalIds -> LocalBranch -> DbBranch
|
||||
localToDbBranch li =
|
||||
|
@ -1,7 +1,12 @@
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
module U.Codebase.Sqlite.Causal where
|
||||
module U.Codebase.Sqlite.Causal
|
||||
( DbCausal,
|
||||
GDbCausal (..),
|
||||
SyncCausalFormat,
|
||||
SyncCausalFormat' (..),
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Vector (Vector)
|
||||
import U.Codebase.Sqlite.DbId (BranchHashId, CausalHashId)
|
||||
import Unison.Prelude
|
||||
|
||||
@ -11,40 +16,11 @@ data GDbCausal causalHash valueHash = DbCausal
|
||||
parents :: Set causalHash
|
||||
}
|
||||
|
||||
-- Causal Plan
|
||||
|
||||
-- * Load a DbCausal (how do we do this)
|
||||
|
||||
-- => new function Queries.localCausalByCausalHashId, can model after loadCausalByCausalHash or factor out of
|
||||
|
||||
-- * Add valueHashId as a dependency if unmigrated
|
||||
|
||||
-- * Add parent causal hash ids as dependencies if unmigrated
|
||||
|
||||
-- => Queries.loadCausalParents
|
||||
|
||||
-- * Map over Branch hash IDs
|
||||
|
||||
-- * Inside saveDBCausal (new / factored out of original)
|
||||
|
||||
-- * Save as a new self-hash
|
||||
-- ==> Queries.saveCausal
|
||||
-- * Map over parent causal hash IDs
|
||||
-- ==> Queries.saveCausalParents
|
||||
|
||||
type DbCausal = GDbCausal CausalHashId BranchHashId
|
||||
|
||||
-- causalHashes_ :: Traversal (GDbCausal ch vh) (GDbCausal ch' vh) ch ch'
|
||||
-- causalHashes_ f DbCausal {..} =
|
||||
-- DbCausal <$> f selfHash <*> pure valueHash <*> (fmap Set.fromList . traverse f . Set.toList $ parents)
|
||||
data SyncCausalFormat' causalHash valueHash = SyncCausalFormat
|
||||
{ valueHash :: valueHash,
|
||||
parents :: Vector causalHash
|
||||
}
|
||||
|
||||
-- valueHashes_ :: Lens (GDbCausal ch vh) (GDbCausal ch vh) vh vh'
|
||||
-- valueHashes_ f DbCausal{..} =
|
||||
-- (\p vh -> DbCausal selfHash vh p) parents <$> f valueHash
|
||||
|
||||
-- data Causal m hc he e = Causal
|
||||
-- { causalHash :: hc,
|
||||
-- valueHash :: he,
|
||||
-- parents :: Map hc (m (Causal m hc he e)),
|
||||
-- value :: m e
|
||||
-- }
|
||||
type SyncCausalFormat = SyncCausalFormat' CausalHashId BranchHashId
|
||||
|
@ -5,10 +5,12 @@ module U.Codebase.Sqlite.Decl.Format where
|
||||
import Data.Vector (Vector)
|
||||
import U.Codebase.Decl (DeclR)
|
||||
import U.Codebase.Reference (Reference')
|
||||
import U.Codebase.Sqlite.LocalIds (LocalDefnId, LocalIds, LocalTextId)
|
||||
import U.Codebase.Sqlite.DbId (ObjectId, TextId)
|
||||
import U.Codebase.Sqlite.LocalIds (LocalDefnId, LocalIds', LocalTextId)
|
||||
import U.Codebase.Sqlite.Symbol (Symbol)
|
||||
import qualified U.Codebase.Type as Type
|
||||
import qualified U.Core.ABT as ABT
|
||||
import Unison.Prelude
|
||||
|
||||
-- | Add new formats here
|
||||
data DeclFormat = Decl LocallyIndexedComponent
|
||||
@ -16,10 +18,22 @@ data DeclFormat = Decl LocallyIndexedComponent
|
||||
|
||||
-- | V1: Decls included `Hash`es inline
|
||||
-- V2: Instead of `Hash`, we use a smaller index.
|
||||
newtype LocallyIndexedComponent
|
||||
= LocallyIndexedComponent (Vector (LocalIds, Decl Symbol))
|
||||
type LocallyIndexedComponent =
|
||||
LocallyIndexedComponent' TextId ObjectId
|
||||
|
||||
newtype LocallyIndexedComponent' t d = LocallyIndexedComponent
|
||||
{unLocallyIndexedComponent :: Vector (LocalIds' t d, Decl Symbol)}
|
||||
deriving (Show)
|
||||
|
||||
type SyncDeclFormat =
|
||||
SyncDeclFormat' TextId ObjectId
|
||||
|
||||
data SyncDeclFormat' t d
|
||||
= SyncDecl (SyncLocallyIndexedComponent' t d)
|
||||
|
||||
newtype SyncLocallyIndexedComponent' t d
|
||||
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
|
||||
|
||||
-- [OldDecl] ==map==> [NewDecl] ==number==> [(NewDecl, Int)] ==sort==> [(NewDecl, Int)] ==> permutation is map snd of that
|
||||
|
||||
-- type List a = Nil | Cons (List a)
|
||||
|
188
codebase2/codebase-sqlite/U/Codebase/Sqlite/Decode.hs
Normal file
188
codebase2/codebase-sqlite/U/Codebase/Sqlite/Decode.hs
Normal file
@ -0,0 +1,188 @@
|
||||
-- | This module contains decoders for blobs stored in SQLite.
|
||||
module U.Codebase.Sqlite.Decode
|
||||
( DecodeError,
|
||||
|
||||
-- * @object.bytes@
|
||||
decodeBranchFormat,
|
||||
decodeComponentLengthOnly,
|
||||
decodeDeclElement,
|
||||
decodeDeclFormat,
|
||||
decodePatchFormat,
|
||||
decodeSyncDeclFormat,
|
||||
decodeSyncNamespaceFormat,
|
||||
decodeSyncPatchFormat,
|
||||
decodeSyncTermFormat,
|
||||
decodeSyncTermAndType,
|
||||
decodeTermElementDiscardingTerm,
|
||||
decodeTermElementDiscardingType,
|
||||
decodeTermElementWithType,
|
||||
decodeTermFormat,
|
||||
|
||||
-- * @temp_entity.blob@
|
||||
decodeTempCausalFormat,
|
||||
decodeTempDeclFormat,
|
||||
decodeTempNamespaceFormat,
|
||||
decodeTempPatchFormat,
|
||||
decodeTempTermFormat,
|
||||
|
||||
-- * @watch_result.result@
|
||||
decodeWatchResultFormat,
|
||||
|
||||
-- * unsyncs
|
||||
unsyncTermComponent,
|
||||
unsyncDeclComponent,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Exception (throwIO)
|
||||
import Data.Bytes.Get (runGetS)
|
||||
import qualified Data.Bytes.Get as Get
|
||||
import qualified U.Codebase.Reference as C.Reference
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as NamespaceFormat
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as DeclFormat
|
||||
import U.Codebase.Sqlite.LocalIds (LocalIds)
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as PatchFormat
|
||||
import U.Codebase.Sqlite.Serialization as Serialization
|
||||
import U.Codebase.Sqlite.Symbol (Symbol)
|
||||
import qualified U.Codebase.Sqlite.TempEntity as TempEntity
|
||||
import qualified U.Codebase.Sqlite.Term.Format as TermFormat
|
||||
import U.Util.Serialization (Get)
|
||||
import qualified U.Util.Serialization as Serialization (lengthFramedArray)
|
||||
import Unison.Prelude
|
||||
import Unison.Sqlite
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Decode error
|
||||
|
||||
data DecodeError = DecodeError
|
||||
{ decoder :: Text, -- the name of the decoder
|
||||
err :: String -- the error message
|
||||
}
|
||||
deriving stock (Show)
|
||||
deriving anyclass (SqliteExceptionReason, Exception)
|
||||
|
||||
getFromBytesOr :: Text -> Get a -> ByteString -> Either DecodeError a
|
||||
getFromBytesOr decoder get bs = case runGetS get bs of
|
||||
Left err -> Left (DecodeError decoder err)
|
||||
Right a -> Right a
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- object.bytes
|
||||
|
||||
decodeBranchFormat :: ByteString -> Either DecodeError NamespaceFormat.BranchFormat
|
||||
decodeBranchFormat =
|
||||
getFromBytesOr "getBranchFormat" Serialization.getBranchFormat
|
||||
|
||||
decodeComponentLengthOnly :: ByteString -> Either DecodeError Word64
|
||||
decodeComponentLengthOnly =
|
||||
getFromBytesOr "lengthFramedArray" (Get.skip 1 >> Serialization.lengthFramedArray)
|
||||
|
||||
decodeDeclElement :: Word64 -> ByteString -> Either DecodeError (LocalIds, DeclFormat.Decl Symbol)
|
||||
decodeDeclElement i =
|
||||
getFromBytesOr ("lookupDeclElement " <> tShow i) (Serialization.lookupDeclElement i)
|
||||
|
||||
decodeDeclFormat :: ByteString -> Either DecodeError DeclFormat.DeclFormat
|
||||
decodeDeclFormat =
|
||||
getFromBytesOr "getDeclFormat" Serialization.getDeclFormat
|
||||
|
||||
decodePatchFormat :: ByteString -> Either DecodeError PatchFormat.PatchFormat
|
||||
decodePatchFormat =
|
||||
getFromBytesOr "getPatchFormat" Serialization.getPatchFormat
|
||||
|
||||
decodeSyncDeclFormat :: ByteString -> Either DecodeError DeclFormat.SyncDeclFormat
|
||||
decodeSyncDeclFormat =
|
||||
getFromBytesOr "decomposeDeclFormat" Serialization.decomposeDeclFormat
|
||||
|
||||
decodeSyncNamespaceFormat :: ByteString -> Either DecodeError NamespaceFormat.SyncBranchFormat
|
||||
decodeSyncNamespaceFormat =
|
||||
getFromBytesOr "decomposeNamespaceFormat" Serialization.decomposeBranchFormat
|
||||
|
||||
decodeSyncPatchFormat :: ByteString -> Either DecodeError PatchFormat.SyncPatchFormat
|
||||
decodeSyncPatchFormat =
|
||||
getFromBytesOr "decomposePatchFormat" Serialization.decomposePatchFormat
|
||||
|
||||
decodeSyncTermFormat :: ByteString -> Either DecodeError TermFormat.SyncTermFormat
|
||||
decodeSyncTermFormat =
|
||||
getFromBytesOr "decomposeTermFormat" Serialization.decomposeTermFormat
|
||||
|
||||
-- | N.B. The bytestring here is not the entire object.bytes column --
|
||||
-- it's just the serialized term and type from 'TermFormat.SyncTermFormat'.
|
||||
decodeSyncTermAndType :: ByteString -> Either DecodeError (TermFormat.Term, TermFormat.Type)
|
||||
decodeSyncTermAndType =
|
||||
getFromBytesOr "getTermAndType" Serialization.getTermAndType
|
||||
|
||||
-- | N.B. The bytestring here is not the entire object.bytes column --
|
||||
-- it's just the serialized decl from 'DeclFormat.SyncDeclFormat'.
|
||||
decodeDecl :: ByteString -> Either DecodeError (DeclFormat.Decl Symbol)
|
||||
decodeDecl =
|
||||
getFromBytesOr "getDeclElement" Serialization.getDeclElement
|
||||
|
||||
decodeTermFormat :: ByteString -> Either DecodeError TermFormat.TermFormat
|
||||
decodeTermFormat =
|
||||
getFromBytesOr "getTermFormat" Serialization.getTermFormat
|
||||
|
||||
decodeTermElementDiscardingTerm :: C.Reference.Pos -> ByteString -> Either DecodeError (LocalIds, TermFormat.Type)
|
||||
decodeTermElementDiscardingTerm i =
|
||||
getFromBytesOr ("lookupTermElementDiscardingTerm " <> tShow i) (Serialization.lookupTermElementDiscardingTerm i)
|
||||
|
||||
decodeTermElementDiscardingType :: C.Reference.Pos -> ByteString -> Either DecodeError (LocalIds, TermFormat.Term)
|
||||
decodeTermElementDiscardingType i =
|
||||
getFromBytesOr ("lookupTermElementDiscardingType " <> tShow i) (Serialization.lookupTermElementDiscardingType i)
|
||||
|
||||
decodeTermElementWithType ::
|
||||
C.Reference.Pos ->
|
||||
ByteString ->
|
||||
Either DecodeError (LocalIds, TermFormat.Term, TermFormat.Type)
|
||||
decodeTermElementWithType i =
|
||||
getFromBytesOr ("lookupTermElement" <> tShow i) (Serialization.lookupTermElement i)
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- temp_entity.blob
|
||||
|
||||
decodeTempCausalFormat :: ByteString -> Either DecodeError TempEntity.TempCausalFormat
|
||||
decodeTempCausalFormat =
|
||||
getFromBytesOr "getTempCausalFormat" Serialization.getTempCausalFormat
|
||||
|
||||
decodeTempDeclFormat :: ByteString -> Either DecodeError TempEntity.TempDeclFormat
|
||||
decodeTempDeclFormat =
|
||||
getFromBytesOr "getTempDeclFormat" Serialization.getTempDeclFormat
|
||||
|
||||
decodeTempNamespaceFormat :: ByteString -> Either DecodeError TempEntity.TempNamespaceFormat
|
||||
decodeTempNamespaceFormat =
|
||||
getFromBytesOr "getTempNamespaceFormat" Serialization.getTempNamespaceFormat
|
||||
|
||||
decodeTempPatchFormat :: ByteString -> Either DecodeError TempEntity.TempPatchFormat
|
||||
decodeTempPatchFormat =
|
||||
getFromBytesOr "getTempPatchFormat" Serialization.getTempPatchFormat
|
||||
|
||||
decodeTempTermFormat :: ByteString -> Either DecodeError TempEntity.TempTermFormat
|
||||
decodeTempTermFormat =
|
||||
getFromBytesOr "getTempTermFormat" Serialization.getTempTermFormat
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- watch_result.result
|
||||
|
||||
decodeWatchResultFormat :: ByteString -> Either DecodeError TermFormat.WatchResultFormat
|
||||
decodeWatchResultFormat =
|
||||
getFromBytesOr "getWatchResultFormat" Serialization.getWatchResultFormat
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- unsyncs
|
||||
|
||||
unsyncTermComponent :: TermFormat.SyncLocallyIndexedComponent' t d -> IO (TermFormat.LocallyIndexedComponent' t d)
|
||||
unsyncTermComponent (TermFormat.SyncLocallyIndexedComponent terms) = do
|
||||
let phi (localIds, bs) = do
|
||||
(a, b) <- decodeSyncTermAndType bs
|
||||
pure (localIds, a, b)
|
||||
case traverse phi terms of
|
||||
Left err -> throwIO err
|
||||
Right x -> pure (TermFormat.LocallyIndexedComponent x)
|
||||
|
||||
unsyncDeclComponent :: DeclFormat.SyncLocallyIndexedComponent' t d -> IO (DeclFormat.LocallyIndexedComponent' t d)
|
||||
unsyncDeclComponent (DeclFormat.SyncLocallyIndexedComponent decls) = do
|
||||
let phi (localIds, bs) = do
|
||||
decl <- decodeDecl bs
|
||||
pure (localIds, decl)
|
||||
case traverse phi decls of
|
||||
Left err -> throwIO err
|
||||
Right x -> pure (DeclFormat.LocallyIndexedComponent x)
|
34
codebase2/codebase-sqlite/U/Codebase/Sqlite/Entity.hs
Normal file
34
codebase2/codebase-sqlite/U/Codebase/Sqlite/Entity.hs
Normal file
@ -0,0 +1,34 @@
|
||||
module U.Codebase.Sqlite.Entity where
|
||||
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as Namespace
|
||||
import qualified U.Codebase.Sqlite.Causal as Causal
|
||||
import U.Codebase.Sqlite.DbId (BranchHashId, BranchObjectId, CausalHashId, HashId, ObjectId, PatchObjectId, TextId)
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as Decl
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as Patch
|
||||
import U.Codebase.Sqlite.TempEntityType (TempEntityType (..))
|
||||
import qualified U.Codebase.Sqlite.Term.Format as Term
|
||||
|
||||
-- |
|
||||
-- data SyncEntity
|
||||
-- = TC SyncTermFormat
|
||||
-- | DC SyncDeclFormat
|
||||
-- | N SyncBranchFormat
|
||||
-- | P SyncPatchFormat
|
||||
-- | C SyncCausalFormat
|
||||
type SyncEntity =
|
||||
SyncEntity' TextId HashId ObjectId PatchObjectId BranchHashId BranchObjectId CausalHashId
|
||||
|
||||
data SyncEntity' text hash defn patch branchh branch causal
|
||||
= TC (Term.SyncTermFormat' text defn)
|
||||
| DC (Decl.SyncDeclFormat' text defn)
|
||||
| N (Namespace.SyncBranchFormat' branch text defn patch (branch, causal))
|
||||
| P (Patch.SyncPatchFormat' patch text hash defn)
|
||||
| C (Causal.SyncCausalFormat' causal branchh)
|
||||
|
||||
entityType :: SyncEntity' text hash defn patch branchh branch causal -> TempEntityType
|
||||
entityType = \case
|
||||
TC _ -> TermComponentType
|
||||
DC _ -> DeclComponentType
|
||||
N _ -> NamespaceType
|
||||
P _ -> PatchType
|
||||
C _ -> CausalType
|
22
codebase2/codebase-sqlite/U/Codebase/Sqlite/HashHandle.hs
Normal file
22
codebase2/codebase-sqlite/U/Codebase/Sqlite/HashHandle.hs
Normal file
@ -0,0 +1,22 @@
|
||||
module U.Codebase.Sqlite.HashHandle
|
||||
( HashHandle (..),
|
||||
)
|
||||
where
|
||||
|
||||
import qualified U.Codebase.Reference as C
|
||||
import U.Codebase.Sqlite.Symbol (Symbol)
|
||||
import qualified U.Codebase.Term as C.Term
|
||||
import qualified U.Codebase.Type as C.Type
|
||||
import U.Util.Hash (Hash)
|
||||
import Unison.Prelude
|
||||
|
||||
data HashHandle = HashHandle
|
||||
{ -- | Hash type
|
||||
toReference :: C.Term.Type Symbol -> C.Reference,
|
||||
-- | Hash type's mentions
|
||||
toReferenceMentions :: C.Term.Type Symbol -> Set C.Reference,
|
||||
-- | Hash the type of a single constructor in a decl component. The provided hash argument is the hash of the decl component.
|
||||
toReferenceDecl :: Hash -> C.Type.TypeD Symbol -> C.Reference,
|
||||
-- | Hash decl's mentions
|
||||
toReferenceDeclMentions :: Hash -> C.Type.TypeD Symbol -> Set C.Reference
|
||||
}
|
@ -4,18 +4,20 @@ module U.Codebase.Sqlite.Operations
|
||||
loadRootCausalHash,
|
||||
expectRootCausalHash,
|
||||
expectRootCausal,
|
||||
loadCausalHashAtPath,
|
||||
expectCausalHashAtPath,
|
||||
saveBranch,
|
||||
loadCausalBranchByCausalHash,
|
||||
expectCausalBranchByCausalHash,
|
||||
|
||||
-- * terms
|
||||
saveTermComponent,
|
||||
Q.saveTermComponent,
|
||||
loadTermComponent,
|
||||
loadTermByReference,
|
||||
loadTypeOfTermByTermReference,
|
||||
|
||||
-- * decls
|
||||
saveDeclComponent,
|
||||
Q.saveDeclComponent,
|
||||
loadDeclComponent,
|
||||
loadDeclByReference,
|
||||
expectDeclTypeById,
|
||||
@ -53,11 +55,11 @@ module U.Codebase.Sqlite.Operations
|
||||
dependentsOfComponent,
|
||||
|
||||
-- ** type index
|
||||
addTypeToIndexForTerm,
|
||||
Q.addTypeToIndexForTerm,
|
||||
termsHavingType,
|
||||
|
||||
-- ** type mentions index
|
||||
addTypeMentionsToIndexForTerm,
|
||||
Q.addTypeMentionsToIndexForTerm,
|
||||
termsMentioningType,
|
||||
|
||||
-- ** name lookup index
|
||||
@ -66,9 +68,11 @@ module U.Codebase.Sqlite.Operations
|
||||
|
||||
-- * low-level stuff
|
||||
expectDbBranch,
|
||||
saveDbBranch,
|
||||
saveDbBranchUnderHashId,
|
||||
expectDbPatch,
|
||||
saveBranchObject,
|
||||
saveDbPatch,
|
||||
expectDbBranchByCausalHashId,
|
||||
|
||||
-- * somewhat unexpectedly unused definitions
|
||||
c2sReferenceId,
|
||||
@ -76,31 +80,23 @@ module U.Codebase.Sqlite.Operations
|
||||
diffPatch,
|
||||
decodeTermElementWithType,
|
||||
loadTermWithTypeByReference,
|
||||
s2cTermWithType,
|
||||
Q.s2cTermWithType,
|
||||
Q.s2cDecl,
|
||||
declReferencesByPrefix,
|
||||
branchHashesByPrefix,
|
||||
derivedDependencies,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Lens (Lens')
|
||||
import qualified Control.Lens as Lens
|
||||
import qualified Control.Monad.Extra as Monad
|
||||
import Control.Monad.State (MonadState, evalStateT)
|
||||
import Control.Monad.Writer (MonadWriter, runWriterT)
|
||||
import qualified Control.Monad.Writer as Writer
|
||||
import Data.Bifunctor (Bifunctor (bimap))
|
||||
import Data.Bitraversable (Bitraversable (bitraverse))
|
||||
import Data.Bytes.Get (runGetS)
|
||||
import qualified Data.Bytes.Get as Get
|
||||
import qualified Data.Foldable as Foldable
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Map.Merge.Lazy as Map
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Text as Text
|
||||
import Data.Tuple.Extra (uncurry3, (***))
|
||||
import qualified Data.Vector as Vector
|
||||
import qualified U.Codebase.Branch as C.Branch
|
||||
import qualified U.Codebase.Causal as C
|
||||
import U.Codebase.Decl (ConstructorId)
|
||||
@ -115,7 +111,6 @@ import U.Codebase.ShortHash (ShortBranchHash (ShortBranchHash))
|
||||
import qualified U.Codebase.Sqlite.Branch.Diff as S.Branch
|
||||
import qualified U.Codebase.Sqlite.Branch.Diff as S.Branch.Diff
|
||||
import qualified U.Codebase.Sqlite.Branch.Diff as S.BranchDiff
|
||||
import U.Codebase.Sqlite.Branch.Format (BranchLocalIds)
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as S
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as S.BranchFormat
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S
|
||||
@ -123,17 +118,15 @@ import qualified U.Codebase.Sqlite.Branch.Full as S.Branch.Full
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S.MetadataSet
|
||||
import qualified U.Codebase.Sqlite.DbId as Db
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as S.Decl
|
||||
import U.Codebase.Sqlite.Decode
|
||||
import U.Codebase.Sqlite.HashHandle (HashHandle (..))
|
||||
import U.Codebase.Sqlite.LocalIds
|
||||
( LocalDefnId (..),
|
||||
LocalIds,
|
||||
LocalIds' (..),
|
||||
LocalTextId (..),
|
||||
( LocalIds,
|
||||
WatchLocalIds,
|
||||
)
|
||||
import qualified U.Codebase.Sqlite.LocalIds as LocalIds
|
||||
import qualified U.Codebase.Sqlite.LocalizeObject as LocalizeObject
|
||||
import qualified U.Codebase.Sqlite.NamedRef as S
|
||||
import qualified U.Codebase.Sqlite.ObjectType as OT
|
||||
import qualified U.Codebase.Sqlite.ObjectType as ObjectType
|
||||
import qualified U.Codebase.Sqlite.Patch.Diff as S
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as S
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as S.Patch.Format
|
||||
@ -154,17 +147,13 @@ import qualified U.Codebase.Term as C
|
||||
import qualified U.Codebase.Term as C.Term
|
||||
import qualified U.Codebase.TermEdit as C
|
||||
import qualified U.Codebase.TermEdit as C.TermEdit
|
||||
import qualified U.Codebase.Type as C.Type
|
||||
import qualified U.Codebase.TypeEdit as C
|
||||
import qualified U.Codebase.TypeEdit as C.TypeEdit
|
||||
import U.Codebase.WatchKind (WatchKind)
|
||||
import qualified U.Core.ABT as ABT
|
||||
import qualified U.Util.Base32Hex as Base32Hex
|
||||
import qualified U.Util.Hash as H
|
||||
import qualified U.Util.Lens as Lens
|
||||
import U.Util.Serialization (Get)
|
||||
import qualified U.Util.Hash32 as Hash32
|
||||
import qualified U.Util.Serialization as S
|
||||
import qualified U.Util.Term as TermUtil
|
||||
import Unison.Prelude
|
||||
import Unison.Sqlite
|
||||
import qualified Unison.Util.Map as Map
|
||||
@ -175,29 +164,17 @@ import qualified Unison.Util.Set as Set
|
||||
debug :: Bool
|
||||
debug = False
|
||||
|
||||
data DecodeError = DecodeError
|
||||
{ decoder :: Text, -- the name of the decoder
|
||||
err :: String -- the error message
|
||||
}
|
||||
deriving stock (Show)
|
||||
deriving anyclass (SqliteExceptionReason)
|
||||
|
||||
newtype NeedTypeForBuiltinMetadata
|
||||
= NeedTypeForBuiltinMetadata Text
|
||||
deriving stock (Show)
|
||||
deriving anyclass (SqliteExceptionReason)
|
||||
|
||||
getFromBytesOr :: Text -> Get a -> ByteString -> Either DecodeError a
|
||||
getFromBytesOr decoder get bs = case runGetS get bs of
|
||||
Left err -> Left (DecodeError decoder err)
|
||||
Right a -> Right a
|
||||
|
||||
-- * Database lookups
|
||||
|
||||
objectExistsForHash :: H.Hash -> Transaction Bool
|
||||
objectExistsForHash h =
|
||||
isJust <$> runMaybeT do
|
||||
id <- MaybeT . Q.loadHashId . H.toBase32Hex $ h
|
||||
id <- MaybeT . Q.loadHashId . Hash32.fromHash $ h
|
||||
MaybeT $ Q.loadObjectIdForAnyHashId id
|
||||
|
||||
expectValueHashByCausalHashId :: Db.CausalHashId -> Transaction BranchHash
|
||||
@ -214,6 +191,40 @@ loadRootCausalHash =
|
||||
runMaybeT $
|
||||
lift . Q.expectCausalHash =<< MaybeT Q.loadNamespaceRoot
|
||||
|
||||
-- | Load the causal hash at the given path from the root.
|
||||
--
|
||||
-- FIXME should we move some Path type here?
|
||||
loadCausalHashAtPath :: [Text] -> Transaction (Maybe CausalHash)
|
||||
loadCausalHashAtPath =
|
||||
let go :: Db.CausalHashId -> [Text] -> MaybeT Transaction CausalHash
|
||||
go hashId = \case
|
||||
[] -> lift (Q.expectCausalHash hashId)
|
||||
t : ts -> do
|
||||
tid <- MaybeT (Q.loadTextId t)
|
||||
S.Branch {children} <- MaybeT (loadDbBranchByCausalHashId hashId)
|
||||
(_, hashId') <- MaybeT (pure (Map.lookup tid children))
|
||||
go hashId' ts
|
||||
in \path -> do
|
||||
hashId <- Q.expectNamespaceRoot
|
||||
runMaybeT (go hashId path)
|
||||
|
||||
-- | Expect the causal hash at the given path from the root.
|
||||
--
|
||||
-- FIXME should we move some Path type here?
|
||||
expectCausalHashAtPath :: [Text] -> Transaction CausalHash
|
||||
expectCausalHashAtPath =
|
||||
let go :: Db.CausalHashId -> [Text] -> Transaction CausalHash
|
||||
go hashId = \case
|
||||
[] -> Q.expectCausalHash hashId
|
||||
t : ts -> do
|
||||
tid <- Q.expectTextId t
|
||||
S.Branch {children} <- expectDbBranchByCausalHashId hashId
|
||||
let (_, hashId') = children Map.! tid
|
||||
go hashId' ts
|
||||
in \path -> do
|
||||
hashId <- Q.expectNamespaceRoot
|
||||
go hashId path
|
||||
|
||||
-- * Reference transformations
|
||||
|
||||
-- ** read existing references
|
||||
@ -281,12 +292,8 @@ h2cReferent = bitraverse h2cReference h2cReference
|
||||
|
||||
-- ** convert and save references
|
||||
|
||||
-- | Save the text and hash parts of a Reference to the database and substitute their ids.
|
||||
saveReferenceH :: C.Reference -> Transaction S.ReferenceH
|
||||
saveReferenceH = bitraverse Q.saveText Q.saveHashHash
|
||||
|
||||
saveReferentH :: C.Referent -> Transaction S.ReferentH
|
||||
saveReferentH = bitraverse saveReferenceH saveReferenceH
|
||||
saveReferentH = bitraverse Q.saveReferenceH Q.saveReferenceH
|
||||
|
||||
-- ** Edits transformations
|
||||
|
||||
@ -317,7 +324,7 @@ c2sPatch :: C.Branch.Patch -> Transaction S.Patch
|
||||
c2sPatch (C.Branch.Patch termEdits typeEdits) =
|
||||
S.Patch
|
||||
<$> Map.bitraverse saveReferentH (Set.traverse c2sTermEdit) termEdits
|
||||
<*> Map.bitraverse saveReferenceH (Set.traverse c2sTypeEdit) typeEdits
|
||||
<*> Map.bitraverse Q.saveReferenceH (Set.traverse c2sTypeEdit) typeEdits
|
||||
where
|
||||
c2sTermEdit = \case
|
||||
C.TermEdit.Replace r t -> S.TermEdit.Replace <$> c2sReferent r <*> pure (c2sTyping t)
|
||||
@ -346,37 +353,6 @@ diffPatch (S.Patch fullTerms fullTypes) (S.Patch refTerms refTypes) =
|
||||
addDiffSet = Map.zipWithMatched (const Set.difference)
|
||||
removeDiffSet = Map.zipWithMatched (const (flip Set.difference))
|
||||
|
||||
-- * Deserialization helpers
|
||||
|
||||
decodeBranchFormat :: ByteString -> Either DecodeError S.BranchFormat.BranchFormat
|
||||
decodeBranchFormat = getFromBytesOr "getBranchFormat" S.getBranchFormat
|
||||
|
||||
decodePatchFormat :: ByteString -> Either DecodeError S.Patch.Format.PatchFormat
|
||||
decodePatchFormat = getFromBytesOr "getPatchFormat" S.getPatchFormat
|
||||
|
||||
decodeTermFormat :: ByteString -> Either DecodeError S.Term.TermFormat
|
||||
decodeTermFormat = getFromBytesOr "getTermFormat" S.getTermFormat
|
||||
|
||||
decodeComponentLengthOnly :: ByteString -> Either DecodeError Word64
|
||||
decodeComponentLengthOnly = getFromBytesOr "lengthFramedArray" (Get.skip 1 >> S.lengthFramedArray)
|
||||
|
||||
decodeTermElementWithType :: C.Reference.Pos -> ByteString -> Either DecodeError (LocalIds, S.Term.Term, S.Term.Type)
|
||||
decodeTermElementWithType i = getFromBytesOr ("lookupTermElement" <> tShow i) (S.lookupTermElement i)
|
||||
|
||||
decodeTermElementDiscardingTerm :: C.Reference.Pos -> ByteString -> Either DecodeError (LocalIds, S.Term.Type)
|
||||
decodeTermElementDiscardingTerm i =
|
||||
getFromBytesOr ("lookupTermElementDiscardingTerm " <> tShow i) (S.lookupTermElementDiscardingTerm i)
|
||||
|
||||
decodeTermElementDiscardingType :: C.Reference.Pos -> ByteString -> Either DecodeError (LocalIds, S.Term.Term)
|
||||
decodeTermElementDiscardingType i =
|
||||
getFromBytesOr ("lookupTermElementDiscardingType " <> tShow i) (S.lookupTermElementDiscardingType i)
|
||||
|
||||
decodeDeclFormat :: ByteString -> Either DecodeError S.Decl.DeclFormat
|
||||
decodeDeclFormat = getFromBytesOr "getDeclFormat" S.getDeclFormat
|
||||
|
||||
decodeDeclElement :: Word64 -> ByteString -> Either DecodeError (LocalIds, S.Decl.Decl Symbol)
|
||||
decodeDeclElement i = getFromBytesOr ("lookupDeclElement " <> tShow i) (S.lookupDeclElement i)
|
||||
|
||||
getCycleLen :: H.Hash -> Transaction (Maybe Word64)
|
||||
getCycleLen h = do
|
||||
when debug $ traceM $ "\ngetCycleLen " ++ (Text.unpack . Base32Hex.toText $ H.toBase32Hex h)
|
||||
@ -409,163 +385,14 @@ loadTermComponent :: H.Hash -> MaybeT Transaction [(C.Term Symbol, C.Term.Type S
|
||||
loadTermComponent h = do
|
||||
oid <- MaybeT (Q.loadObjectIdForAnyHash h)
|
||||
S.Term.Term (S.Term.LocallyIndexedComponent elements) <- MaybeT (Q.loadTermObject oid decodeTermFormat)
|
||||
lift . traverse (uncurry3 s2cTermWithType) $ Foldable.toList elements
|
||||
|
||||
saveTermComponent :: H.Hash -> [(C.Term Symbol, C.Term.Type Symbol)] -> Transaction Db.ObjectId
|
||||
saveTermComponent h terms = do
|
||||
when debug . traceM $ "Operations.saveTermComponent " ++ show h
|
||||
sTermElements <- traverse (uncurry c2sTerm) terms
|
||||
hashId <- Q.saveHashHash h
|
||||
let li = S.Term.LocallyIndexedComponent $ Vector.fromList sTermElements
|
||||
bytes = S.putBytes S.putTermFormat $ S.Term.Term li
|
||||
oId <- Q.saveObject hashId OT.TermComponent bytes
|
||||
-- populate dependents index
|
||||
let dependencies :: Set (S.Reference.Reference, S.Reference.Id) = foldMap unlocalizeRefs (sTermElements `zip` [0 ..])
|
||||
unlocalizeRefs :: ((LocalIds, S.Term.Term, S.Term.Type), C.Reference.Pos) -> Set (S.Reference.Reference, S.Reference.Id)
|
||||
unlocalizeRefs ((LocalIds tIds oIds, tm, tp), i) =
|
||||
let self = C.Reference.Id oId i
|
||||
dependencies :: Set S.Reference =
|
||||
let (tmRefs, tpRefs, tmLinks, tpLinks) = TermUtil.dependencies tm
|
||||
tpRefs' = Foldable.toList $ C.Type.dependencies tp
|
||||
getTermSRef :: S.Term.TermRef -> S.Reference
|
||||
getTermSRef = \case
|
||||
C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t)
|
||||
C.Reference.Derived Nothing i -> C.Reference.Derived oId i -- index self-references
|
||||
C.Reference.Derived (Just h) i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i
|
||||
getTypeSRef :: S.Term.TypeRef -> S.Reference
|
||||
getTypeSRef = \case
|
||||
C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t)
|
||||
C.Reference.Derived h i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i
|
||||
getSTypeLink = getTypeSRef
|
||||
getSTermLink :: S.Term.TermLink -> S.Reference
|
||||
getSTermLink = \case
|
||||
C.Referent.Con ref _conId -> getTypeSRef ref
|
||||
C.Referent.Ref ref -> getTermSRef ref
|
||||
in Set.fromList $
|
||||
map getTermSRef tmRefs
|
||||
++ map getSTermLink tmLinks
|
||||
++ map getTypeSRef (tpRefs ++ tpRefs')
|
||||
++ map getSTypeLink tpLinks
|
||||
in Set.map (,self) dependencies
|
||||
traverse_ (uncurry Q.addToDependentsIndex) dependencies
|
||||
|
||||
pure oId
|
||||
|
||||
-- | implementation detail of c2{s,w}Term
|
||||
-- The Type is optional, because we don't store them for watch expression results.
|
||||
c2xTerm :: forall m t d. Monad m => (Text -> m t) -> (H.Hash -> m d) -> C.Term Symbol -> Maybe (C.Term.Type Symbol) -> m (LocalIds' t d, S.Term.Term, Maybe (S.Term.Type))
|
||||
c2xTerm saveText saveDefn tm tp =
|
||||
done =<< (runWriterT . flip evalStateT mempty) do
|
||||
sterm <- ABT.transformM go tm
|
||||
stype <- traverse (ABT.transformM goType) tp
|
||||
pure (sterm, stype)
|
||||
where
|
||||
go :: forall m a. (MonadWriter (Seq Text, Seq H.Hash) m, MonadState (Map Text LocalTextId, Map H.Hash LocalDefnId) m) => C.Term.F Symbol a -> m (S.Term.F a)
|
||||
go = \case
|
||||
C.Term.Int n -> pure $ C.Term.Int n
|
||||
C.Term.Nat n -> pure $ C.Term.Nat n
|
||||
C.Term.Float n -> pure $ C.Term.Float n
|
||||
C.Term.Boolean b -> pure $ C.Term.Boolean b
|
||||
C.Term.Text t -> C.Term.Text <$> lookupText t
|
||||
C.Term.Char ch -> pure $ C.Term.Char ch
|
||||
C.Term.Ref r ->
|
||||
C.Term.Ref <$> bitraverse lookupText (traverse lookupDefn) r
|
||||
C.Term.Constructor typeRef cid ->
|
||||
C.Term.Constructor
|
||||
<$> bitraverse lookupText lookupDefn typeRef
|
||||
<*> pure cid
|
||||
C.Term.Request typeRef cid ->
|
||||
C.Term.Request <$> bitraverse lookupText lookupDefn typeRef <*> pure cid
|
||||
C.Term.Handle a a2 -> pure $ C.Term.Handle a a2
|
||||
C.Term.App a a2 -> pure $ C.Term.App a a2
|
||||
C.Term.Ann a typ -> C.Term.Ann a <$> ABT.transformM goType typ
|
||||
C.Term.List as -> pure $ C.Term.List as
|
||||
C.Term.If c t f -> pure $ C.Term.If c t f
|
||||
C.Term.And a a2 -> pure $ C.Term.And a a2
|
||||
C.Term.Or a a2 -> pure $ C.Term.Or a a2
|
||||
C.Term.Lam a -> pure $ C.Term.Lam a
|
||||
C.Term.LetRec bs a -> pure $ C.Term.LetRec bs a
|
||||
C.Term.Let a a2 -> pure $ C.Term.Let a a2
|
||||
C.Term.Match a cs -> C.Term.Match a <$> traverse goCase cs
|
||||
C.Term.TermLink r ->
|
||||
C.Term.TermLink
|
||||
<$> bitraverse
|
||||
(bitraverse lookupText (traverse lookupDefn))
|
||||
(bitraverse lookupText lookupDefn)
|
||||
r
|
||||
C.Term.TypeLink r ->
|
||||
C.Term.TypeLink <$> bitraverse lookupText lookupDefn r
|
||||
goType ::
|
||||
forall m a.
|
||||
(MonadWriter (Seq Text, Seq H.Hash) m, MonadState (Map Text LocalTextId, Map H.Hash LocalDefnId) m) =>
|
||||
C.Type.FT a ->
|
||||
m (S.Term.FT a)
|
||||
goType = \case
|
||||
C.Type.Ref r -> C.Type.Ref <$> bitraverse lookupText lookupDefn r
|
||||
C.Type.Arrow i o -> pure $ C.Type.Arrow i o
|
||||
C.Type.Ann a k -> pure $ C.Type.Ann a k
|
||||
C.Type.App f a -> pure $ C.Type.App f a
|
||||
C.Type.Effect e a -> pure $ C.Type.Effect e a
|
||||
C.Type.Effects es -> pure $ C.Type.Effects es
|
||||
C.Type.Forall a -> pure $ C.Type.Forall a
|
||||
C.Type.IntroOuter a -> pure $ C.Type.IntroOuter a
|
||||
goCase ::
|
||||
forall m w s a.
|
||||
( MonadState s m,
|
||||
MonadWriter w m,
|
||||
Lens.Field1' s (Map Text LocalTextId),
|
||||
Lens.Field1' w (Seq Text),
|
||||
Lens.Field2' s (Map H.Hash LocalDefnId),
|
||||
Lens.Field2' w (Seq H.Hash)
|
||||
) =>
|
||||
C.Term.MatchCase Text C.Term.TypeRef a ->
|
||||
m (C.Term.MatchCase LocalTextId S.Term.TypeRef a)
|
||||
goCase = \case
|
||||
C.Term.MatchCase pat guard body ->
|
||||
C.Term.MatchCase <$> goPat pat <*> pure guard <*> pure body
|
||||
goPat ::
|
||||
forall m s w.
|
||||
( MonadState s m,
|
||||
MonadWriter w m,
|
||||
Lens.Field1' s (Map Text LocalTextId),
|
||||
Lens.Field1' w (Seq Text),
|
||||
Lens.Field2' s (Map H.Hash LocalDefnId),
|
||||
Lens.Field2' w (Seq H.Hash)
|
||||
) =>
|
||||
C.Term.Pattern Text C.Term.TypeRef ->
|
||||
m (C.Term.Pattern LocalTextId S.Term.TypeRef)
|
||||
goPat = \case
|
||||
C.Term.PUnbound -> pure $ C.Term.PUnbound
|
||||
C.Term.PVar -> pure $ C.Term.PVar
|
||||
C.Term.PBoolean b -> pure $ C.Term.PBoolean b
|
||||
C.Term.PInt i -> pure $ C.Term.PInt i
|
||||
C.Term.PNat n -> pure $ C.Term.PNat n
|
||||
C.Term.PFloat d -> pure $ C.Term.PFloat d
|
||||
C.Term.PText t -> C.Term.PText <$> lookupText t
|
||||
C.Term.PChar c -> pure $ C.Term.PChar c
|
||||
C.Term.PConstructor r i ps -> C.Term.PConstructor <$> bitraverse lookupText lookupDefn r <*> pure i <*> traverse goPat ps
|
||||
C.Term.PAs p -> C.Term.PAs <$> goPat p
|
||||
C.Term.PEffectPure p -> C.Term.PEffectPure <$> goPat p
|
||||
C.Term.PEffectBind r i bindings k -> C.Term.PEffectBind <$> bitraverse lookupText lookupDefn r <*> pure i <*> traverse goPat bindings <*> goPat k
|
||||
C.Term.PSequenceLiteral ps -> C.Term.PSequenceLiteral <$> traverse goPat ps
|
||||
C.Term.PSequenceOp l op r -> C.Term.PSequenceOp <$> goPat l <*> pure op <*> goPat r
|
||||
|
||||
done :: ((S.Term.Term, Maybe S.Term.Type), (Seq Text, Seq H.Hash)) -> m (LocalIds' t d, S.Term.Term, Maybe S.Term.Type)
|
||||
done ((tm, tp), (localTextValues, localDefnValues)) = do
|
||||
textIds <- traverse saveText localTextValues
|
||||
defnIds <- traverse saveDefn localDefnValues
|
||||
let ids =
|
||||
LocalIds
|
||||
(Vector.fromList (Foldable.toList textIds))
|
||||
(Vector.fromList (Foldable.toList defnIds))
|
||||
pure (ids, void tm, void <$> tp)
|
||||
lift . traverse (uncurry3 Q.s2cTermWithType) $ Foldable.toList elements
|
||||
|
||||
loadTermWithTypeByReference :: C.Reference.Id -> MaybeT Transaction (C.Term Symbol, C.Term.Type Symbol)
|
||||
loadTermWithTypeByReference (C.Reference.Id h i) = do
|
||||
oid <- MaybeT (Q.loadObjectIdForPrimaryHash h)
|
||||
-- retrieve and deserialize the blob
|
||||
(localIds, term, typ) <- MaybeT (Q.loadTermObject oid (decodeTermElementWithType i))
|
||||
lift (s2cTermWithType localIds term typ)
|
||||
lift (Q.s2cTermWithType localIds term typ)
|
||||
|
||||
loadTermByReference :: C.Reference.Id -> MaybeT Transaction (C.Term Symbol)
|
||||
loadTermByReference r@(C.Reference.Id h i) = do
|
||||
@ -583,95 +410,15 @@ loadTypeOfTermByTermReference id@(C.Reference.Id h i) = do
|
||||
(localIds, typ) <- MaybeT (Q.loadTermObject oid (decodeTermElementDiscardingTerm i))
|
||||
lift (s2cTypeOfTerm localIds typ)
|
||||
|
||||
s2cTermWithType :: LocalIds -> S.Term.Term -> S.Term.Type -> Transaction (C.Term Symbol, C.Term.Type Symbol)
|
||||
s2cTermWithType ids tm tp = do
|
||||
(substText, substHash) <- localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId ids
|
||||
pure (x2cTerm substText substHash tm, x2cTType substText substHash tp)
|
||||
|
||||
s2cTerm :: LocalIds -> S.Term.Term -> Transaction (C.Term Symbol)
|
||||
s2cTerm ids tm = do
|
||||
(substText, substHash) <- localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId ids
|
||||
pure $ x2cTerm substText substHash tm
|
||||
(substText, substHash) <- Q.localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId ids
|
||||
pure $ Q.x2cTerm substText substHash tm
|
||||
|
||||
s2cTypeOfTerm :: LocalIds -> S.Term.Type -> Transaction (C.Term.Type Symbol)
|
||||
s2cTypeOfTerm ids tp = do
|
||||
(substText, substHash) <- localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId ids
|
||||
pure $ x2cTType substText substHash tp
|
||||
|
||||
-- | implementation detail of {s,w}2c*Term* & s2cDecl
|
||||
localIdsToLookups :: Monad m => (t -> m Text) -> (d -> m H.Hash) -> LocalIds' t d -> m (LocalTextId -> Text, LocalDefnId -> H.Hash)
|
||||
localIdsToLookups loadText loadHash localIds = do
|
||||
texts <- traverse loadText $ LocalIds.textLookup localIds
|
||||
hashes <- traverse loadHash $ LocalIds.defnLookup localIds
|
||||
let substText (LocalTextId w) = texts Vector.! fromIntegral w
|
||||
substHash (LocalDefnId w) = hashes Vector.! fromIntegral w
|
||||
pure (substText, substHash)
|
||||
|
||||
localIdsToTypeRefLookup :: LocalIds -> Transaction (S.Decl.TypeRef -> C.Decl.TypeRef)
|
||||
localIdsToTypeRefLookup localIds = do
|
||||
(substText, substHash) <- localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId localIds
|
||||
pure $ bimap substText (fmap substHash)
|
||||
|
||||
-- | implementation detail of {s,w}2c*Term*
|
||||
x2cTerm :: (LocalTextId -> Text) -> (LocalDefnId -> H.Hash) -> S.Term.Term -> C.Term Symbol
|
||||
x2cTerm substText substHash =
|
||||
-- substitute the text and hashes back into the term
|
||||
C.Term.extraMap substText substTermRef substTypeRef substTermLink substTypeLink id
|
||||
where
|
||||
substTermRef = bimap substText (fmap substHash)
|
||||
substTypeRef = bimap substText substHash
|
||||
substTermLink = bimap substTermRef substTypeRef
|
||||
substTypeLink = substTypeRef
|
||||
|
||||
-- | implementation detail of {s,w}2c*Term*
|
||||
x2cTType :: (LocalTextId -> Text) -> (LocalDefnId -> H.Hash) -> S.Term.Type -> C.Term.Type Symbol
|
||||
x2cTType substText substHash = C.Type.rmap (bimap substText substHash)
|
||||
|
||||
lookupText ::
|
||||
forall m s w t.
|
||||
( MonadState s m,
|
||||
MonadWriter w m,
|
||||
Lens.Field1' s (Map t LocalTextId),
|
||||
Lens.Field1' w (Seq t),
|
||||
Ord t
|
||||
) =>
|
||||
t ->
|
||||
m LocalTextId
|
||||
lookupText = lookup_ Lens._1 Lens._1 LocalTextId
|
||||
|
||||
lookupDefn ::
|
||||
forall m s w d.
|
||||
( MonadState s m,
|
||||
MonadWriter w m,
|
||||
Lens.Field2' s (Map d LocalDefnId),
|
||||
Lens.Field2' w (Seq d),
|
||||
Ord d
|
||||
) =>
|
||||
d ->
|
||||
m LocalDefnId
|
||||
lookupDefn = lookup_ Lens._2 Lens._2 LocalDefnId
|
||||
|
||||
-- | shared implementation of lookupTextHelper and lookupDefnHelper
|
||||
-- Look up a value in the LUT, or append it.
|
||||
lookup_ ::
|
||||
(MonadState s m, MonadWriter w m, Ord t) =>
|
||||
Lens' s (Map t t') ->
|
||||
Lens' w (Seq t) ->
|
||||
(Word64 -> t') ->
|
||||
t ->
|
||||
m t'
|
||||
lookup_ stateLens writerLens mk t = do
|
||||
map <- Lens.use stateLens
|
||||
case Map.lookup t map of
|
||||
Nothing -> do
|
||||
let id = mk . fromIntegral $ Map.size map
|
||||
stateLens Lens.%= Map.insert t id
|
||||
Writer.tell $ Lens.set writerLens (Seq.singleton t) mempty
|
||||
pure id
|
||||
Just t' -> pure t'
|
||||
|
||||
c2sTerm :: C.Term Symbol -> C.Term.Type Symbol -> Transaction (LocalIds, S.Term.Term, S.Term.Type)
|
||||
c2sTerm tm tp = c2xTerm Q.saveText Q.expectObjectIdForPrimaryHash tm (Just tp) <&> \(w, tm, Just tp) -> (w, tm, tp)
|
||||
(substText, substHash) <- Q.localIdsToLookups Q.expectText Q.expectPrimaryHashByObjectId ids
|
||||
pure $ Q.x2cTType substText substHash tp
|
||||
|
||||
-- *** Watch expressions
|
||||
|
||||
@ -682,7 +429,7 @@ listWatches k = Q.loadWatchesByWatchKind k >>= traverse h2cReferenceId
|
||||
loadWatch :: WatchKind -> C.Reference.Id -> MaybeT Transaction (C.Term Symbol)
|
||||
loadWatch k r = do
|
||||
r' <- C.Reference.idH (lift . Q.saveHashHash) r
|
||||
S.Term.WatchResult wlids t <- MaybeT (Q.loadWatch k r' (getFromBytesOr "getWatchResultFormat" S.getWatchResultFormat))
|
||||
S.Term.WatchResult wlids t <- MaybeT (Q.loadWatch k r' decodeWatchResultFormat)
|
||||
lift (w2cTerm wlids t)
|
||||
|
||||
saveWatch :: WatchKind -> C.Reference.Id -> C.Term Symbol -> Transaction ()
|
||||
@ -696,12 +443,12 @@ clearWatches :: Transaction ()
|
||||
clearWatches = Q.clearWatches
|
||||
|
||||
c2wTerm :: C.Term Symbol -> Transaction (WatchLocalIds, S.Term.Term)
|
||||
c2wTerm tm = c2xTerm Q.saveText Q.saveHashHash tm Nothing <&> \(w, tm, _) -> (w, tm)
|
||||
c2wTerm tm = Q.c2xTerm Q.saveText Q.saveHashHash tm Nothing <&> \(w, tm, _) -> (w, tm)
|
||||
|
||||
w2cTerm :: WatchLocalIds -> S.Term.Term -> Transaction (C.Term Symbol)
|
||||
w2cTerm ids tm = do
|
||||
(substText, substHash) <- localIdsToLookups Q.expectText Q.expectHash ids
|
||||
pure $ x2cTerm substText substHash tm
|
||||
(substText, substHash) <- Q.localIdsToLookups Q.expectText Q.expectHash ids
|
||||
pure $ Q.x2cTerm substText substHash tm
|
||||
|
||||
-- ** Saving & loading type decls
|
||||
|
||||
@ -709,80 +456,14 @@ loadDeclComponent :: H.Hash -> MaybeT Transaction [C.Decl Symbol]
|
||||
loadDeclComponent h = do
|
||||
oid <- MaybeT (Q.loadObjectIdForAnyHash h)
|
||||
S.Decl.Decl (S.Decl.LocallyIndexedComponent elements) <- MaybeT (Q.loadDeclObject oid decodeDeclFormat)
|
||||
lift . traverse (uncurry s2cDecl) $ Foldable.toList elements
|
||||
|
||||
saveDeclComponent :: H.Hash -> [C.Decl Symbol] -> Transaction Db.ObjectId
|
||||
saveDeclComponent h decls = do
|
||||
when debug . traceM $ "Operations.saveDeclComponent " ++ show h
|
||||
sDeclElements <- traverse (c2sDecl Q.saveText Q.expectObjectIdForPrimaryHash) decls
|
||||
hashId <- Q.saveHashHash h
|
||||
let li = S.Decl.LocallyIndexedComponent $ Vector.fromList sDeclElements
|
||||
bytes = S.putBytes S.putDeclFormat $ S.Decl.Decl li
|
||||
oId <- Q.saveObject hashId OT.DeclComponent bytes
|
||||
-- populate dependents index
|
||||
let dependencies :: Set (S.Reference.Reference, S.Reference.Id) = foldMap unlocalizeRefs (sDeclElements `zip` [0 ..])
|
||||
unlocalizeRefs :: ((LocalIds, S.Decl.Decl Symbol), C.Reference.Pos) -> Set (S.Reference.Reference, S.Reference.Id)
|
||||
unlocalizeRefs ((LocalIds tIds oIds, decl), i) =
|
||||
let self = C.Reference.Id oId i
|
||||
dependencies :: Set S.Decl.TypeRef = C.Decl.dependencies decl
|
||||
getSRef :: C.Reference.Reference' LocalTextId (Maybe LocalDefnId) -> S.Reference.Reference
|
||||
getSRef = \case
|
||||
C.ReferenceBuiltin t -> C.ReferenceBuiltin (tIds Vector.! fromIntegral t)
|
||||
C.Reference.Derived Nothing i -> C.Reference.Derived oId i -- index self-references
|
||||
C.Reference.Derived (Just h) i -> C.Reference.Derived (oIds Vector.! fromIntegral h) i
|
||||
in Set.map ((,self) . getSRef) dependencies
|
||||
traverse_ (uncurry Q.addToDependentsIndex) dependencies
|
||||
|
||||
pure oId
|
||||
|
||||
c2sDecl ::
|
||||
forall m t d.
|
||||
Monad m =>
|
||||
(Text -> m t) ->
|
||||
(H.Hash -> m d) ->
|
||||
C.Decl Symbol ->
|
||||
m (LocalIds' t d, S.Decl.Decl Symbol)
|
||||
c2sDecl saveText saveDefn (C.Decl.DataDeclaration dt m b cts) = do
|
||||
done =<< (runWriterT . flip evalStateT mempty) do
|
||||
cts' <- traverse (ABT.transformM goType) cts
|
||||
pure (C.Decl.DataDeclaration dt m b cts')
|
||||
where
|
||||
goType ::
|
||||
forall m a.
|
||||
(MonadWriter (Seq Text, Seq H.Hash) m, MonadState (Map Text LocalTextId, Map H.Hash LocalDefnId) m) =>
|
||||
C.Type.FD a ->
|
||||
m (S.Decl.F a)
|
||||
goType = \case
|
||||
C.Type.Ref r -> C.Type.Ref <$> bitraverse lookupText (traverse lookupDefn) r
|
||||
C.Type.Arrow i o -> pure $ C.Type.Arrow i o
|
||||
C.Type.Ann a k -> pure $ C.Type.Ann a k
|
||||
C.Type.App f a -> pure $ C.Type.App f a
|
||||
C.Type.Effect e a -> pure $ C.Type.Effect e a
|
||||
C.Type.Effects es -> pure $ C.Type.Effects es
|
||||
C.Type.Forall a -> pure $ C.Type.Forall a
|
||||
C.Type.IntroOuter a -> pure $ C.Type.IntroOuter a
|
||||
done :: (S.Decl.Decl Symbol, (Seq Text, Seq H.Hash)) -> m (LocalIds' t d, S.Decl.Decl Symbol)
|
||||
done (decl, (localTextValues, localDefnValues)) = do
|
||||
textIds <- traverse saveText localTextValues
|
||||
defnIds <- traverse saveDefn localDefnValues
|
||||
let ids =
|
||||
LocalIds
|
||||
(Vector.fromList (Foldable.toList textIds))
|
||||
(Vector.fromList (Foldable.toList defnIds))
|
||||
pure (ids, decl)
|
||||
|
||||
-- | Unlocalize a decl.
|
||||
s2cDecl :: LocalIds -> S.Decl.Decl Symbol -> Transaction (C.Decl Symbol)
|
||||
s2cDecl ids (C.Decl.DataDeclaration dt m b ct) = do
|
||||
substTypeRef <- localIdsToTypeRefLookup ids
|
||||
pure (C.Decl.DataDeclaration dt m b (C.Type.rmap substTypeRef <$> ct))
|
||||
lift . traverse (uncurry Q.s2cDecl) $ Foldable.toList elements
|
||||
|
||||
loadDeclByReference :: C.Reference.Id -> MaybeT Transaction (C.Decl Symbol)
|
||||
loadDeclByReference r@(C.Reference.Id h i) = do
|
||||
when debug . traceM $ "loadDeclByReference " ++ show r
|
||||
oid <- MaybeT (Q.loadObjectIdForPrimaryHash h)
|
||||
(localIds, decl) <- MaybeT (Q.loadDeclObject oid (decodeDeclElement i))
|
||||
lift (s2cDecl localIds decl)
|
||||
lift (Q.s2cDecl localIds decl)
|
||||
|
||||
expectDeclByReference :: C.Reference.Id -> Transaction (C.Decl Symbol)
|
||||
expectDeclByReference r@(C.Reference.Id h i) = do
|
||||
@ -790,7 +471,7 @@ expectDeclByReference r@(C.Reference.Id h i) = do
|
||||
-- retrieve the blob
|
||||
Q.expectObjectIdForPrimaryHash h
|
||||
>>= (\oid -> Q.expectDeclObject oid (decodeDeclElement i))
|
||||
>>= uncurry s2cDecl
|
||||
>>= uncurry Q.s2cDecl
|
||||
|
||||
-- * Branch transformation
|
||||
|
||||
@ -884,10 +565,13 @@ s2cBranch (S.Branch.Full.Branch tms tps patches children) =
|
||||
boId <- Q.expectBranchObjectIdByCausalHashId chId
|
||||
expectBranch boId
|
||||
|
||||
saveRootBranch :: C.Branch.CausalBranch Transaction -> Transaction (Db.BranchObjectId, Db.CausalHashId)
|
||||
saveRootBranch c = do
|
||||
saveRootBranch ::
|
||||
HashHandle ->
|
||||
C.Branch.CausalBranch Transaction ->
|
||||
Transaction (Db.BranchObjectId, Db.CausalHashId)
|
||||
saveRootBranch hh c = do
|
||||
when debug $ traceM $ "Operations.saveRootBranch " ++ show (C.causalHash c)
|
||||
(boId, chId) <- saveBranch c
|
||||
(boId, chId) <- saveBranch hh c
|
||||
Q.setNamespaceRoot chId
|
||||
pure (boId, chId)
|
||||
|
||||
@ -930,16 +614,18 @@ saveRootBranch c = do
|
||||
-- References, but also values
|
||||
-- Shallow - Hash? representation of the database relationships
|
||||
|
||||
saveBranch :: C.Branch.CausalBranch Transaction -> Transaction (Db.BranchObjectId, Db.CausalHashId)
|
||||
saveBranch (C.Causal hc he parents me) = do
|
||||
saveBranch ::
|
||||
HashHandle ->
|
||||
C.Branch.CausalBranch Transaction ->
|
||||
Transaction (Db.BranchObjectId, Db.CausalHashId)
|
||||
saveBranch hh (C.Causal hc he parents me) = do
|
||||
when debug $ traceM $ "\nOperations.saveBranch \n hc = " ++ show hc ++ ",\n he = " ++ show he ++ ",\n parents = " ++ show (Map.keys parents)
|
||||
|
||||
(chId, bhId) <- flip Monad.fromMaybeM (Q.loadCausalByCausalHash hc) do
|
||||
-- if not exist, create these
|
||||
chId <- Q.saveCausalHash hc
|
||||
bhId <- Q.saveBranchHash he
|
||||
Q.saveCausal chId bhId
|
||||
-- save the link between child and parents
|
||||
|
||||
parentCausalHashIds <-
|
||||
-- so try to save each parent (recursively) before continuing to save hc
|
||||
for (Map.toList parents) $ \(parentHash, mcausal) ->
|
||||
@ -947,14 +633,14 @@ saveBranch (C.Causal hc he parents me) = do
|
||||
-- by checking if there are causal parents associated with hc
|
||||
(flip Monad.fromMaybeM)
|
||||
(Q.loadCausalHashIdByCausalHash parentHash)
|
||||
(mcausal >>= fmap snd . saveBranch)
|
||||
unless (null parentCausalHashIds) $
|
||||
Q.saveCausalParents chId parentCausalHashIds
|
||||
(mcausal >>= fmap snd . saveBranch hh)
|
||||
|
||||
-- Save these CausalHashIds to the causal_parents table,
|
||||
Q.saveCausal hh chId bhId parentCausalHashIds
|
||||
pure (chId, bhId)
|
||||
boId <- flip Monad.fromMaybeM (Q.loadBranchObjectIdByCausalHashId chId) do
|
||||
branch <- c2sBranch =<< me
|
||||
let (li, lBranch) = LocalizeObject.localizeBranch branch
|
||||
saveBranchObject bhId li lBranch
|
||||
saveDbBranchUnderHashId hh bhId branch
|
||||
pure (boId, chId)
|
||||
where
|
||||
c2sBranch :: C.Branch.Branch Transaction -> Transaction S.DbBranch
|
||||
@ -963,7 +649,7 @@ saveBranch (C.Causal hc he parents me) = do
|
||||
<$> Map.bitraverse saveNameSegment (Map.bitraverse c2sReferent c2sMetadata) terms
|
||||
<*> Map.bitraverse saveNameSegment (Map.bitraverse c2sReference c2sMetadata) types
|
||||
<*> Map.bitraverse saveNameSegment savePatchObjectId patches
|
||||
<*> Map.bitraverse saveNameSegment saveBranch children
|
||||
<*> Map.bitraverse saveNameSegment (saveBranch hh) children
|
||||
|
||||
saveNameSegment :: C.Branch.NameSegment -> Transaction Db.TextId
|
||||
saveNameSegment = Q.saveText . C.Branch.unNameSegment
|
||||
@ -979,14 +665,7 @@ saveBranch (C.Causal hc he parents me) = do
|
||||
Just patchOID -> pure patchOID
|
||||
Nothing -> do
|
||||
patch <- mp
|
||||
savePatch h patch
|
||||
|
||||
saveBranchObject :: Db.BranchHashId -> BranchLocalIds -> S.Branch.Full.LocalBranch -> Transaction Db.BranchObjectId
|
||||
saveBranchObject id@(Db.unBranchHashId -> hashId) li lBranch = do
|
||||
when debug $ traceM $ "saveBranchObject\n\tid = " ++ show id ++ "\n\tli = " ++ show li ++ "\n\tlBranch = " ++ show lBranch
|
||||
let bytes = S.putBytes S.putBranchFormat $ S.BranchFormat.Full li lBranch
|
||||
oId <- Q.saveObject hashId OT.Namespace bytes
|
||||
pure $ Db.BranchObjectId oId
|
||||
savePatch hh h patch
|
||||
|
||||
expectRootCausal :: Transaction (C.Branch.CausalBranch Transaction)
|
||||
expectRootCausal = Q.expectNamespaceRoot >>= expectCausalBranchByCausalHashId
|
||||
@ -1017,6 +696,19 @@ expectBranchByCausalHashId id = do
|
||||
boId <- Q.expectBranchObjectIdByCausalHashId id
|
||||
expectBranch boId
|
||||
|
||||
-- | Load a branch value given its causal hash id.
|
||||
loadDbBranchByCausalHashId :: Db.CausalHashId -> Transaction (Maybe S.DbBranch)
|
||||
loadDbBranchByCausalHashId causalHashId =
|
||||
Q.loadBranchObjectIdByCausalHashId causalHashId >>= \case
|
||||
Nothing -> pure Nothing
|
||||
Just branchObjectId -> Just <$> expectDbBranch branchObjectId
|
||||
|
||||
-- | Expect a branch value given its causal hash id.
|
||||
expectDbBranchByCausalHashId :: Db.CausalHashId -> Transaction S.DbBranch
|
||||
expectDbBranchByCausalHashId causalHashId = do
|
||||
branchObjectId <- Q.expectBranchObjectIdByCausalHashId causalHashId
|
||||
expectDbBranch branchObjectId
|
||||
|
||||
expectDbBranch :: Db.BranchObjectId -> Transaction S.DbBranch
|
||||
expectDbBranch id =
|
||||
deserializeBranchObject id >>= \case
|
||||
@ -1123,6 +815,36 @@ expectDbBranch id =
|
||||
let (Set.fromList -> adds, Set.fromList -> removes) = S.BranchDiff.addsRemoves md'
|
||||
in Just . S.MetadataSet.Inline $ (Set.union adds $ Set.difference md removes)
|
||||
|
||||
-- | Save a 'S.DbBranch', given its hash (which the caller is expected to produce from the branch).
|
||||
--
|
||||
-- Note: long-standing question: should this package depend on the hashing package? (If so, we would only need to take
|
||||
-- the DbBranch, and hash internally).
|
||||
saveDbBranch ::
|
||||
HashHandle ->
|
||||
BranchHash ->
|
||||
S.DbBranch ->
|
||||
Transaction Db.BranchObjectId
|
||||
saveDbBranch hh hash branch = do
|
||||
hashId <- Q.saveBranchHash hash
|
||||
saveDbBranchUnderHashId hh hashId branch
|
||||
|
||||
-- | Variant of 'saveDbBranch' that might be preferred by callers that already have a hash id, not a hash.
|
||||
saveDbBranchUnderHashId ::
|
||||
HashHandle ->
|
||||
Db.BranchHashId ->
|
||||
S.DbBranch ->
|
||||
Transaction Db.BranchObjectId
|
||||
saveDbBranchUnderHashId hh id@(Db.unBranchHashId -> hashId) branch = do
|
||||
let (localBranchIds, localBranch) = LocalizeObject.localizeBranch branch
|
||||
when debug $
|
||||
traceM $
|
||||
"saveBranchObject\n\tid = " ++ show id ++ "\n\tli = " ++ show localBranchIds
|
||||
++ "\n\tlBranch = "
|
||||
++ show localBranch
|
||||
let bytes = S.putBytes S.putBranchFormat $ S.BranchFormat.Full localBranchIds localBranch
|
||||
oId <- Q.saveObject hh hashId ObjectType.Namespace bytes
|
||||
pure $ Db.BranchObjectId oId
|
||||
|
||||
expectBranch :: Db.BranchObjectId -> Transaction (C.Branch.Branch Transaction)
|
||||
expectBranch id =
|
||||
expectDbBranch id >>= s2cBranch
|
||||
@ -1145,16 +867,24 @@ expectDbPatch patchId =
|
||||
S.Patch.Format.Full li f -> pure (S.Patch.Format.applyPatchDiffs (S.Patch.Format.localPatchToPatch li f) ds)
|
||||
S.Patch.Format.Diff ref' li' d' -> doDiff ref' (S.Patch.Format.localPatchDiffToPatchDiff li' d' : ds)
|
||||
|
||||
savePatch :: PatchHash -> C.Branch.Patch -> Transaction Db.PatchObjectId
|
||||
savePatch h c = do
|
||||
savePatch ::
|
||||
HashHandle ->
|
||||
PatchHash ->
|
||||
C.Branch.Patch ->
|
||||
Transaction Db.PatchObjectId
|
||||
savePatch hh h c = do
|
||||
(li, lPatch) <- LocalizeObject.localizePatch <$> c2sPatch c
|
||||
saveDbPatch h (S.Patch.Format.Full li lPatch)
|
||||
saveDbPatch hh h (S.Patch.Format.Full li lPatch)
|
||||
|
||||
saveDbPatch :: PatchHash -> S.PatchFormat -> Transaction Db.PatchObjectId
|
||||
saveDbPatch hash patch = do
|
||||
saveDbPatch ::
|
||||
HashHandle ->
|
||||
PatchHash ->
|
||||
S.PatchFormat ->
|
||||
Transaction Db.PatchObjectId
|
||||
saveDbPatch hh hash patch = do
|
||||
hashId <- Q.saveHashHash (unPatchHash hash)
|
||||
let bytes = S.putBytes S.putPatchFormat patch
|
||||
Db.PatchObjectId <$> Q.saveObject hashId OT.Patch bytes
|
||||
Db.PatchObjectId <$> Q.saveObject hh hashId ObjectType.Patch bytes
|
||||
|
||||
s2cPatch :: S.Patch -> Transaction C.Branch.Patch
|
||||
s2cPatch (S.Patch termEdits typeEdits) =
|
||||
@ -1204,19 +934,10 @@ termsMentioningType cTypeRef =
|
||||
set <- traverse s2cReferentId sIds
|
||||
pure (Set.fromList set)
|
||||
|
||||
addTypeToIndexForTerm :: S.Referent.Id -> C.Reference -> Transaction ()
|
||||
addTypeToIndexForTerm sTermId cTypeRef = do
|
||||
sTypeRef <- saveReferenceH cTypeRef
|
||||
Q.addToTypeIndex sTypeRef sTermId
|
||||
|
||||
addTypeMentionsToIndexForTerm :: S.Referent.Id -> Set C.Reference -> Transaction ()
|
||||
addTypeMentionsToIndexForTerm sTermId cTypeMentionRefs = do
|
||||
traverse_ (flip Q.addToTypeMentionsIndex sTermId <=< saveReferenceH) cTypeMentionRefs
|
||||
|
||||
-- something kind of funny here. first, we don't need to enumerate all the reference pos if we're just picking one
|
||||
-- second, it would be nice if we could leave these as S.References a little longer
|
||||
-- so that we remember how to blow up if they're missing
|
||||
componentReferencesByPrefix :: OT.ObjectType -> Text -> Maybe C.Reference.Pos -> Transaction [S.Reference.Id]
|
||||
componentReferencesByPrefix :: ObjectType.ObjectType -> Text -> Maybe C.Reference.Pos -> Transaction [S.Reference.Id]
|
||||
componentReferencesByPrefix ot b32prefix pos = do
|
||||
oIds :: [Db.ObjectId] <- Q.objectIdByBase32Prefix ot b32prefix
|
||||
let test = maybe (const True) (==) pos
|
||||
@ -1225,12 +946,12 @@ componentReferencesByPrefix ot b32prefix pos = do
|
||||
|
||||
termReferencesByPrefix :: Text -> Maybe Word64 -> Transaction [C.Reference.Id]
|
||||
termReferencesByPrefix t w =
|
||||
componentReferencesByPrefix OT.TermComponent t w
|
||||
componentReferencesByPrefix ObjectType.TermComponent t w
|
||||
>>= traverse (C.Reference.idH Q.expectPrimaryHashByObjectId)
|
||||
|
||||
declReferencesByPrefix :: Text -> Maybe Word64 -> Transaction [C.Reference.Id]
|
||||
declReferencesByPrefix t w =
|
||||
componentReferencesByPrefix OT.DeclComponent t w
|
||||
componentReferencesByPrefix ObjectType.DeclComponent t w
|
||||
>>= traverse (C.Reference.idH Q.expectPrimaryHashByObjectId)
|
||||
|
||||
termReferentsByPrefix :: Text -> Maybe Word64 -> Transaction [C.Referent.Id]
|
||||
@ -1245,7 +966,7 @@ declReferentsByPrefix ::
|
||||
Maybe ConstructorId ->
|
||||
Transaction [(H.Hash, C.Reference.Pos, C.DeclType, [C.Decl.ConstructorId])]
|
||||
declReferentsByPrefix b32prefix pos cid = do
|
||||
componentReferencesByPrefix OT.DeclComponent b32prefix pos
|
||||
componentReferencesByPrefix ObjectType.DeclComponent b32prefix pos
|
||||
>>= traverse (loadConstructors cid)
|
||||
where
|
||||
loadConstructors ::
|
||||
|
@ -1,7 +1,9 @@
|
||||
module U.Codebase.Sqlite.Patch.Format
|
||||
( PatchFormat (..),
|
||||
PatchLocalIds (..),
|
||||
SyncPatchFormat (..),
|
||||
PatchLocalIds,
|
||||
PatchLocalIds' (..),
|
||||
SyncPatchFormat,
|
||||
SyncPatchFormat' (..),
|
||||
applyPatchDiffs,
|
||||
localPatchToPatch,
|
||||
localPatchDiffToPatchDiff,
|
||||
@ -24,15 +26,20 @@ data PatchFormat
|
||||
= Full PatchLocalIds LocalPatch
|
||||
| Diff PatchObjectId PatchLocalIds LocalPatchDiff
|
||||
|
||||
data PatchLocalIds = LocalIds
|
||||
{ patchTextLookup :: Vector TextId,
|
||||
patchHashLookup :: Vector HashId,
|
||||
patchDefnLookup :: Vector ObjectId
|
||||
type PatchLocalIds = PatchLocalIds' TextId HashId ObjectId
|
||||
|
||||
data PatchLocalIds' t h d = LocalIds
|
||||
{ patchTextLookup :: Vector t,
|
||||
patchHashLookup :: Vector h,
|
||||
patchDefnLookup :: Vector d
|
||||
}
|
||||
|
||||
data SyncPatchFormat
|
||||
= SyncFull PatchLocalIds ByteString
|
||||
| SyncDiff PatchObjectId PatchLocalIds ByteString
|
||||
type SyncPatchFormat = SyncPatchFormat' PatchObjectId TextId HashId ObjectId
|
||||
|
||||
data SyncPatchFormat' parent text hash defn
|
||||
= SyncFull (PatchLocalIds' text hash defn) ByteString
|
||||
| -- | p is the identity of the thing that the diff is relative to
|
||||
SyncDiff parent (PatchLocalIds' text hash defn) ByteString
|
||||
|
||||
-- | Apply a list of patch diffs to a patch, left to right.
|
||||
applyPatchDiffs :: Patch -> [PatchDiff] -> Patch
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,14 +4,13 @@
|
||||
|
||||
module U.Codebase.Sqlite.Reference where
|
||||
|
||||
import Data.Tuple.Only (Only (..))
|
||||
import U.Codebase.Reference (Id' (Id), Reference' (ReferenceBuiltin, ReferenceDerived))
|
||||
import U.Codebase.Sqlite.DbId (HashId, ObjectId, TextId)
|
||||
import U.Codebase.Sqlite.LocalIds (LocalDefnId, LocalHashId, LocalTextId)
|
||||
import U.Codebase.Sqlite.Orphans ()
|
||||
import U.Util.Base32Hex
|
||||
import Unison.Prelude
|
||||
import Unison.Sqlite (FromField, FromRow (fromRow), RowParser, SQLData (SQLNull), ToField, ToRow (toRow), field)
|
||||
import Unison.Sqlite (FromField, FromRow (fromRow), Only (..), RowParser, SQLData (SQLNull), ToField, ToRow (toRow), field)
|
||||
|
||||
type Reference = Reference' TextId ObjectId
|
||||
|
||||
|
@ -6,13 +6,12 @@
|
||||
module U.Codebase.Sqlite.Referent where
|
||||
|
||||
import Control.Applicative (liftA3)
|
||||
import Data.Tuple.Only (Only (..))
|
||||
import qualified U.Codebase.Reference as Reference
|
||||
import U.Codebase.Referent (Id', Referent')
|
||||
import qualified U.Codebase.Referent as Referent
|
||||
import U.Codebase.Sqlite.DbId (ObjectId)
|
||||
import qualified U.Codebase.Sqlite.Reference as Sqlite
|
||||
import Unison.Sqlite (FromRow (..), SQLData (..), ToField (toField), ToRow (..), field)
|
||||
import Unison.Sqlite (FromRow (..), Only (..), SQLData (..), ToField (toField), ToRow (..), field)
|
||||
|
||||
type Referent = Referent' Sqlite.Reference Sqlite.Reference
|
||||
|
||||
|
@ -14,11 +14,9 @@ import Data.Bytes.Get (MonadGet, getByteString, getWord8, runGetS)
|
||||
import Data.Bytes.Put (MonadPut, putByteString, putWord8)
|
||||
import Data.Bytes.Serial (SerialEndian (serializeBE), deserialize, deserializeBE, serialize)
|
||||
import Data.Bytes.VarInt (VarInt (VarInt), unVarInt)
|
||||
import Data.Int (Int64)
|
||||
import Data.List (elemIndex)
|
||||
import qualified Data.Set as Set
|
||||
import Data.Word (Word64)
|
||||
import Debug.Trace (trace)
|
||||
import Data.Vector (Vector)
|
||||
import qualified U.Codebase.Decl as Decl
|
||||
import U.Codebase.Kind (Kind)
|
||||
import qualified U.Codebase.Kind as Kind
|
||||
@ -29,7 +27,9 @@ import qualified U.Codebase.Referent as Referent
|
||||
import qualified U.Codebase.Sqlite.Branch.Diff as BranchDiff
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as BranchFormat
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as BranchFull
|
||||
import qualified U.Codebase.Sqlite.Causal as Causal
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as DeclFormat
|
||||
import qualified U.Codebase.Sqlite.Entity as Entity
|
||||
import U.Codebase.Sqlite.LocalIds (LocalIds, LocalIds' (..), LocalTextId, WatchLocalIds)
|
||||
import qualified U.Codebase.Sqlite.Patch.Diff as PatchDiff
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as PatchFormat
|
||||
@ -37,12 +37,18 @@ import qualified U.Codebase.Sqlite.Patch.Full as PatchFull
|
||||
import qualified U.Codebase.Sqlite.Patch.TermEdit as TermEdit
|
||||
import qualified U.Codebase.Sqlite.Patch.TypeEdit as TypeEdit
|
||||
import U.Codebase.Sqlite.Symbol (Symbol (..))
|
||||
import U.Codebase.Sqlite.TempEntity (TempEntity)
|
||||
import qualified U.Codebase.Sqlite.TempEntity as TempEntity
|
||||
import qualified U.Codebase.Sqlite.Term.Format as TermFormat
|
||||
import qualified U.Codebase.Term as Term
|
||||
import qualified U.Codebase.Type as Type
|
||||
import qualified U.Core.ABT as ABT
|
||||
import qualified U.Util.Base32Hex as Base32Hex
|
||||
import U.Util.Hash32 (Hash32)
|
||||
import qualified U.Util.Hash32 as Hash32
|
||||
import qualified U.Util.Monoid as Monoid
|
||||
import U.Util.Serialization hiding (debug)
|
||||
import Unison.Prelude
|
||||
import Prelude hiding (getChar, putChar)
|
||||
|
||||
debug :: Bool
|
||||
@ -108,15 +114,22 @@ getABT getVar getA getF = getList getVar >>= go []
|
||||
_ -> unknownTag "getABT" tag
|
||||
|
||||
putLocalIds :: (MonadPut m, Integral t, Bits t, Integral d, Bits d) => LocalIds' t d -> m ()
|
||||
putLocalIds LocalIds {..} = do
|
||||
putFoldable putVarInt textLookup
|
||||
putFoldable putVarInt defnLookup
|
||||
putLocalIds = putLocalIdsWith putVarInt putVarInt
|
||||
|
||||
putLocalIdsWith :: (MonadPut m) => (t -> m ()) -> (d -> m ()) -> LocalIds' t d -> m ()
|
||||
putLocalIdsWith putText putDefn LocalIds {textLookup, defnLookup} = do
|
||||
putFoldable putText textLookup
|
||||
putFoldable putDefn defnLookup
|
||||
|
||||
getLocalIds :: MonadGet m => m LocalIds
|
||||
getLocalIds = LocalIds <$> getVector getVarInt <*> getVector getVarInt
|
||||
getLocalIds = getLocalIdsWith getVarInt getVarInt
|
||||
|
||||
getWatchLocalIds :: MonadGet m => m WatchLocalIds
|
||||
getWatchLocalIds = LocalIds <$> getVector getVarInt <*> getVector getVarInt
|
||||
getWatchLocalIds = getLocalIdsWith getVarInt getVarInt
|
||||
|
||||
getLocalIdsWith :: MonadGet m => m t -> m d -> m (LocalIds' t d)
|
||||
getLocalIdsWith getText getDefn =
|
||||
LocalIds <$> getVector getText <*> getVector getDefn
|
||||
|
||||
putUnit :: Applicative m => () -> m ()
|
||||
putUnit _ = pure ()
|
||||
@ -252,6 +265,9 @@ getTermComponent =
|
||||
TermFormat.LocallyIndexedComponent
|
||||
<$> getFramedArray (getTuple3 getLocalIds (getFramed getTerm) getTType)
|
||||
|
||||
getTermAndType :: MonadGet m => m (TermFormat.Term, TermFormat.Type)
|
||||
getTermAndType = (,) <$> getFramed getTerm <*> getTType
|
||||
|
||||
getTerm :: MonadGet m => m TermFormat.Term
|
||||
getTerm = getABT getSymbol getUnit getF
|
||||
where
|
||||
@ -633,7 +649,25 @@ getBranchLocalIds =
|
||||
<*> getVector getVarInt
|
||||
<*> getVector (getPair getVarInt getVarInt)
|
||||
|
||||
decomposeComponent :: MonadGet m => m [(LocalIds, BS.ByteString)]
|
||||
decomposeTermFormat :: MonadGet m => m TermFormat.SyncTermFormat
|
||||
decomposeTermFormat =
|
||||
getWord8 >>= \case
|
||||
0 ->
|
||||
TermFormat.SyncTerm
|
||||
. TermFormat.SyncLocallyIndexedComponent
|
||||
<$> decomposeComponent
|
||||
tag -> error $ "todo: unknown term format tag " ++ show tag
|
||||
|
||||
decomposeDeclFormat :: MonadGet m => m DeclFormat.SyncDeclFormat
|
||||
decomposeDeclFormat =
|
||||
getWord8 >>= \case
|
||||
0 ->
|
||||
DeclFormat.SyncDecl
|
||||
. DeclFormat.SyncLocallyIndexedComponent
|
||||
<$> decomposeComponent
|
||||
tag -> error $ "todo: unknown term format tag " ++ show tag
|
||||
|
||||
decomposeComponent :: MonadGet m => m (Vector (LocalIds, BS.ByteString))
|
||||
decomposeComponent = do
|
||||
offsets <- getList (getVarInt @_ @Int)
|
||||
componentBytes <- getByteString (last offsets)
|
||||
@ -643,7 +677,17 @@ decomposeComponent = do
|
||||
split = (,) <$> getLocalIds <*> getRemainingByteString
|
||||
Monoid.foldMapM get1 (zip offsets (tail offsets))
|
||||
|
||||
recomposeComponent :: MonadPut m => [(LocalIds, BS.ByteString)] -> m ()
|
||||
recomposeTermFormat :: MonadPut m => TermFormat.SyncTermFormat -> m ()
|
||||
recomposeTermFormat = \case
|
||||
TermFormat.SyncTerm (TermFormat.SyncLocallyIndexedComponent x) ->
|
||||
putWord8 0 >> recomposeComponent x
|
||||
|
||||
recomposeDeclFormat :: MonadPut m => DeclFormat.SyncDeclFormat -> m ()
|
||||
recomposeDeclFormat = \case
|
||||
DeclFormat.SyncDecl (DeclFormat.SyncLocallyIndexedComponent x) ->
|
||||
putWord8 0 >> recomposeComponent x
|
||||
|
||||
recomposeComponent :: MonadPut m => Vector (LocalIds, BS.ByteString) -> m ()
|
||||
recomposeComponent = putFramedArray \(localIds, bytes) -> do
|
||||
putLocalIds localIds
|
||||
putByteString bytes
|
||||
@ -686,6 +730,126 @@ recomposeBranchFormat = \case
|
||||
BranchFormat.SyncDiff id li bs ->
|
||||
putWord8 1 *> putVarInt id *> putBranchLocalIds li *> putByteString bs
|
||||
|
||||
putTempEntity :: MonadPut m => TempEntity -> m ()
|
||||
putTempEntity = \case
|
||||
Entity.TC tc -> case tc of
|
||||
TermFormat.SyncTerm term ->
|
||||
putWord8 0 *> putSyncTerm term
|
||||
Entity.DC dc -> case dc of
|
||||
DeclFormat.SyncDecl decl ->
|
||||
putWord8 0 *> putSyncDecl decl
|
||||
Entity.P p -> case p of
|
||||
PatchFormat.SyncFull lids bytes ->
|
||||
putWord8 0 *> putSyncFullPatch lids bytes
|
||||
PatchFormat.SyncDiff parent lids bytes ->
|
||||
putWord8 1 *> putSyncDiffPatch parent lids bytes
|
||||
Entity.N n -> case n of
|
||||
BranchFormat.SyncFull lids bytes ->
|
||||
putWord8 0 *> putSyncFullNamespace lids bytes
|
||||
BranchFormat.SyncDiff parent lids bytes ->
|
||||
putWord8 1 *> putSyncDiffNamespace parent lids bytes
|
||||
Entity.C gdc ->
|
||||
putSyncCausal gdc
|
||||
where
|
||||
putHash32 = putText . Hash32.toText
|
||||
putPatchLocalIds PatchFormat.LocalIds {patchTextLookup, patchHashLookup, patchDefnLookup} = do
|
||||
putFoldable putText patchTextLookup
|
||||
putFoldable putHash32 patchHashLookup
|
||||
putFoldable putHash32 patchDefnLookup
|
||||
putNamespaceLocalIds BranchFormat.LocalIds {branchTextLookup, branchDefnLookup, branchPatchLookup, branchChildLookup} = do
|
||||
putFoldable putText branchTextLookup
|
||||
putFoldable putHash32 branchDefnLookup
|
||||
putFoldable putHash32 branchPatchLookup
|
||||
putFoldable (putPair putHash32 putHash32) branchChildLookup
|
||||
putSyncCausal Causal.SyncCausalFormat {valueHash, parents} = do
|
||||
putHash32 valueHash
|
||||
putFoldable putHash32 parents
|
||||
putSyncFullPatch lids bytes = do
|
||||
putPatchLocalIds lids
|
||||
putFramedByteString bytes
|
||||
putSyncDiffPatch parent lids bytes = do
|
||||
putHash32 parent
|
||||
putPatchLocalIds lids
|
||||
putFramedByteString bytes
|
||||
putSyncFullNamespace lids bytes = do
|
||||
putNamespaceLocalIds lids
|
||||
putFramedByteString bytes
|
||||
putSyncDiffNamespace parent lids bytes = do
|
||||
putHash32 parent
|
||||
putNamespaceLocalIds lids
|
||||
putFramedByteString bytes
|
||||
putSyncTerm (TermFormat.SyncLocallyIndexedComponent vec) =
|
||||
-- we're not leaving ourselves the ability to skip over the localIds
|
||||
-- when deserializing, because we don't think we need to (and it adds a
|
||||
-- little overhead.)
|
||||
flip putFoldable vec \(localIds, bytes) -> do
|
||||
putLocalIdsWith putText putHash32 localIds
|
||||
putFramedByteString bytes
|
||||
putSyncDecl (DeclFormat.SyncLocallyIndexedComponent vec) =
|
||||
flip putFoldable vec \(localIds, bytes) -> do
|
||||
putLocalIdsWith putText putHash32 localIds
|
||||
putFramedByteString bytes
|
||||
|
||||
getHash32 :: MonadGet m => m Hash32
|
||||
getHash32 = Hash32.UnsafeFromBase32Hex . Base32Hex.UnsafeFromText <$> getText
|
||||
|
||||
getTempTermFormat :: MonadGet m => m TempEntity.TempTermFormat
|
||||
getTempTermFormat =
|
||||
getWord8 >>= \case
|
||||
0 ->
|
||||
TermFormat.SyncTerm . TermFormat.SyncLocallyIndexedComponent
|
||||
<$> getVector
|
||||
( getPair
|
||||
(getLocalIdsWith getText getHash32)
|
||||
getFramedByteString
|
||||
)
|
||||
tag -> unknownTag "getTempTermFormat" tag
|
||||
|
||||
getTempDeclFormat :: MonadGet m => m TempEntity.TempDeclFormat
|
||||
getTempDeclFormat =
|
||||
getWord8 >>= \case
|
||||
0 ->
|
||||
DeclFormat.SyncDecl . DeclFormat.SyncLocallyIndexedComponent
|
||||
<$> getVector
|
||||
( getPair
|
||||
(getLocalIdsWith getText getHash32)
|
||||
getFramedByteString
|
||||
)
|
||||
tag -> unknownTag "getTempDeclFormat" tag
|
||||
|
||||
getTempPatchFormat :: MonadGet m => m TempEntity.TempPatchFormat
|
||||
getTempPatchFormat =
|
||||
getWord8 >>= \case
|
||||
0 -> PatchFormat.SyncFull <$> getPatchLocalIds <*> getFramedByteString
|
||||
1 -> PatchFormat.SyncDiff <$> getHash32 <*> getPatchLocalIds <*> getFramedByteString
|
||||
tag -> unknownTag "getTempPatchFormat" tag
|
||||
where
|
||||
getPatchLocalIds =
|
||||
PatchFormat.LocalIds
|
||||
<$> getVector getText
|
||||
<*> getVector getHash32
|
||||
<*> getVector getHash32
|
||||
|
||||
getTempNamespaceFormat :: MonadGet m => m TempEntity.TempNamespaceFormat
|
||||
getTempNamespaceFormat =
|
||||
getWord8 >>= \case
|
||||
0 -> BranchFormat.SyncFull <$> getBranchLocalIds <*> getFramedByteString
|
||||
1 -> BranchFormat.SyncDiff <$> getHash32 <*> getBranchLocalIds <*> getFramedByteString
|
||||
tag -> unknownTag "getTempNamespaceFormat" tag
|
||||
where
|
||||
getBranchLocalIds =
|
||||
BranchFormat.LocalIds
|
||||
<$> getVector getText
|
||||
<*> getVector getHash32
|
||||
<*> getVector getHash32
|
||||
<*> getVector (getPair getHash32 getHash32)
|
||||
|
||||
getTempCausalFormat :: MonadGet m => m TempEntity.TempCausalFormat
|
||||
getTempCausalFormat =
|
||||
Causal.SyncCausalFormat
|
||||
<$> getHash32
|
||||
<*> getVector getHash32
|
||||
|
||||
getSymbol :: MonadGet m => m Symbol
|
||||
getSymbol = Symbol <$> getVarInt <*> getText
|
||||
|
||||
|
@ -14,14 +14,17 @@ import Control.Monad.Validate (ValidateT, runValidateT)
|
||||
import qualified Control.Monad.Validate as Validate
|
||||
import Data.Bifunctor (bimap)
|
||||
import Data.Bitraversable (bitraverse)
|
||||
import Data.Bytes.Get (getWord8, runGetS)
|
||||
import Data.Bytes.Put (putWord8, runPutS)
|
||||
import Data.Bytes.Get (runGetS)
|
||||
import Data.Bytes.Put (runPutS)
|
||||
import Data.List.Extra (nubOrd)
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Vector as Vector
|
||||
import Data.Void (Void)
|
||||
import qualified U.Codebase.Reference as Reference
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as BL
|
||||
import U.Codebase.Sqlite.DbId
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as DeclFormat
|
||||
import U.Codebase.Sqlite.HashHandle (HashHandle)
|
||||
import qualified U.Codebase.Sqlite.LocalIds as L
|
||||
import qualified U.Codebase.Sqlite.ObjectType as OT
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as PL
|
||||
@ -31,6 +34,7 @@ import qualified U.Codebase.Sqlite.Reference as Sqlite.Reference
|
||||
import qualified U.Codebase.Sqlite.Referent as Sqlite.Referent
|
||||
import qualified U.Codebase.Sqlite.Serialization as S
|
||||
import qualified U.Codebase.Sqlite.Term.Format as TL
|
||||
import qualified U.Codebase.Sqlite.Term.Format as TermFormat
|
||||
import U.Codebase.Sync (Sync (Sync), TrySyncResult)
|
||||
import qualified U.Codebase.Sync as Sync
|
||||
import qualified U.Codebase.WatchKind as WK
|
||||
@ -86,18 +90,20 @@ sync22 ::
|
||||
( MonadIO m,
|
||||
MonadError Error m
|
||||
) =>
|
||||
HashHandle ->
|
||||
Env m ->
|
||||
IO (Sync m Entity)
|
||||
sync22 Env {runSrc, runDest, idCacheSize = size} = do
|
||||
sync22 hh Env {runSrc, runDest, idCacheSize = size} = do
|
||||
tCache <- Cache.semispaceCache size
|
||||
hCache <- Cache.semispaceCache size
|
||||
oCache <- Cache.semispaceCache size
|
||||
cCache <- Cache.semispaceCache size
|
||||
pure $ Sync (trySync runSrc runDest tCache hCache oCache cCache)
|
||||
pure $ Sync (trySync hh runSrc runDest tCache hCache oCache cCache)
|
||||
|
||||
trySync ::
|
||||
forall m.
|
||||
(MonadIO m, MonadError Error m) =>
|
||||
HashHandle ->
|
||||
(forall a. Transaction a -> m a) ->
|
||||
(forall a. Transaction a -> m a) ->
|
||||
Cache TextId TextId ->
|
||||
@ -106,7 +112,7 @@ trySync ::
|
||||
Cache CausalHashId CausalHashId ->
|
||||
Entity ->
|
||||
m (TrySyncResult Entity)
|
||||
trySync runSrc runDest tCache hCache oCache cCache = \case
|
||||
trySync hh runSrc runDest tCache hCache oCache cCache = \case
|
||||
-- for causals, we need to get the value_hash_id of the thingo
|
||||
-- - maybe enqueue their parents
|
||||
-- - enqueue the self_ and value_ hashes
|
||||
@ -123,9 +129,7 @@ trySync runSrc runDest tCache hCache oCache cCache = \case
|
||||
parents' :: [CausalHashId] <- findParents' chId
|
||||
bhId' <- lift $ syncBranchHashId bhId
|
||||
chId' <- lift $ syncCausalHashId chId
|
||||
(lift . runDest) do
|
||||
Q.saveCausal chId' bhId'
|
||||
Q.saveCausalParents chId' parents'
|
||||
lift (runDest (Q.saveCausal hh chId' bhId' parents'))
|
||||
|
||||
case result of
|
||||
Left deps -> pure . Sync.Missing $ toList deps
|
||||
@ -142,91 +146,93 @@ trySync runSrc runDest tCache hCache oCache cCache = \case
|
||||
result <- runValidateT @(Set Entity) @m @ObjectId case objType of
|
||||
OT.TermComponent -> do
|
||||
-- split up the localIds (parsed), term, and type blobs
|
||||
-- note: this whole business with `fmt` is pretty weird, and will need to be
|
||||
-- revisited when there are more formats.
|
||||
-- (or maybe i'll learn something by implementing sync for patches and namespaces,
|
||||
-- which have two formats already)
|
||||
(fmt, unzip -> (localIds, bytes)) <-
|
||||
lift case flip runGetS bytes do
|
||||
tag <- getWord8
|
||||
component <- S.decomposeComponent
|
||||
pure (tag, component) of
|
||||
Right x -> pure x
|
||||
Left s -> throwError $ DecodeError ErrTermComponent bytes s
|
||||
-- iterate through the local ids looking for missing deps;
|
||||
-- then either enqueue the missing deps, or proceed to move the object
|
||||
when debug $ traceM $ "LocalIds for Source " ++ show oId ++ ": " ++ show localIds
|
||||
localIds' <- traverse syncLocalIds localIds
|
||||
when debug $ traceM $ "LocalIds for Dest: " ++ show localIds'
|
||||
-- reassemble and save the reindexed term
|
||||
let bytes' =
|
||||
runPutS $
|
||||
putWord8 fmt >> S.recomposeComponent (zip localIds' bytes)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
lift do
|
||||
-- copy reference-specific stuff
|
||||
for_ [0 .. length localIds - 1] \(fromIntegral -> idx) -> do
|
||||
let ref = Reference.Id oId idx
|
||||
refH = Reference.Id hId idx
|
||||
ref' = Reference.Id oId' idx
|
||||
-- sync watch results
|
||||
for_ [WK.TestWatch] \wk ->
|
||||
syncWatch wk refH
|
||||
syncDependenciesIndex ref ref'
|
||||
syncTypeIndex oId oId'
|
||||
syncTypeMentionsIndex oId oId'
|
||||
pure oId'
|
||||
case flip runGetS bytes S.decomposeTermFormat of
|
||||
Left s -> throwError $ DecodeError ErrTermComponent bytes s
|
||||
Right
|
||||
( TermFormat.SyncTerm
|
||||
( TermFormat.SyncLocallyIndexedComponent
|
||||
(Vector.unzip -> (localIds, bytes))
|
||||
)
|
||||
) -> do
|
||||
-- iterate through the local ids looking for missing deps;
|
||||
-- then either enqueue the missing deps, or proceed to move the object
|
||||
when debug $ traceM $ "LocalIds for Source " ++ show oId ++ ": " ++ show localIds
|
||||
localIds' <- traverse syncLocalIds localIds
|
||||
when debug $ traceM $ "LocalIds for Dest: " ++ show localIds'
|
||||
-- reassemble and save the reindexed term
|
||||
let bytes' =
|
||||
runPutS
|
||||
. S.recomposeTermFormat
|
||||
. TermFormat.SyncTerm
|
||||
. TermFormat.SyncLocallyIndexedComponent
|
||||
$ Vector.zip localIds' bytes
|
||||
lift do
|
||||
oId' <- runDest $ Q.saveObject hh hId' objType bytes'
|
||||
-- copy reference-specific stuff
|
||||
for_ [0 .. length localIds - 1] \(fromIntegral -> idx) -> do
|
||||
let ref = Reference.Id oId idx
|
||||
refH = Reference.Id hId idx
|
||||
ref' = Reference.Id oId' idx
|
||||
-- sync watch results
|
||||
for_ [WK.TestWatch] \wk ->
|
||||
syncWatch wk refH
|
||||
syncDependenciesIndex ref ref'
|
||||
syncTypeIndex oId oId'
|
||||
syncTypeMentionsIndex oId oId'
|
||||
pure oId'
|
||||
OT.DeclComponent -> do
|
||||
-- split up the localIds (parsed), decl blobs
|
||||
(fmt, unzip -> (localIds, declBytes)) <-
|
||||
case flip runGetS bytes do
|
||||
tag <- getWord8
|
||||
component <- S.decomposeComponent
|
||||
pure (tag, component) of
|
||||
Right x -> pure x
|
||||
Left s -> throwError $ DecodeError ErrDeclComponent bytes s
|
||||
-- iterate through the local ids looking for missing deps;
|
||||
-- then either enqueue the missing deps, or proceed to move the object
|
||||
localIds' <- traverse syncLocalIds localIds
|
||||
-- reassemble and save the reindexed term
|
||||
let bytes' =
|
||||
runPutS $
|
||||
putWord8 fmt
|
||||
>> S.recomposeComponent (zip localIds' declBytes)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
lift do
|
||||
-- copy per-element-of-the-component stuff
|
||||
for_ [0 .. length localIds - 1] \(fromIntegral -> idx) -> do
|
||||
let ref = Reference.Id oId idx
|
||||
ref' = Reference.Id oId' idx
|
||||
syncDependenciesIndex ref ref'
|
||||
syncTypeIndex oId oId'
|
||||
syncTypeMentionsIndex oId oId'
|
||||
pure oId'
|
||||
case flip runGetS bytes S.decomposeDeclFormat of
|
||||
Left s -> throwError $ DecodeError ErrDeclComponent bytes s
|
||||
Right
|
||||
( DeclFormat.SyncDecl
|
||||
( DeclFormat.SyncLocallyIndexedComponent
|
||||
(Vector.unzip -> (localIds, declBytes))
|
||||
)
|
||||
) -> do
|
||||
-- iterate through the local ids looking for missing deps;
|
||||
-- then either enqueue the missing deps, or proceed to move the object
|
||||
localIds' <- traverse syncLocalIds localIds
|
||||
-- reassemble and save the reindexed term
|
||||
let bytes' =
|
||||
runPutS . S.recomposeDeclFormat
|
||||
. DeclFormat.SyncDecl
|
||||
. DeclFormat.SyncLocallyIndexedComponent
|
||||
$ Vector.zip localIds' declBytes
|
||||
lift do
|
||||
oId' <- runDest $ Q.saveObject hh hId' objType bytes'
|
||||
-- copy per-element-of-the-component stuff
|
||||
for_ [0 .. length localIds - 1] \(fromIntegral -> idx) -> do
|
||||
let ref = Reference.Id oId idx
|
||||
ref' = Reference.Id oId' idx
|
||||
syncDependenciesIndex ref ref'
|
||||
syncTypeIndex oId oId'
|
||||
syncTypeMentionsIndex oId oId'
|
||||
pure oId'
|
||||
OT.Namespace -> case flip runGetS bytes S.decomposeBranchFormat of
|
||||
Right (BL.SyncFull ids body) -> do
|
||||
ids' <- syncBranchLocalIds ids
|
||||
let bytes' = runPutS $ S.recomposeBranchFormat (BL.SyncFull ids' body)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
oId' <- lift . runDest $ Q.saveObject hh hId' objType bytes'
|
||||
pure oId'
|
||||
Right (BL.SyncDiff boId ids body) -> do
|
||||
boId' <- syncBranchObjectId boId
|
||||
ids' <- syncBranchLocalIds ids
|
||||
let bytes' = runPutS $ S.recomposeBranchFormat (BL.SyncDiff boId' ids' body)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
oId' <- lift . runDest $ Q.saveObject hh hId' objType bytes'
|
||||
pure oId'
|
||||
Left s -> throwError $ DecodeError ErrBranchFormat bytes s
|
||||
OT.Patch -> case flip runGetS bytes S.decomposePatchFormat of
|
||||
Right (PL.SyncFull ids body) -> do
|
||||
ids' <- syncPatchLocalIds ids
|
||||
let bytes' = runPutS $ S.recomposePatchFormat (PL.SyncFull ids' body)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
oId' <- lift . runDest $ Q.saveObject hh hId' objType bytes'
|
||||
pure oId'
|
||||
Right (PL.SyncDiff poId ids body) -> do
|
||||
poId' <- syncPatchObjectId poId
|
||||
ids' <- syncPatchLocalIds ids
|
||||
let bytes' = runPutS $ S.recomposePatchFormat (PL.SyncDiff poId' ids' body)
|
||||
oId' <- lift . runDest $ Q.saveObject hId' objType bytes'
|
||||
oId' <- lift . runDest $ Q.saveObject hh hId' objType bytes'
|
||||
pure oId'
|
||||
Left s -> throwError $ DecodeError ErrPatchFormat bytes s
|
||||
case result of
|
||||
|
37
codebase2/codebase-sqlite/U/Codebase/Sqlite/TempEntity.hs
Normal file
37
codebase2/codebase-sqlite/U/Codebase/Sqlite/TempEntity.hs
Normal file
@ -0,0 +1,37 @@
|
||||
module U.Codebase.Sqlite.TempEntity where
|
||||
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as Namespace
|
||||
import qualified U.Codebase.Sqlite.Causal as Causal
|
||||
import qualified U.Codebase.Sqlite.Decl.Format as Decl
|
||||
import qualified U.Codebase.Sqlite.Entity as Entity
|
||||
import U.Codebase.Sqlite.LocalIds (LocalIds')
|
||||
import qualified U.Codebase.Sqlite.Patch.Format as Patch
|
||||
import qualified U.Codebase.Sqlite.Term.Format as Term
|
||||
import U.Util.Hash32 (Hash32)
|
||||
import Unison.Prelude
|
||||
|
||||
-- |
|
||||
-- data TempEntity
|
||||
-- = TC TempTermFormat
|
||||
-- | DC TempDeclFormat
|
||||
-- | N TempNamespaceFormat
|
||||
-- | P TempPatchFormat
|
||||
-- | C TempCausalFormat
|
||||
type TempEntity =
|
||||
Entity.SyncEntity' Text Hash32 Hash32 Hash32 Hash32 Hash32 Hash32
|
||||
|
||||
type TempLocalIds = LocalIds' Text Hash32
|
||||
|
||||
type TempTermFormat = Term.SyncTermFormat' Text Hash32
|
||||
|
||||
type TempDeclFormat = Decl.SyncDeclFormat' Text Hash32
|
||||
|
||||
type TempPatchFormat = Patch.SyncPatchFormat' Hash32 Text Hash32 Hash32
|
||||
|
||||
type TempPatchLocalIds = Patch.PatchLocalIds' Text Hash32 Hash32
|
||||
|
||||
type TempNamespaceFormat = Namespace.SyncBranchFormat' Hash32 Text Hash32 Hash32 (Hash32, Hash32)
|
||||
|
||||
type TempNamespaceLocalIds = Namespace.BranchLocalIds' Text Hash32 Hash32 (Hash32, Hash32)
|
||||
|
||||
type TempCausalFormat = Causal.SyncCausalFormat' Hash32 Hash32
|
@ -0,0 +1,20 @@
|
||||
module U.Codebase.Sqlite.TempEntityType where
|
||||
|
||||
import Unison.Sqlite (FromField (..), SQLData (SQLInteger), ToField (..))
|
||||
|
||||
-- | Don't reorder these, they are part of the database,
|
||||
-- and the ToField and FromField implementation currently
|
||||
-- depends on the derived Enum implementation.
|
||||
data TempEntityType
|
||||
= TermComponentType -- 0
|
||||
| DeclComponentType -- 1
|
||||
| NamespaceType -- 2
|
||||
| PatchType -- 3
|
||||
| CausalType -- 4
|
||||
deriving (Eq, Ord, Show, Enum)
|
||||
|
||||
instance ToField TempEntityType where
|
||||
toField = SQLInteger . fromIntegral . fromEnum
|
||||
|
||||
instance FromField TempEntityType where
|
||||
fromField = fmap toEnum . fromField
|
@ -38,10 +38,13 @@ type TypeLink = TypeRef
|
||||
-- * The term's type, also with internal references to local id.
|
||||
type LocallyIndexedComponent = LocallyIndexedComponent' TextId ObjectId
|
||||
|
||||
newtype LocallyIndexedComponent' t d
|
||||
= LocallyIndexedComponent (Vector (LocalIds' t d, Term, Type))
|
||||
newtype LocallyIndexedComponent' t d = LocallyIndexedComponent
|
||||
{unLocallyIndexedComponent :: Vector (LocalIds' t d, Term, Type)}
|
||||
deriving (Show)
|
||||
|
||||
newtype SyncLocallyIndexedComponent' t d
|
||||
= SyncLocallyIndexedComponent (Vector (LocalIds' t d, ByteString))
|
||||
|
||||
{-
|
||||
message = "hello, world" -> ABT { ... { Term.F.Text "hello, world" } } -> hashes to (#abc, 0)
|
||||
program = printLine message -> ABT { ... { Term.F.App (ReferenceBuiltin ##io.PrintLine) (Reference #abc 0) } } -> hashes to (#def, 0)
|
||||
@ -109,8 +112,13 @@ type FTT = Type.F' Sqlite.Reference
|
||||
|
||||
type TypeOfTerm = ABT.Term FTT Symbol ()
|
||||
|
||||
data TermFormat
|
||||
= Term LocallyIndexedComponent
|
||||
type TermFormat = TermFormat' TextId ObjectId
|
||||
|
||||
data TermFormat' t d = Term (LocallyIndexedComponent' t d)
|
||||
|
||||
type SyncTermFormat = SyncTermFormat' TextId ObjectId
|
||||
|
||||
data SyncTermFormat' t d = SyncTerm (SyncLocallyIndexedComponent' t d)
|
||||
|
||||
data WatchResultFormat
|
||||
= WatchResult WatchLocalIds Term
|
||||
|
@ -22,6 +22,7 @@ dependencies:
|
||||
- generic-lens
|
||||
- monad-validate
|
||||
- mtl
|
||||
- nonempty-containers
|
||||
- safe
|
||||
- text
|
||||
- transformers
|
||||
@ -31,6 +32,8 @@ dependencies:
|
||||
- unison-prelude
|
||||
- unison-sqlite
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
- unison-util-base32hex-orphans-sqlite
|
||||
- unison-util-serialization
|
||||
- unison-util-term
|
||||
- unliftio
|
||||
@ -49,6 +52,7 @@ default-extensions:
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- DuplicateRecordFields
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- FunctionalDependencies
|
||||
|
61
codebase2/codebase-sqlite/sql/001-temp-entity-tables.sql
Normal file
61
codebase2/codebase-sqlite/sql/001-temp-entity-tables.sql
Normal file
@ -0,0 +1,61 @@
|
||||
create table temp_entity_type_description (
|
||||
id integer primary key not null,
|
||||
description text unique not null
|
||||
);
|
||||
insert into temp_entity_type_description values
|
||||
(0, 'Term Component'),
|
||||
(1, 'Decl Component'),
|
||||
(2, 'Namespace'),
|
||||
(3, 'Patch'),
|
||||
(4, 'Causal');
|
||||
|
||||
-- A "temp entity" is a term/decl/namespace/patch/causal that we cannot store in the database proper due to missing
|
||||
-- dependencies.
|
||||
--
|
||||
-- The existence of each `temp_entity` row implies the existence of one or more corresponding
|
||||
-- `temp_entity_missing_dependency` rows: it does not make sense to make a `temp_entity` row for a thing that has no
|
||||
-- missing dependencies!
|
||||
--
|
||||
-- Similarly, each `temp_entity` row implies we do not have the entity in the database proper. When and if we *do* store
|
||||
-- an entity proper (after storing all of its dependencies), we should always atomically delete the corresponding
|
||||
-- `temp_entity` row, if any.
|
||||
create table temp_entity (
|
||||
hash text primary key not null,
|
||||
blob bytes not null,
|
||||
type_id integer not null references temp_entity_type_description(id)
|
||||
);
|
||||
|
||||
-- A many-to-many relationship between `temp_entity` (entities we can't yet store due to missing dependencies), and the
|
||||
-- non-empty set of hashes of each entity's dependencies.
|
||||
--
|
||||
-- We store with each missing dependency the JWT that Unison Share provided us to download that dependency. For
|
||||
-- downloading a particular dependency #bar, we only need one JWT, even if it's in the table multiple times. (In fact,
|
||||
-- in this case, there is one "best" JWT - the one with the latest expiry time).
|
||||
--
|
||||
-- The JWTs are also encoded in the local ids part of entity itself (`temp_entity.blob`), but we don't want to have to
|
||||
-- keep going back there there to decode during a pull.
|
||||
--
|
||||
-- For example, if we wanted to store term #foo, but couldn't because it depends on term #bar which we don't have yet,
|
||||
-- we would end up with the following rows.
|
||||
--
|
||||
-- temp_entity
|
||||
-- +------------------------+
|
||||
-- | hash | blob | type_id |
|
||||
-- |========================|
|
||||
-- | #foo | ... | 0 (term) |
|
||||
-- +------------------------+
|
||||
--
|
||||
-- temp_entity_missing_dependency
|
||||
-- +----------------------------------------+
|
||||
-- | dependent | dependency | dependencyJwt |
|
||||
-- |========================================|
|
||||
-- | #foo | #bar | aT.Eb.cx |
|
||||
-- +----------------------------------------+
|
||||
create table temp_entity_missing_dependency (
|
||||
dependent text not null references temp_entity(hash),
|
||||
dependency text not null,
|
||||
dependencyJwt text not null,
|
||||
unique (dependent, dependency)
|
||||
);
|
||||
create index temp_entity_missing_dependency_ix_dependent on temp_entity_missing_dependency (dependent);
|
||||
create index temp_entity_missing_dependency_ix_dependency on temp_entity_missing_dependency (dependency)
|
@ -3,7 +3,7 @@
|
||||
CREATE TABLE schema_version (
|
||||
version INTEGER NOT NULL
|
||||
);
|
||||
INSERT INTO schema_version (version) VALUES (4);
|
||||
INSERT INTO schema_version (version) VALUES (5);
|
||||
|
||||
-- actually stores the 512-byte hashes
|
||||
CREATE TABLE hash (
|
||||
@ -140,7 +140,7 @@ CREATE TABLE watch (
|
||||
CREATE INDEX watch_kind ON watch(watch_kind_id);
|
||||
|
||||
-- Note [Watch expression identifier]
|
||||
-- The hash_id + component_index is an unevaluated term reference. We use hash_id instead of object_id because the
|
||||
-- The hash_id + component_index is an unevaluated term reference. We use hash_id instead of object_id because the
|
||||
-- unevaluated term may not exist in the codebase: it is not added merely by watching it without a name, e.g `> 2 + 3`.
|
||||
|
||||
|
||||
|
1
codebase2/codebase-sqlite/unison
Symbolic link
1
codebase2/codebase-sqlite/unison
Symbolic link
@ -0,0 +1 @@
|
||||
.
|
@ -1,6 +1,6 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.7.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
@ -10,6 +10,7 @@ homepage: https://github.com/unisonweb/unison#readme
|
||||
bug-reports: https://github.com/unisonweb/unison/issues
|
||||
build-type: Simple
|
||||
extra-source-files:
|
||||
sql/001-temp-entity-tables.sql
|
||||
sql/create.sql
|
||||
|
||||
source-repository head
|
||||
@ -24,6 +25,9 @@ library
|
||||
U.Codebase.Sqlite.Causal
|
||||
U.Codebase.Sqlite.DbId
|
||||
U.Codebase.Sqlite.Decl.Format
|
||||
U.Codebase.Sqlite.Decode
|
||||
U.Codebase.Sqlite.Entity
|
||||
U.Codebase.Sqlite.HashHandle
|
||||
U.Codebase.Sqlite.LocalIds
|
||||
U.Codebase.Sqlite.LocalizeObject
|
||||
U.Codebase.Sqlite.NamedRef
|
||||
@ -41,6 +45,8 @@ library
|
||||
U.Codebase.Sqlite.Serialization
|
||||
U.Codebase.Sqlite.Symbol
|
||||
U.Codebase.Sqlite.Sync22
|
||||
U.Codebase.Sqlite.TempEntity
|
||||
U.Codebase.Sqlite.TempEntityType
|
||||
U.Codebase.Sqlite.Term.Format
|
||||
hs-source-dirs:
|
||||
./
|
||||
@ -57,6 +63,7 @@ library
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
DuplicateRecordFields
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
FunctionalDependencies
|
||||
@ -89,6 +96,7 @@ library
|
||||
, lens
|
||||
, monad-validate
|
||||
, mtl
|
||||
, nonempty-containers
|
||||
, safe
|
||||
, text
|
||||
, transformers
|
||||
@ -98,6 +106,8 @@ library
|
||||
, unison-prelude
|
||||
, unison-sqlite
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
, unison-util-base32hex-orphans-sqlite
|
||||
, unison-util-serialization
|
||||
, unison-util-term
|
||||
, unliftio
|
||||
|
@ -33,4 +33,5 @@ dependencies:
|
||||
- text
|
||||
- unison-core
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
- unison-prelude
|
||||
|
@ -3,8 +3,6 @@ cabal-version: 1.12
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: b4d3c77715f39c915cacffccf179f1ed62bce29ba013cecc3a25c847f5851233
|
||||
|
||||
name: unison-codebase
|
||||
version: 0.0.0
|
||||
@ -60,4 +58,5 @@ library
|
||||
, unison-core
|
||||
, unison-prelude
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
default-language: Haskell2010
|
||||
|
@ -16,6 +16,7 @@ benchmarks:
|
||||
- criterion
|
||||
- sandi
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
main: Main.hs
|
||||
source-dirs: bench
|
||||
|
||||
|
@ -10,7 +10,7 @@ import System.IO.Unsafe (unsafePerformIO)
|
||||
import UnliftIO (MonadIO, liftIO)
|
||||
|
||||
enabled :: Bool
|
||||
enabled = False
|
||||
enabled = True
|
||||
|
||||
time :: MonadIO m => String -> m a -> m a
|
||||
time _ ma | not enabled = ma
|
||||
|
@ -17,10 +17,8 @@ source-repository head
|
||||
library
|
||||
exposed-modules:
|
||||
U.Util.Alternative
|
||||
U.Util.Base32Hex
|
||||
U.Util.Cache
|
||||
U.Util.Components
|
||||
U.Util.Hash
|
||||
U.Util.Lens
|
||||
U.Util.Monoid
|
||||
U.Util.String
|
||||
@ -97,6 +95,7 @@ benchmark bench
|
||||
, text
|
||||
, time
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
, unison-util-relation
|
||||
, unliftio
|
||||
, vector
|
||||
|
@ -15,6 +15,7 @@ packages:
|
||||
|
||||
lib/unison-prelude
|
||||
lib/unison-sqlite
|
||||
lib/unison-util-base32hex
|
||||
lib/unison-util-relation
|
||||
lib/unison-pretty-printer
|
||||
|
||||
|
@ -40,7 +40,7 @@ Cut a release of base. @runarorama does this usually.
|
||||
|
||||
|
||||
```
|
||||
.> pull git@github.com:unisonweb/base basedev.release
|
||||
.> pull git(git@github.com:unisonweb/base) basedev.release
|
||||
.> cd .basedev.release
|
||||
.basedev.release> delete.namespace releases._latest
|
||||
.basedev.release> squash trunk releases._<ReleaseName>
|
||||
@ -50,7 +50,7 @@ Edit `releases._<ReleaseName>.README` to include `Release: <ReleaseName>`.
|
||||
|
||||
```
|
||||
.basedev.release> fork releases._<ReleaseName> releases._latest
|
||||
.basedev.release> push git@github.com:unisonweb/base
|
||||
.basedev.release> push git(git@github.com:unisonweb/base)
|
||||
```
|
||||
|
||||
__6__
|
||||
@ -64,7 +64,7 @@ Build a new version of Unison Share by following these instructions: https://git
|
||||
|
||||
__8__
|
||||
|
||||
Update homebrew.
|
||||
Update homebrew.
|
||||
|
||||
```
|
||||
git clone git@github.com/unisonweb/homebrew-unison.git
|
||||
@ -96,7 +96,7 @@ Release announcement template (be sure to update the release urls) -
|
||||
|
||||
We've just released a new version of Unison, $RELEASE_NAME, release notes here (link to the issue). Install/upgrade instructions in the thread.
|
||||
|
||||
Mac upgrade is just `brew upgrade unison-language`.
|
||||
Mac upgrade is just `brew upgrade unison-language`.
|
||||
|
||||
A fresh install via:
|
||||
|
||||
|
79
hie.yaml
79
hie.yaml
@ -1,74 +1,107 @@
|
||||
cradle:
|
||||
stack:
|
||||
- path: "codebase2/codebase"
|
||||
- path: "codebase2/codebase/./"
|
||||
component: "unison-codebase:lib"
|
||||
|
||||
- path: "codebase2/codebase-sqlite"
|
||||
- path: "codebase2/codebase-sqlite/./"
|
||||
component: "unison-codebase-sqlite:lib"
|
||||
|
||||
- path: "codebase2/codebase-sync"
|
||||
- path: "codebase2/codebase-sync/./"
|
||||
component: "unison-codebase-sync:lib"
|
||||
|
||||
- path: "codebase2/core"
|
||||
- path: "codebase2/core/./"
|
||||
component: "unison-core:lib"
|
||||
|
||||
- path: "codebase2/util/bench"
|
||||
component: "unison-util:bench:bench"
|
||||
|
||||
- path: "codebase2/util/src"
|
||||
component: "unison-util:lib"
|
||||
|
||||
- path: "codebase2/util-serialization"
|
||||
- path: "codebase2/util/bench/Main.hs"
|
||||
component: "unison-util:bench:bench"
|
||||
|
||||
- path: "codebase2/util-serialization/./"
|
||||
component: "unison-util-serialization:lib"
|
||||
|
||||
- path: "codebase2/util-term"
|
||||
- path: "codebase2/util-term/./"
|
||||
component: "unison-util-term:lib"
|
||||
|
||||
- path: "lib/unison-prelude/src"
|
||||
component: "unison-prelude:lib"
|
||||
|
||||
- path: "lib/unison-pretty-printer/src"
|
||||
component: "unison-pretty-printer:lib"
|
||||
|
||||
- path: "lib/unison-pretty-printer/prettyprintdemo/Main.hs"
|
||||
component: "unison-pretty-printer:exe:prettyprintdemo"
|
||||
|
||||
- path: "lib/unison-pretty-printer/tests"
|
||||
component: "unison-pretty-printer:test:pretty-printer-tests"
|
||||
|
||||
- path: "lib/unison-sqlite/src"
|
||||
component: "unison-sqlite:lib"
|
||||
|
||||
- path: "unison-share-api/src"
|
||||
component: "unison-share-api:lib"
|
||||
- path: "lib/unison-util-base32hex/src"
|
||||
component: "unison-util-base32hex:lib"
|
||||
|
||||
- path: "lib/unison-util-base32hex-orphans-aeson/src"
|
||||
component: "unison-util-base32hex-orphans-aeson:lib"
|
||||
|
||||
- path: "lib/unison-util-base32hex-orphans-sqlite/src"
|
||||
component: "unison-util-base32hex-orphans-sqlite:lib"
|
||||
|
||||
- path: "lib/unison-util-relation/src"
|
||||
component: "unison-util-relation:lib"
|
||||
|
||||
- path: "lib/unison-util-relation/test"
|
||||
component: "unison-util-relation:test:tests"
|
||||
component: "unison-util-relation:test:util-relation-tests"
|
||||
|
||||
- path: "lib/unison-util-relation/benchmarks/relation"
|
||||
- path: "lib/unison-util-relation/benchmarks/relation/Main.hs"
|
||||
component: "unison-util-relation:bench:relation"
|
||||
|
||||
- path: "parser-typechecker/src"
|
||||
component: "unison-parser-typechecker:lib"
|
||||
|
||||
- path: "parser-typechecker/prettyprintdemo"
|
||||
component: "unison-parser-typechecker:exe:prettyprintdemo"
|
||||
|
||||
- path: "parser-typechecker/tests"
|
||||
component: "unison-parser-typechecker:exe:tests"
|
||||
component: "unison-parser-typechecker:test:parser-typechecker-tests"
|
||||
|
||||
- path: "unison-cli/src"
|
||||
component: "unison-cli:lib"
|
||||
|
||||
- path: "unison-cli/integration-tests"
|
||||
component: "unison-cli:exe:integration-tests"
|
||||
- path: "unison-cli/integration-tests/Suite.hs"
|
||||
component: "unison-cli:exe:cli-integration-tests"
|
||||
|
||||
- path: "unison-cli/tests"
|
||||
component: "unison-cli:test:tests"
|
||||
- path: "unison-cli/integration-tests/IntegrationTests/ArgumentParsing.hs"
|
||||
component: "unison-cli:exe:cli-integration-tests"
|
||||
|
||||
- path: "unison-cli/transcripts"
|
||||
- path: "unison-cli/transcripts/Transcripts.hs"
|
||||
component: "unison-cli:exe:transcripts"
|
||||
|
||||
- path: "unison-cli/unison"
|
||||
- path: "unison-cli/unison/Main.hs"
|
||||
component: "unison-cli:exe:unison"
|
||||
|
||||
- path: "unison-cli/unison/ArgParse.hs"
|
||||
component: "unison-cli:exe:unison"
|
||||
|
||||
- path: "unison-cli/unison/System/Path.hs"
|
||||
component: "unison-cli:exe:unison"
|
||||
|
||||
- path: "unison-cli/unison/Version.hs"
|
||||
component: "unison-cli:exe:unison"
|
||||
|
||||
- path: "unison-cli/tests"
|
||||
component: "unison-cli:test:cli-tests"
|
||||
|
||||
- path: "unison-core/src"
|
||||
component: "unison-core1:lib"
|
||||
|
||||
- path: "unison-hashing-v2/src"
|
||||
component: "unison-hashing-v2:lib"
|
||||
|
||||
- path: "unison-hashing-v2/src"
|
||||
component: "unison-hashing-v2:lib"
|
||||
|
||||
- path: "unison-share-api/src"
|
||||
component: "unison-share-api:lib"
|
||||
|
||||
- path: "yaks/easytest/src"
|
||||
component: "easytest:lib"
|
||||
|
||||
|
@ -6,6 +6,7 @@ module Unison.Debug
|
||||
whenDebug,
|
||||
debugLog,
|
||||
debugLogM,
|
||||
shouldDebug,
|
||||
DebugFlag (..),
|
||||
)
|
||||
where
|
||||
@ -26,6 +27,7 @@ data DebugFlag
|
||||
| Auth
|
||||
| Migration
|
||||
| Integrity
|
||||
| Sync
|
||||
deriving (Eq, Ord, Show, Bounded, Enum)
|
||||
|
||||
debugFlags :: Set DebugFlag
|
||||
@ -43,6 +45,7 @@ debugFlags = case (unsafePerformIO (lookupEnv "UNISON_DEBUG")) of
|
||||
"AUTH" -> pure Auth
|
||||
"MIGRATION" -> pure Migration
|
||||
"INTEGRITY" -> pure Integrity
|
||||
"SYNC" -> pure Sync
|
||||
_ -> empty
|
||||
{-# NOINLINE debugFlags #-}
|
||||
|
||||
@ -70,6 +73,10 @@ debugIntegrity :: Bool
|
||||
debugIntegrity = Integrity `Set.member` debugFlags
|
||||
{-# NOINLINE debugIntegrity #-}
|
||||
|
||||
debugSync :: Bool
|
||||
debugSync = Sync `Set.member` debugFlags
|
||||
{-# NOINLINE debugSync #-}
|
||||
|
||||
-- | Use for trace-style selective debugging.
|
||||
-- E.g. 1 + (debug Git "The second number" 2)
|
||||
--
|
||||
@ -116,3 +123,4 @@ shouldDebug = \case
|
||||
Auth -> debugAuth
|
||||
Migration -> debugMigration
|
||||
Integrity -> debugIntegrity
|
||||
Sync -> debugSync
|
||||
|
@ -7,10 +7,13 @@ module Unison.Prelude
|
||||
uncurry4,
|
||||
reportBug,
|
||||
tShow,
|
||||
wundefined,
|
||||
|
||||
-- * @Maybe@ control flow
|
||||
onNothing,
|
||||
whenNothing,
|
||||
whenJust,
|
||||
whenJustM,
|
||||
eitherToMaybe,
|
||||
maybeToEither,
|
||||
|
||||
@ -77,6 +80,14 @@ onNothing m may = maybe m pure may
|
||||
whenNothing :: Applicative m => Maybe a -> m a -> m a
|
||||
whenNothing may m = maybe m pure may
|
||||
|
||||
whenJust :: Applicative m => Maybe a -> (a -> m ()) -> m ()
|
||||
whenJust mx f =
|
||||
maybe (pure ()) f mx
|
||||
|
||||
whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()
|
||||
whenJustM mx f = do
|
||||
mx >>= maybe (pure ()) f
|
||||
|
||||
whenLeft :: Applicative m => Either a b -> (a -> m b) -> m b
|
||||
whenLeft = \case
|
||||
Left a -> \f -> f a
|
||||
@ -155,3 +166,7 @@ reportBug bugId msg =
|
||||
"on the issue to let the team know you encountered it, and you can add",
|
||||
"any additional details you know of to the issue."
|
||||
]
|
||||
|
||||
{-# WARNING wundefined "You left this wundefined." #-}
|
||||
wundefined :: HasCallStack => a
|
||||
wundefined = undefined
|
||||
|
@ -4,12 +4,15 @@ module Unison.Util.Set
|
||||
symmetricDifference,
|
||||
Unison.Util.Set.traverse,
|
||||
flatMap,
|
||||
filterM,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Functor ((<&>))
|
||||
import qualified Data.Maybe as Maybe
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
import Unison.Util.Monoid (foldMapM)
|
||||
|
||||
-- | Set difference, but return @Nothing@ if the difference is empty.
|
||||
difference1 :: Ord a => Set a -> Set a -> Maybe (Set a)
|
||||
@ -29,3 +32,10 @@ traverse f = fmap Set.fromList . Prelude.traverse f . Set.toList
|
||||
|
||||
flatMap :: Ord b => (a -> Set b) -> Set a -> Set b
|
||||
flatMap f = Set.unions . fmap f . Set.toList
|
||||
|
||||
filterM :: (Ord a, Monad m) => (a -> m Bool) -> Set a -> m (Set a)
|
||||
filterM p =
|
||||
foldMapM \x ->
|
||||
p x <&> \case
|
||||
False -> Set.empty
|
||||
True -> Set.singleton x
|
||||
|
@ -20,6 +20,7 @@ library
|
||||
Unison.Debug
|
||||
Unison.Prelude
|
||||
Unison.Util.Map
|
||||
Unison.Util.Monoid
|
||||
Unison.Util.Set
|
||||
hs-source-dirs:
|
||||
src
|
||||
|
@ -73,6 +73,9 @@ module Unison.Sqlite
|
||||
queryOneRowCheck_,
|
||||
queryOneColCheck_,
|
||||
|
||||
-- * Rows modified
|
||||
rowsModified,
|
||||
|
||||
-- * Data version
|
||||
DataVersion (..),
|
||||
getDataVersion,
|
||||
@ -101,11 +104,11 @@ module Unison.Sqlite
|
||||
(Sqlite.Simple.:.) (..),
|
||||
Sqlite.Simple.FromField (fromField),
|
||||
Sqlite.Simple.FromRow (fromRow),
|
||||
Sqlite.Simple.Only (..),
|
||||
Sqlite.Simple.RowParser,
|
||||
Sqlite.Simple.SQLData (..),
|
||||
Sqlite.Simple.ToField (toField),
|
||||
Sqlite.Simple.ToRow (toRow),
|
||||
Sqlite.Simple.Only (..),
|
||||
)
|
||||
where
|
||||
|
||||
|
@ -50,6 +50,9 @@ module Unison.Sqlite.Connection
|
||||
queryOneRowCheck_,
|
||||
queryOneColCheck_,
|
||||
|
||||
-- * Rows modified
|
||||
rowsModified,
|
||||
|
||||
-- * Vacuum (into)
|
||||
vacuum,
|
||||
vacuumInto,
|
||||
@ -472,6 +475,12 @@ queryOneColCheck_ ::
|
||||
queryOneColCheck_ conn s check =
|
||||
queryOneRowCheck_ conn s (coerce @(a -> Either e r) @(Sqlite.Only a -> Either e r) check)
|
||||
|
||||
-- Rows modified
|
||||
|
||||
rowsModified :: Connection -> IO Int
|
||||
rowsModified (Connection _ _ conn) =
|
||||
Sqlite.changes conn
|
||||
|
||||
-- Vacuum
|
||||
|
||||
-- | @VACUUM@
|
||||
|
@ -7,6 +7,7 @@ module Unison.Sqlite.Transaction
|
||||
unsafeUnTransaction,
|
||||
savepoint,
|
||||
unsafeIO,
|
||||
unsafeGetConnection,
|
||||
|
||||
-- * Executing queries
|
||||
|
||||
@ -54,6 +55,9 @@ module Unison.Sqlite.Transaction
|
||||
queryMaybeColCheck_,
|
||||
queryOneRowCheck_,
|
||||
queryOneColCheck_,
|
||||
|
||||
-- * Rows modified
|
||||
rowsModified,
|
||||
)
|
||||
where
|
||||
|
||||
@ -77,6 +81,9 @@ newtype Transaction a
|
||||
-- Omit MonadThrow instance so we always throw SqliteException (via *Check) with lots of context
|
||||
deriving (Applicative, Functor, Monad) via (ReaderT Connection IO)
|
||||
|
||||
unsafeGetConnection :: Transaction Connection
|
||||
unsafeGetConnection = Transaction pure
|
||||
|
||||
-- | Run a transaction on the given connection.
|
||||
runTransaction :: MonadIO m => Connection -> Transaction a -> m a
|
||||
runTransaction conn (Transaction f) = liftIO do
|
||||
@ -361,3 +368,9 @@ queryOneRowCheck_ s check =
|
||||
queryOneColCheck_ :: (Sqlite.FromField a, SqliteExceptionReason e) => Sql -> (a -> Either e r) -> Transaction r
|
||||
queryOneColCheck_ s check =
|
||||
Transaction \conn -> Connection.queryOneColCheck_ conn s check
|
||||
|
||||
-- Rows modified
|
||||
|
||||
rowsModified :: Transaction Int
|
||||
rowsModified =
|
||||
Transaction Connection.rowsModified
|
||||
|
44
lib/unison-util-base32hex-orphans-aeson/package.yaml
Normal file
44
lib/unison-util-base32hex-orphans-aeson/package.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
name: unison-util-base32hex-orphans-aeson
|
||||
github: unisonweb/unison
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
|
||||
library:
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_util_base32hex_orphans_aeson
|
||||
source-dirs: src
|
||||
|
||||
dependencies:
|
||||
- aeson
|
||||
- base
|
||||
- text
|
||||
- unison-util-base32hex
|
||||
|
||||
ghc-options:
|
||||
-Wall
|
||||
-fno-warn-orphans
|
||||
|
||||
default-extensions:
|
||||
- ApplicativeDo
|
||||
- BangPatterns
|
||||
- BlockArguments
|
||||
- DeriveFunctor
|
||||
- DeriveGeneric
|
||||
- DeriveTraversable
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
- LambdaCase
|
||||
- MultiParamTypeClasses
|
||||
- NamedFieldPuns
|
||||
- OverloadedStrings
|
||||
- PatternSynonyms
|
||||
- RankNTypes
|
||||
- ScopedTypeVariables
|
||||
- StandaloneDeriving
|
||||
- TupleSections
|
||||
- TypeApplications
|
||||
- ViewPatterns
|
@ -0,0 +1,16 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
|
||||
module U.Util.Hash32.Orphans.Aeson () where
|
||||
|
||||
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
|
||||
import Data.Text (Text)
|
||||
import U.Util.Base32Hex (Base32Hex (..))
|
||||
import U.Util.Hash32 (Hash32 (..))
|
||||
|
||||
deriving via Text instance FromJSON Hash32
|
||||
|
||||
deriving via Text instance FromJSONKey Hash32
|
||||
|
||||
deriving via Text instance ToJSON Hash32
|
||||
|
||||
deriving via Text instance ToJSONKey Hash32
|
@ -0,0 +1,53 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: unison-util-base32hex-orphans-aeson
|
||||
version: 0.0.0
|
||||
homepage: https://github.com/unisonweb/unison#readme
|
||||
bug-reports: https://github.com/unisonweb/unison/issues
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
build-type: Simple
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/unisonweb/unison
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
U.Util.Hash32.Orphans.Aeson
|
||||
hs-source-dirs:
|
||||
src
|
||||
default-extensions:
|
||||
ApplicativeDo
|
||||
BangPatterns
|
||||
BlockArguments
|
||||
DeriveFunctor
|
||||
DeriveGeneric
|
||||
DeriveTraversable
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
LambdaCase
|
||||
MultiParamTypeClasses
|
||||
NamedFieldPuns
|
||||
OverloadedStrings
|
||||
PatternSynonyms
|
||||
RankNTypes
|
||||
ScopedTypeVariables
|
||||
StandaloneDeriving
|
||||
TupleSections
|
||||
TypeApplications
|
||||
ViewPatterns
|
||||
ghc-options: -Wall -fno-warn-orphans
|
||||
build-depends:
|
||||
aeson
|
||||
, base
|
||||
, text
|
||||
, unison-util-base32hex
|
||||
default-language: Haskell2010
|
44
lib/unison-util-base32hex-orphans-sqlite/package.yaml
Normal file
44
lib/unison-util-base32hex-orphans-sqlite/package.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
name: unison-util-base32hex-orphans-sqlite
|
||||
github: unisonweb/unison
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
|
||||
library:
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_util_base32hex_orphans_sqlite
|
||||
source-dirs: src
|
||||
|
||||
dependencies:
|
||||
- base
|
||||
- sqlite-simple
|
||||
- text
|
||||
- unison-util-base32hex
|
||||
|
||||
ghc-options:
|
||||
-Wall
|
||||
-fno-warn-orphans
|
||||
|
||||
default-extensions:
|
||||
- ApplicativeDo
|
||||
- BangPatterns
|
||||
- BlockArguments
|
||||
- DeriveFunctor
|
||||
- DeriveGeneric
|
||||
- DeriveTraversable
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
- LambdaCase
|
||||
- MultiParamTypeClasses
|
||||
- NamedFieldPuns
|
||||
- OverloadedStrings
|
||||
- PatternSynonyms
|
||||
- RankNTypes
|
||||
- ScopedTypeVariables
|
||||
- StandaloneDeriving
|
||||
- TupleSections
|
||||
- TypeApplications
|
||||
- ViewPatterns
|
@ -0,0 +1,13 @@
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
|
||||
module U.Util.Hash32.Orphans.Sqlite () where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Database.SQLite.Simple.FromField (FromField)
|
||||
import Database.SQLite.Simple.ToField (ToField)
|
||||
import U.Util.Base32Hex (Base32Hex (..))
|
||||
import U.Util.Hash32 (Hash32 (..))
|
||||
|
||||
deriving via Text instance ToField Hash32
|
||||
|
||||
deriving via Text instance FromField Hash32
|
@ -0,0 +1,53 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: unison-util-base32hex-orphans-sqlite
|
||||
version: 0.0.0
|
||||
homepage: https://github.com/unisonweb/unison#readme
|
||||
bug-reports: https://github.com/unisonweb/unison/issues
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
build-type: Simple
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/unisonweb/unison
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
U.Util.Hash32.Orphans.Sqlite
|
||||
hs-source-dirs:
|
||||
src
|
||||
default-extensions:
|
||||
ApplicativeDo
|
||||
BangPatterns
|
||||
BlockArguments
|
||||
DeriveFunctor
|
||||
DeriveGeneric
|
||||
DeriveTraversable
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
LambdaCase
|
||||
MultiParamTypeClasses
|
||||
NamedFieldPuns
|
||||
OverloadedStrings
|
||||
PatternSynonyms
|
||||
RankNTypes
|
||||
ScopedTypeVariables
|
||||
StandaloneDeriving
|
||||
TupleSections
|
||||
TypeApplications
|
||||
ViewPatterns
|
||||
ghc-options: -Wall -fno-warn-orphans
|
||||
build-depends:
|
||||
base
|
||||
, sqlite-simple
|
||||
, text
|
||||
, unison-util-base32hex
|
||||
default-language: Haskell2010
|
44
lib/unison-util-base32hex/package.yaml
Normal file
44
lib/unison-util-base32hex/package.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
name: unison-util-base32hex
|
||||
github: unisonweb/unison
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
|
||||
library:
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_util_base32hex
|
||||
source-dirs: src
|
||||
|
||||
dependencies:
|
||||
- base
|
||||
- base32
|
||||
- bytestring
|
||||
- containers
|
||||
- unison-prelude
|
||||
- text
|
||||
|
||||
ghc-options:
|
||||
-Wall
|
||||
|
||||
default-extensions:
|
||||
- ApplicativeDo
|
||||
- BangPatterns
|
||||
- BlockArguments
|
||||
- DeriveFunctor
|
||||
- DeriveGeneric
|
||||
- DeriveTraversable
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
- LambdaCase
|
||||
- MultiParamTypeClasses
|
||||
- NamedFieldPuns
|
||||
- OverloadedStrings
|
||||
- PatternSynonyms
|
||||
- RankNTypes
|
||||
- ScopedTypeVariables
|
||||
- TupleSections
|
||||
- TypeApplications
|
||||
- ViewPatterns
|
@ -1,5 +1,3 @@
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
module U.Util.Base32Hex
|
||||
( Base32Hex (UnsafeFromText),
|
||||
fromByteString,
|
||||
@ -10,13 +8,11 @@ module U.Util.Base32Hex
|
||||
)
|
||||
where
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.ByteString.Base32.Hex as Base32.Hex
|
||||
import Data.Set (Set)
|
||||
import qualified Data.Set as Set
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Text as Text
|
||||
import qualified Data.Text.Encoding as Text
|
||||
import Unison.Prelude
|
||||
|
||||
newtype Base32Hex = UnsafeFromText Text
|
||||
deriving (Eq, Ord, Show)
|
55
lib/unison-util-base32hex/src/U/Util/Hash32.hs
Normal file
55
lib/unison-util-base32hex/src/U/Util/Hash32.hs
Normal file
@ -0,0 +1,55 @@
|
||||
-- | A 512-bit hash, internally represented as base32hex.
|
||||
module U.Util.Hash32
|
||||
( -- * Hash32 type
|
||||
Hash32 (..),
|
||||
|
||||
-- * Conversions
|
||||
|
||||
-- ** The other Hash :)
|
||||
fromHash,
|
||||
toHash,
|
||||
|
||||
-- ** Base32Hex
|
||||
unsafeFromBase32Hex,
|
||||
toBase32Hex,
|
||||
|
||||
-- ** Text
|
||||
toText,
|
||||
)
|
||||
where
|
||||
|
||||
import U.Util.Base32Hex (Base32Hex (..))
|
||||
import U.Util.Hash (Hash)
|
||||
import qualified U.Util.Hash as Hash
|
||||
import Unison.Prelude
|
||||
|
||||
-- | A 512-bit hash, internally represented as base32hex.
|
||||
--
|
||||
-- Some orphan instances provided in:
|
||||
--
|
||||
-- * @unison-util-base32hex-orphans-aeson@
|
||||
-- * @unison-util-base32hex-orphans-sqlite@
|
||||
newtype Hash32 = UnsafeFromBase32Hex Base32Hex
|
||||
deriving (Eq, Ord, Show) via (Text)
|
||||
|
||||
fromHash :: Hash -> Hash32
|
||||
fromHash =
|
||||
unsafeFromBase32Hex . Hash.toBase32Hex
|
||||
|
||||
toHash :: Hash32 -> Hash
|
||||
toHash =
|
||||
Hash.fromBase32Hex . toBase32Hex
|
||||
|
||||
-- | Convert base32hex to a hash32 (asserting that it is a 512-bit hash).
|
||||
unsafeFromBase32Hex :: Base32Hex -> Hash32
|
||||
unsafeFromBase32Hex =
|
||||
coerce
|
||||
|
||||
-- | Convert a hash32 to base32hex.
|
||||
toBase32Hex :: Hash32 -> Base32Hex
|
||||
toBase32Hex =
|
||||
coerce
|
||||
|
||||
toText :: Hash32 -> Text
|
||||
toText =
|
||||
coerce
|
56
lib/unison-util-base32hex/unison-util-base32hex.cabal
Normal file
56
lib/unison-util-base32hex/unison-util-base32hex.cabal
Normal file
@ -0,0 +1,56 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: unison-util-base32hex
|
||||
version: 0.0.0
|
||||
homepage: https://github.com/unisonweb/unison#readme
|
||||
bug-reports: https://github.com/unisonweb/unison/issues
|
||||
copyright: Copyright (C) 2013-2021 Unison Computing, PBC and contributors
|
||||
build-type: Simple
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/unisonweb/unison
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
U.Util.Base32Hex
|
||||
U.Util.Hash
|
||||
U.Util.Hash32
|
||||
hs-source-dirs:
|
||||
src
|
||||
default-extensions:
|
||||
ApplicativeDo
|
||||
BangPatterns
|
||||
BlockArguments
|
||||
DeriveFunctor
|
||||
DeriveGeneric
|
||||
DeriveTraversable
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
LambdaCase
|
||||
MultiParamTypeClasses
|
||||
NamedFieldPuns
|
||||
OverloadedStrings
|
||||
PatternSynonyms
|
||||
RankNTypes
|
||||
ScopedTypeVariables
|
||||
TupleSections
|
||||
TypeApplications
|
||||
ViewPatterns
|
||||
ghc-options: -Wall
|
||||
build-depends:
|
||||
base
|
||||
, base32
|
||||
, bytestring
|
||||
, containers
|
||||
, text
|
||||
, unison-prelude
|
||||
default-language: Haskell2010
|
@ -13,118 +13,121 @@ when:
|
||||
- condition: flag(optimized)
|
||||
ghc-options: -funbox-strict-fields -O2
|
||||
|
||||
dependencies:
|
||||
- ListLike
|
||||
- NanoID
|
||||
- aeson
|
||||
- ansi-terminal
|
||||
- async
|
||||
- base
|
||||
- base16 >= 0.2.1.0
|
||||
- base64-bytestring
|
||||
- basement
|
||||
- binary
|
||||
- bytes
|
||||
- bytestring
|
||||
- bytestring-to-vector
|
||||
- cereal
|
||||
- clock
|
||||
- configurator
|
||||
- containers >= 0.6.3
|
||||
- cryptonite
|
||||
- data-default
|
||||
- data-memocombinators
|
||||
- deepseq
|
||||
- directory
|
||||
- either
|
||||
- errors
|
||||
- exceptions
|
||||
- extra
|
||||
- filepath
|
||||
- fingertree
|
||||
- fsnotify
|
||||
- fuzzyfind
|
||||
- generic-lens
|
||||
- generic-monoid
|
||||
- hashable
|
||||
- hashtables
|
||||
- haskeline
|
||||
- here
|
||||
- http-client
|
||||
- http-media
|
||||
- http-types
|
||||
- lens
|
||||
- lucid
|
||||
- megaparsec
|
||||
- memory
|
||||
- mmorph
|
||||
- monad-validate
|
||||
- mtl
|
||||
- mutable-containers
|
||||
- mwc-random
|
||||
- natural-transformation
|
||||
- network
|
||||
- network-simple
|
||||
- network-uri
|
||||
- nonempty-containers
|
||||
- open-browser
|
||||
- openapi3
|
||||
- optparse-applicative >= 0.16.1.0
|
||||
- pem
|
||||
- prelude-extras
|
||||
- pretty-simple
|
||||
- primitive
|
||||
- process
|
||||
- random >= 1.2.0
|
||||
- raw-strings-qq
|
||||
- regex-base
|
||||
- regex-tdfa
|
||||
- safe
|
||||
- safe-exceptions
|
||||
- semialign
|
||||
- servant
|
||||
- servant-client
|
||||
- servant-docs
|
||||
- servant-openapi3
|
||||
- servant-server
|
||||
- shellmet
|
||||
- stm
|
||||
- strings
|
||||
- tagged
|
||||
- temporary
|
||||
- terminal-size
|
||||
- text
|
||||
- text-short
|
||||
- these
|
||||
- time
|
||||
- tls
|
||||
- transformers
|
||||
- unicode-show
|
||||
- unison-codebase
|
||||
- unison-codebase-sqlite
|
||||
- unison-codebase-sqlite-hashing-v2
|
||||
- unison-codebase-sync
|
||||
- unison-core
|
||||
- unison-core1
|
||||
- unison-hashing-v2
|
||||
- unison-prelude
|
||||
- unison-pretty-printer
|
||||
- unison-sqlite
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
- unison-util-relation
|
||||
- unison-util-serialization
|
||||
- unliftio
|
||||
- uri-encode
|
||||
- utf8-string
|
||||
- vector
|
||||
- wai
|
||||
- warp
|
||||
- x509
|
||||
- x509-store
|
||||
- x509-system
|
||||
- yaml
|
||||
- zlib
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
dependencies:
|
||||
- aeson
|
||||
- ansi-terminal
|
||||
- async
|
||||
- base
|
||||
- base16 >= 0.2.1.0
|
||||
- base64-bytestring
|
||||
- basement
|
||||
- binary
|
||||
- bytes
|
||||
- bytestring
|
||||
- bytestring-to-vector
|
||||
- cereal
|
||||
- clock
|
||||
- containers >= 0.6.3
|
||||
- configurator
|
||||
- cryptonite
|
||||
- data-default
|
||||
- deepseq
|
||||
- directory
|
||||
- either
|
||||
- fuzzyfind
|
||||
- data-memocombinators
|
||||
- errors
|
||||
- exceptions
|
||||
- extra
|
||||
- filepath
|
||||
- fingertree
|
||||
- fsnotify
|
||||
- generic-monoid
|
||||
- generic-lens
|
||||
- hashable
|
||||
- hashtables
|
||||
- haskeline
|
||||
- here
|
||||
- http-types
|
||||
- http-media
|
||||
- http-client
|
||||
- lens
|
||||
- ListLike
|
||||
- megaparsec
|
||||
- memory
|
||||
- mmorph
|
||||
- monad-validate
|
||||
- mtl
|
||||
- mutable-containers
|
||||
- natural-transformation
|
||||
- network
|
||||
- network-simple
|
||||
- nonempty-containers
|
||||
- optparse-applicative >= 0.16.1.0
|
||||
- openapi3
|
||||
- pem
|
||||
- prelude-extras
|
||||
- pretty-simple
|
||||
- process
|
||||
- primitive
|
||||
- random >= 1.2.0
|
||||
- raw-strings-qq
|
||||
- regex-base
|
||||
- regex-tdfa
|
||||
- safe
|
||||
- safe-exceptions
|
||||
- semialign
|
||||
- mwc-random
|
||||
- NanoID
|
||||
- lucid
|
||||
- yaml
|
||||
- semialign
|
||||
- servant
|
||||
- servant-docs
|
||||
- servant-openapi3
|
||||
- servant-server
|
||||
- shellmet
|
||||
- stm
|
||||
- strings
|
||||
- tagged
|
||||
- temporary
|
||||
- terminal-size
|
||||
- text
|
||||
- text-short
|
||||
- these
|
||||
- time
|
||||
- tls
|
||||
- transformers
|
||||
- unliftio
|
||||
- utf8-string
|
||||
- unicode-show
|
||||
- vector
|
||||
- wai
|
||||
- warp
|
||||
- unicode-show
|
||||
- x509
|
||||
- x509-store
|
||||
- x509-system
|
||||
- zlib
|
||||
- unison-codebase
|
||||
- unison-codebase-sqlite
|
||||
- unison-codebase-sync
|
||||
- unison-core
|
||||
- unison-core1
|
||||
- unison-prelude
|
||||
- unison-pretty-printer
|
||||
- unison-sqlite
|
||||
- unison-util
|
||||
- unison-util-relation
|
||||
- unison-util-serialization
|
||||
- open-browser
|
||||
- uri-encode
|
||||
- generic-lens
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_parser_typechecker
|
||||
@ -135,37 +138,11 @@ tests:
|
||||
main: Suite.hs
|
||||
ghc-options: -W -threaded -rtsopts "-with-rtsopts=-N -T" -v0
|
||||
dependencies:
|
||||
- async
|
||||
- base
|
||||
- bytestring
|
||||
- code-page
|
||||
- containers
|
||||
- directory
|
||||
- easytest
|
||||
- errors
|
||||
- exceptions
|
||||
- extra
|
||||
- filepath
|
||||
- filemanip
|
||||
- haskeline
|
||||
- here
|
||||
- lens
|
||||
- megaparsec
|
||||
- mtl
|
||||
- raw-strings-qq
|
||||
- stm
|
||||
- shellmet
|
||||
- split
|
||||
- temporary
|
||||
- text
|
||||
- transformers
|
||||
- unliftio
|
||||
- unison-core1
|
||||
- unison-parser-typechecker
|
||||
- unison-prelude
|
||||
- unison-util
|
||||
- unison-util-relation
|
||||
- unison-pretty-printer
|
||||
when:
|
||||
- condition: false
|
||||
other-modules: Paths_unison_parser_typechecker
|
||||
@ -181,6 +158,7 @@ default-extensions:
|
||||
- DerivingStrategies
|
||||
- DerivingVia
|
||||
- DoAndIfThenElse
|
||||
- DuplicateRecordFields
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
|
@ -655,7 +655,7 @@ hashBuiltins =
|
||||
B "crypto.hmac" $ forall1 "a" (\a -> hashAlgo --> bytes --> a --> bytes),
|
||||
B "crypto.hmacBytes" $ hashAlgo --> bytes --> bytes --> bytes
|
||||
]
|
||||
++ map h ["Sha3_512", "Sha3_256", "Sha2_512", "Sha2_256", "Blake2b_512", "Blake2b_256", "Blake2s_256"]
|
||||
++ map h ["Sha3_512", "Sha3_256", "Sha2_512", "Sha2_256", "Sha1", "Blake2b_512", "Blake2b_256", "Blake2s_256"]
|
||||
where
|
||||
hashAlgo = Type.ref () Type.hashAlgorithmRef
|
||||
h name = B ("crypto.HashAlgorithm." <> name) hashAlgo
|
||||
|
@ -120,7 +120,7 @@ import Unison.Codebase.BuiltinAnnotation (BuiltinAnnotation (builtinAnnotation))
|
||||
import qualified Unison.Codebase.CodeLookup as CL
|
||||
import Unison.Codebase.Editor.Git (withStatus)
|
||||
import qualified Unison.Codebase.Editor.Git as Git
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadGitRemoteNamespace)
|
||||
import qualified Unison.Codebase.GitError as GitError
|
||||
import Unison.Codebase.Path
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
@ -381,14 +381,14 @@ data Preprocessing m
|
||||
= Unmodified
|
||||
| Preprocessed (Branch m -> m (Branch m))
|
||||
|
||||
-- | Sync elements as needed from a remote codebase into the local one.
|
||||
-- | Sync elements as needed from a remote git codebase into the local one.
|
||||
-- If `sbh` is supplied, we try to load the specified branch hash;
|
||||
-- otherwise we try to load the root branch.
|
||||
importRemoteBranch ::
|
||||
forall m v a.
|
||||
MonadUnliftIO m =>
|
||||
Codebase m v a ->
|
||||
ReadRemoteNamespace ->
|
||||
ReadGitRemoteNamespace ->
|
||||
SyncMode ->
|
||||
Preprocessing m ->
|
||||
m (Either GitError (Branch m))
|
||||
@ -414,7 +414,7 @@ importRemoteBranch codebase ns mode preprocess = runExceptT $ do
|
||||
viewRemoteBranch ::
|
||||
MonadIO m =>
|
||||
Codebase m v a ->
|
||||
ReadRemoteNamespace ->
|
||||
ReadGitRemoteNamespace ->
|
||||
Git.GitBranchBehavior ->
|
||||
(Branch m -> m r) ->
|
||||
m (Either GitError r)
|
||||
|
@ -11,6 +11,7 @@ module Unison.Codebase.Editor.Git
|
||||
withIsolatedRepo,
|
||||
debugGit,
|
||||
gitDirToPath,
|
||||
gitVerbosity,
|
||||
GitBranchBehavior (..),
|
||||
GitRepo (..),
|
||||
|
||||
@ -27,21 +28,18 @@ import qualified Data.Text as Text
|
||||
import Shellmet (($?), ($^), ($|))
|
||||
import System.Exit (ExitCode (ExitSuccess))
|
||||
import System.FilePath ((</>))
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRepo (..))
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadGitRepo (..))
|
||||
import Unison.Codebase.GitError (GitProtocolError)
|
||||
import qualified Unison.Codebase.GitError as GitError
|
||||
import qualified Unison.Debug as Debug
|
||||
import Unison.Prelude
|
||||
import qualified UnliftIO
|
||||
import UnliftIO.Directory (XdgDirectory (XdgCache), doesDirectoryExist, findExecutable, getXdgDirectory)
|
||||
import UnliftIO.Environment (lookupEnv)
|
||||
import UnliftIO.IO (hFlush, stdout)
|
||||
import qualified UnliftIO.Process as UnliftIO
|
||||
|
||||
debugGit :: Bool
|
||||
debugGit =
|
||||
isJust (unsafePerformIO (lookupEnv "UNISON_DEBUG_GIT"))
|
||||
{-# NOINLINE debugGit #-}
|
||||
debugGit = Debug.shouldDebug Debug.Git
|
||||
|
||||
gitVerbosity :: [Text]
|
||||
gitVerbosity =
|
||||
@ -136,7 +134,7 @@ data GitBranchBehavior
|
||||
withRepo ::
|
||||
forall m a.
|
||||
(MonadUnliftIO m) =>
|
||||
ReadRepo ->
|
||||
ReadGitRepo ->
|
||||
GitBranchBehavior ->
|
||||
(GitRepo -> m a) ->
|
||||
m (Either GitProtocolError a)
|
||||
@ -209,7 +207,7 @@ withRepo repo@(ReadGitRepo {url = uri, ref = mayGitRef}) branchBehavior action =
|
||||
pure succeeded
|
||||
|
||||
-- | Do a `git clone` (for a not-previously-cached repo).
|
||||
cloneIfMissing :: (MonadIO m, MonadError GitProtocolError m) => ReadRepo -> FilePath -> m GitRepo
|
||||
cloneIfMissing :: (MonadIO m, MonadError GitProtocolError m) => ReadGitRepo -> FilePath -> m GitRepo
|
||||
cloneIfMissing repo@(ReadGitRepo {url = uri}) localPath = do
|
||||
doesDirectoryExist localPath >>= \case
|
||||
True ->
|
||||
|
@ -10,41 +10,129 @@ import qualified Unison.Codebase.Path as Path
|
||||
import Unison.Codebase.ShortBranchHash (ShortBranchHash)
|
||||
import qualified Unison.Codebase.ShortBranchHash as SBH
|
||||
import Unison.Prelude
|
||||
import Unison.Share.Types
|
||||
|
||||
data ReadRepo = ReadGitRepo {url :: Text, ref :: Maybe Text} deriving (Eq, Ord, Show)
|
||||
data ReadRepo
|
||||
= ReadRepoGit ReadGitRepo
|
||||
| ReadRepoShare ShareCodeserver
|
||||
deriving stock (Eq, Ord, Show)
|
||||
|
||||
data WriteRepo = WriteGitRepo {url' :: Text, branch :: Maybe Text} deriving (Eq, Ord, Show)
|
||||
data ShareCodeserver
|
||||
= DefaultCodeserver
|
||||
| CustomCodeserver CodeserverURI
|
||||
deriving stock (Eq, Ord, Show)
|
||||
|
||||
-- |
|
||||
-- >>> :set -XOverloadedLists
|
||||
-- >>> import Data.Maybe (fromJust)
|
||||
-- >>> import Network.URI
|
||||
-- >>> displayShareCodeserver DefaultCodeserver "share" ["base", "List"]
|
||||
-- "share.base.List"
|
||||
-- >>> displayShareCodeserver DefaultCodeserver "share" []
|
||||
-- "share"
|
||||
-- >>> displayShareCodeserver (CustomCodeserver . fromJust $ parseURI "https://share-next.unison-lang.org/api" >>= codeserverFromURI ) "unison" ["base", "List"]
|
||||
-- "share(https://share-next.unison-lang.org:443/api).unison.base.List"
|
||||
displayShareCodeserver :: ShareCodeserver -> Text -> Path -> Text
|
||||
displayShareCodeserver cs repo path =
|
||||
let shareServer = case cs of
|
||||
DefaultCodeserver -> ""
|
||||
CustomCodeserver cu -> "share(" <> tShow cu <> ")."
|
||||
in shareServer <> repo <> maybePrintPath path
|
||||
|
||||
data ReadGitRepo = ReadGitRepo {url :: Text, ref :: Maybe Text}
|
||||
deriving stock (Eq, Ord, Show)
|
||||
|
||||
data WriteRepo
|
||||
= WriteRepoGit WriteGitRepo
|
||||
| WriteRepoShare ShareCodeserver
|
||||
deriving stock (Eq, Ord, Show)
|
||||
|
||||
data WriteGitRepo = WriteGitRepo {url :: Text, branch :: Maybe Text}
|
||||
deriving stock (Eq, Ord, Show)
|
||||
|
||||
writeToRead :: WriteRepo -> ReadRepo
|
||||
writeToRead (WriteGitRepo {url', branch}) = ReadGitRepo {url = url', ref = branch}
|
||||
writeToRead = \case
|
||||
WriteRepoGit repo -> ReadRepoGit (writeToReadGit repo)
|
||||
WriteRepoShare repo -> ReadRepoShare repo
|
||||
|
||||
writeToReadGit :: WriteGitRepo -> ReadGitRepo
|
||||
writeToReadGit = \case
|
||||
WriteGitRepo {url, branch} -> ReadGitRepo {url = url, ref = branch}
|
||||
|
||||
writePathToRead :: WriteRemotePath -> ReadRemoteNamespace
|
||||
writePathToRead (w, p) = (writeToRead w, Nothing, p)
|
||||
writePathToRead = \case
|
||||
WriteRemotePathGit WriteGitRemotePath {repo, path} ->
|
||||
ReadRemoteNamespaceGit ReadGitRemoteNamespace {repo = writeToReadGit repo, sbh = Nothing, path}
|
||||
WriteRemotePathShare WriteShareRemotePath {server, repo, path} ->
|
||||
ReadRemoteNamespaceShare ReadShareRemoteNamespace {server, repo, path}
|
||||
|
||||
printReadRepo :: ReadRepo -> Text
|
||||
printReadRepo ReadGitRepo {url, ref} = url <> Monoid.fromMaybe (Text.cons ':' <$> ref)
|
||||
printReadGitRepo :: ReadGitRepo -> Text
|
||||
printReadGitRepo ReadGitRepo {url, ref} =
|
||||
"git(" <> url <> Monoid.fromMaybe (Text.cons ':' <$> ref) <> ")"
|
||||
|
||||
printWriteRepo :: WriteRepo -> Text
|
||||
printWriteRepo WriteGitRepo {url', branch} = url' <> Monoid.fromMaybe (Text.cons ':' <$> branch)
|
||||
printWriteGitRepo :: WriteGitRepo -> Text
|
||||
printWriteGitRepo WriteGitRepo {url, branch} = "git(" <> url <> Monoid.fromMaybe (Text.cons ':' <$> branch) <> ")"
|
||||
|
||||
printNamespace :: ReadRepo -> Maybe ShortBranchHash -> Path -> Text
|
||||
printNamespace repo sbh path =
|
||||
printReadRepo repo <> case sbh of
|
||||
Nothing ->
|
||||
if path == Path.empty
|
||||
then mempty
|
||||
else ":." <> Path.toText path
|
||||
Just sbh ->
|
||||
":#" <> SBH.toText sbh
|
||||
<> if path == Path.empty
|
||||
then mempty
|
||||
else "." <> Path.toText path
|
||||
-- | print remote namespace
|
||||
printNamespace :: ReadRemoteNamespace -> Text
|
||||
printNamespace = \case
|
||||
ReadRemoteNamespaceGit ReadGitRemoteNamespace {repo, sbh, path} ->
|
||||
printReadGitRepo repo <> maybePrintSBH sbh <> maybePrintPath path
|
||||
where
|
||||
maybePrintSBH = \case
|
||||
Nothing -> mempty
|
||||
Just sbh -> "#" <> SBH.toText sbh
|
||||
ReadRemoteNamespaceShare ReadShareRemoteNamespace {server, repo, path} ->
|
||||
displayShareCodeserver server repo path
|
||||
|
||||
printHead :: WriteRepo -> Path -> Text
|
||||
printHead repo path =
|
||||
printWriteRepo repo
|
||||
<> if path == Path.empty then mempty else ":." <> Path.toText path
|
||||
-- | Render a 'WriteRemotePath' as text.
|
||||
printWriteRemotePath :: WriteRemotePath -> Text
|
||||
printWriteRemotePath = \case
|
||||
WriteRemotePathGit (WriteGitRemotePath {repo, path}) ->
|
||||
printWriteGitRepo repo <> maybePrintPath path
|
||||
WriteRemotePathShare (WriteShareRemotePath {server, repo, path}) ->
|
||||
displayShareCodeserver server repo path
|
||||
|
||||
type ReadRemoteNamespace = (ReadRepo, Maybe ShortBranchHash, Path)
|
||||
maybePrintPath :: Path -> Text
|
||||
maybePrintPath path =
|
||||
if path == Path.empty
|
||||
then mempty
|
||||
else "." <> Path.toText path
|
||||
|
||||
type WriteRemotePath = (WriteRepo, Path)
|
||||
data ReadRemoteNamespace
|
||||
= ReadRemoteNamespaceGit ReadGitRemoteNamespace
|
||||
| ReadRemoteNamespaceShare ReadShareRemoteNamespace
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
data ReadGitRemoteNamespace = ReadGitRemoteNamespace
|
||||
{ repo :: ReadGitRepo,
|
||||
sbh :: Maybe ShortBranchHash,
|
||||
path :: Path
|
||||
}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
data ReadShareRemoteNamespace = ReadShareRemoteNamespace
|
||||
{ server :: ShareCodeserver,
|
||||
repo :: Text,
|
||||
-- sbh :: Maybe ShortBranchHash, -- maybe later
|
||||
path :: Path
|
||||
}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
data WriteRemotePath
|
||||
= WriteRemotePathGit WriteGitRemotePath
|
||||
| WriteRemotePathShare WriteShareRemotePath
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
data WriteGitRemotePath = WriteGitRemotePath
|
||||
{ repo :: WriteGitRepo,
|
||||
path :: Path
|
||||
}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
data WriteShareRemotePath = WriteShareRemotePath
|
||||
{ server :: ShareCodeserver,
|
||||
repo :: Text,
|
||||
path :: Path
|
||||
}
|
||||
deriving stock (Eq, Show)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
module Unison.Codebase.GitError where
|
||||
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, ReadRepo, WriteRepo)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadGitRemoteNamespace, ReadGitRepo, WriteGitRepo)
|
||||
import Unison.Codebase.Path
|
||||
import Unison.Codebase.ShortBranchHash (ShortBranchHash)
|
||||
import Unison.Prelude
|
||||
@ -11,15 +11,15 @@ type CodebasePath = FilePath
|
||||
|
||||
data GitProtocolError
|
||||
= NoGit
|
||||
| UnrecognizableCacheDir ReadRepo CodebasePath
|
||||
| UnrecognizableCheckoutDir ReadRepo CodebasePath
|
||||
| UnrecognizableCacheDir ReadGitRepo CodebasePath
|
||||
| UnrecognizableCheckoutDir ReadGitRepo CodebasePath
|
||||
| -- srcPath destPath error-description
|
||||
CopyException FilePath FilePath String
|
||||
| CloneException ReadRepo String
|
||||
| PushException WriteRepo String
|
||||
| PushNoOp WriteRepo
|
||||
| CloneException ReadGitRepo String
|
||||
| PushException WriteGitRepo String
|
||||
| PushNoOp WriteGitRepo
|
||||
| -- url commit Diff of what would change on merge with remote
|
||||
PushDestinationHasNewStuff WriteRepo
|
||||
PushDestinationHasNewStuff WriteGitRepo
|
||||
| CleanupError SomeException
|
||||
| -- Thrown when a commit, tag, or branch isn't found in a repo.
|
||||
-- repo ref
|
||||
@ -28,10 +28,10 @@ data GitProtocolError
|
||||
deriving anyclass (Exception)
|
||||
|
||||
data GitCodebaseError h
|
||||
= NoRemoteNamespaceWithHash ReadRepo ShortBranchHash
|
||||
| RemoteNamespaceHashAmbiguous ReadRepo ShortBranchHash (Set h)
|
||||
| CouldntLoadRootBranch ReadRepo h
|
||||
| CouldntParseRemoteBranch ReadRepo String
|
||||
| CouldntLoadSyncedBranch ReadRemoteNamespace h
|
||||
| CouldntFindRemoteBranch ReadRepo Path
|
||||
= NoRemoteNamespaceWithHash ReadGitRepo ShortBranchHash
|
||||
| RemoteNamespaceHashAmbiguous ReadGitRepo ShortBranchHash (Set h)
|
||||
| CouldntLoadRootBranch ReadGitRepo h
|
||||
| CouldntParseRemoteBranch ReadGitRepo String
|
||||
| CouldntLoadSyncedBranch ReadGitRemoteNamespace h
|
||||
| CouldntFindRemoteBranch ReadGitRepo Path
|
||||
deriving (Show)
|
||||
|
@ -23,6 +23,7 @@ import qualified Data.Text.Lazy as TL
|
||||
import qualified Data.Text.Lazy.IO as TL
|
||||
import Data.Void
|
||||
import Text.Pretty.Simple
|
||||
import U.Codebase.HashTags (BranchHash (..))
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as DBBranch
|
||||
import qualified U.Codebase.Sqlite.DbId as DB
|
||||
import qualified U.Codebase.Sqlite.Operations as Ops
|
||||
@ -51,11 +52,11 @@ data IntegrityError
|
||||
| -- (causal hash, branch hash)
|
||||
DetectedCausalsWithoutCorrespondingBranchObjects (NESet (Hash, Hash))
|
||||
| DetectedCausalsWithCausalHashAsBranchHash (NESet Hash)
|
||||
| DetectedBranchErrors Hash (NESet BranchError)
|
||||
| DetectedBranchErrors BranchHash (NESet BranchError)
|
||||
deriving stock (Show, Eq, Ord)
|
||||
|
||||
data BranchError
|
||||
= IncorrectHashForBranch Hash Hash
|
||||
= IncorrectHashForBranch BranchHash BranchHash
|
||||
| MismatchedObjectForChild Hash DB.BranchObjectId DB.BranchObjectId
|
||||
| MissingObjectForChildCausal Hash
|
||||
| MissingObject DB.BranchObjectId
|
||||
@ -151,7 +152,7 @@ integrityCheckAllBranches = do
|
||||
integrityCheckBranch objId = do
|
||||
dbBranch <- Ops.expectDbBranch objId
|
||||
expectedBranchHash <- Helpers.dbBranchHash dbBranch
|
||||
actualBranchHash <- Q.expectPrimaryHashByObjectId (DB.unBranchObjectId objId)
|
||||
actualBranchHash <- BranchHash <$> Q.expectPrimaryHashByObjectId (DB.unBranchObjectId objId)
|
||||
branchHashCheck <- assertExpectedBranchHash expectedBranchHash actualBranchHash
|
||||
branchChildChecks <- flip foldMapM (toListOf DBBranch.childrenHashes_ dbBranch) $ \(childObjId, childCausalHashId) -> do
|
||||
let checks =
|
||||
@ -164,7 +165,7 @@ integrityCheckAllBranches = do
|
||||
Nothing -> pure NoIntegrityErrors
|
||||
Just errs -> pure . IntegrityErrorDetected . NESet.singleton $ DetectedBranchErrors actualBranchHash errs
|
||||
where
|
||||
assertExpectedBranchHash :: Hash -> Hash -> Sqlite.Transaction (Set BranchError)
|
||||
assertExpectedBranchHash :: BranchHash -> BranchHash -> Sqlite.Transaction (Set BranchError)
|
||||
assertExpectedBranchHash expectedBranchHash actualBranchHash = do
|
||||
if (expectedBranchHash /= actualBranchHash)
|
||||
then do
|
||||
@ -201,9 +202,9 @@ integrityCheckAllBranches = do
|
||||
pure (Set.singleton $ MissingObjectForChildCausal ch)
|
||||
Just foundBranchId
|
||||
| foundBranchId /= branchObjId -> do
|
||||
failure $ "Expected child branch object to match canonical object ID for causal hash's namespace: " <> pShow (causalHashId, foundBranchId, branchObjId)
|
||||
ch <- Q.expectHash (DB.unCausalHashId causalHashId)
|
||||
pure (Set.singleton $ MismatchedObjectForChild ch branchObjId foundBranchId)
|
||||
failure $ "Expected child branch object to match canonical object ID for causal hash's namespace: " <> pShow (causalHashId, foundBranchId, branchObjId)
|
||||
ch <- Q.expectHash (DB.unCausalHashId causalHashId)
|
||||
pure (Set.singleton $ MismatchedObjectForChild ch branchObjId foundBranchId)
|
||||
| otherwise -> pure mempty
|
||||
|
||||
failure :: TL.Text -> Sqlite.Transaction ()
|
||||
@ -214,34 +215,34 @@ prettyPrintIntegrityErrors :: Foldable f => f IntegrityError -> P.Pretty P.Color
|
||||
prettyPrintIntegrityErrors xs
|
||||
| null xs = mempty
|
||||
| otherwise =
|
||||
xs
|
||||
& toList
|
||||
& fmap
|
||||
( \case
|
||||
DetectedObjectsWithoutCorrespondingHashObjects objs ->
|
||||
P.hang
|
||||
"Detected objects without any corresponding hash_object. Object IDs:"
|
||||
(P.commas (prettyObjectId <$> NESet.toList objs))
|
||||
DetectedCausalsWithoutCorrespondingBranchObjects hashes ->
|
||||
P.hang
|
||||
"Detected causals without a corresponding branch object:\n"
|
||||
( P.column2Header
|
||||
"Causal Hash"
|
||||
"Branch Hash"
|
||||
(toList hashes <&> bimap prettyHash prettyHash)
|
||||
)
|
||||
DetectedCausalsWithCausalHashAsBranchHash ns ->
|
||||
P.hang
|
||||
"Detected causals with the same causal hash as branch hash:"
|
||||
(P.commas (prettyHash <$> toList ns))
|
||||
DetectedBranchErrors bh errs ->
|
||||
P.hang
|
||||
("Detected errors in branch: " <> prettyHash bh)
|
||||
(P.lines . fmap (<> "\n") . fmap prettyBranchError . toList $ errs)
|
||||
)
|
||||
& fmap (<> "\n")
|
||||
& P.lines
|
||||
& P.warnCallout
|
||||
xs
|
||||
& toList
|
||||
& fmap
|
||||
( \case
|
||||
DetectedObjectsWithoutCorrespondingHashObjects objs ->
|
||||
P.hang
|
||||
"Detected objects without any corresponding hash_object. Object IDs:"
|
||||
(P.commas (prettyObjectId <$> NESet.toList objs))
|
||||
DetectedCausalsWithoutCorrespondingBranchObjects hashes ->
|
||||
P.hang
|
||||
"Detected causals without a corresponding branch object:\n"
|
||||
( P.column2Header
|
||||
"Causal Hash"
|
||||
"Branch Hash"
|
||||
(toList hashes <&> bimap prettyHash prettyHash)
|
||||
)
|
||||
DetectedCausalsWithCausalHashAsBranchHash ns ->
|
||||
P.hang
|
||||
"Detected causals with the same causal hash as branch hash:"
|
||||
(P.commas (prettyHash <$> toList ns))
|
||||
DetectedBranchErrors bh errs ->
|
||||
P.hang
|
||||
("Detected errors in branch: " <> prettyHash (unBranchHash bh))
|
||||
(P.lines . fmap (<> "\n") . fmap prettyBranchError . toList $ errs)
|
||||
)
|
||||
& fmap (<> "\n")
|
||||
& P.lines
|
||||
& P.warnCallout
|
||||
where
|
||||
prettyHash :: Hash -> P.Pretty P.ColorText
|
||||
prettyHash h = P.blue . P.text $ ("#" <> Hash.toBase32HexText h)
|
||||
@ -252,7 +253,7 @@ prettyPrintIntegrityErrors xs
|
||||
prettyBranchError :: BranchError -> P.Pretty P.ColorText
|
||||
prettyBranchError =
|
||||
P.wrap . \case
|
||||
IncorrectHashForBranch expected actual -> "The Branch hash for this branch is incorrect. Expected Hash: " <> prettyHash expected <> ", Actual Hash: " <> prettyHash actual
|
||||
IncorrectHashForBranch expected actual -> "The Branch hash for this branch is incorrect. Expected Hash: " <> prettyHash (unBranchHash expected) <> ", Actual Hash: " <> prettyHash (unBranchHash actual)
|
||||
MismatchedObjectForChild ha obj1 obj2 ->
|
||||
"The child with causal hash: " <> prettyHash ha <> " is mapped to object ID " <> prettyBranchObjectId obj1 <> " but should map to " <> prettyBranchObjectId obj2 <> "."
|
||||
MissingObjectForChildCausal ha ->
|
||||
|
@ -4,6 +4,7 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
{-# LANGUAGE StandaloneDeriving #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
module Unison.Codebase.Path
|
||||
@ -79,6 +80,7 @@ import qualified Data.List.NonEmpty as List.NonEmpty
|
||||
import Data.Sequence (Seq ((:<|), (:|>)))
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Text as Text
|
||||
import qualified GHC.Exts as GHC
|
||||
import qualified Unison.HashQualified' as HQ'
|
||||
import Unison.Name (Convert (..), Name, Parse)
|
||||
import qualified Unison.Name as Name
|
||||
@ -92,6 +94,13 @@ newtype Path = Path {toSeq :: Seq NameSegment}
|
||||
deriving stock (Eq, Ord)
|
||||
deriving newtype (Semigroup, Monoid)
|
||||
|
||||
-- | Meant for use mostly in doc-tests where it's
|
||||
-- sometimes convenient to specify paths as lists.
|
||||
instance GHC.IsList Path where
|
||||
type Item Path = NameSegment
|
||||
toList (Path segs) = Foldable.toList segs
|
||||
fromList = Path . Seq.fromList
|
||||
|
||||
newtype Absolute = Absolute {unabsolute :: Path} deriving (Eq, Ord)
|
||||
|
||||
newtype Relative = Relative {unrelative :: Path} deriving (Eq, Ord)
|
||||
|
@ -29,10 +29,10 @@ import qualified System.FilePath as FilePath
|
||||
import qualified System.FilePath.Posix as FilePath.Posix
|
||||
import qualified U.Codebase.Branch as V2Branch
|
||||
import U.Codebase.HashTags (CausalHash (CausalHash))
|
||||
import qualified U.Codebase.Reference as C.Reference
|
||||
import qualified U.Codebase.Sqlite.Operations as Ops
|
||||
import qualified U.Codebase.Sqlite.Queries as Q
|
||||
import qualified U.Codebase.Sqlite.Sync22 as Sync22
|
||||
import U.Codebase.Sqlite.V2.HashHandle (v2HashHandle)
|
||||
import qualified U.Codebase.Sync as Sync
|
||||
import qualified U.Util.Cache as Cache
|
||||
import qualified U.Util.Hash as H2
|
||||
@ -41,11 +41,16 @@ import Unison.Codebase (Codebase, CodebasePath)
|
||||
import qualified Unison.Codebase as Codebase1
|
||||
import Unison.Codebase.Branch (Branch (..))
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import qualified Unison.Codebase.Branch.Names as Branch
|
||||
import qualified Unison.Codebase.Causal.Type as Causal
|
||||
import Unison.Codebase.Editor.Git (gitIn, gitInCaptured, gitTextIn, withRepo)
|
||||
import qualified Unison.Codebase.Editor.Git as Git
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, ReadRepo, WriteRepo (..), printWriteRepo, writeToRead)
|
||||
import Unison.Codebase.Editor.RemoteRepo
|
||||
( ReadGitRemoteNamespace (..),
|
||||
ReadGitRepo,
|
||||
WriteGitRepo (..),
|
||||
printWriteGitRepo,
|
||||
writeToReadGit,
|
||||
)
|
||||
import qualified Unison.Codebase.GitError as GitError
|
||||
import qualified Unison.Codebase.Init as Codebase
|
||||
import qualified Unison.Codebase.Init.CreateCodebaseError as Codebase1
|
||||
@ -63,7 +68,6 @@ import qualified Unison.Codebase.SqliteCodebase.SyncEphemeral as SyncEphemeral
|
||||
import Unison.Codebase.SyncMode (SyncMode)
|
||||
import Unison.Codebase.Type (LocalOrRemote (..), PushGitBranchOpts (..))
|
||||
import qualified Unison.Codebase.Type as C
|
||||
import qualified Unison.ConstructorType as CT
|
||||
import Unison.DataDeclaration (Decl)
|
||||
import Unison.Hash (Hash)
|
||||
import Unison.Parser.Ann (Ann)
|
||||
@ -135,7 +139,7 @@ createCodebaseOrError debugName path action = do
|
||||
Sqlite.trySetJournalMode conn Sqlite.JournalMode'WAL
|
||||
Sqlite.runTransaction conn do
|
||||
Q.createSchema
|
||||
void . Ops.saveRootBranch $ Cv.causalbranch1to2 Branch.empty
|
||||
void . Ops.saveRootBranch v2HashHandle $ Cv.causalbranch1to2 Branch.empty
|
||||
|
||||
sqliteCodebase debugName path Local action >>= \case
|
||||
Left schemaVersion -> error ("Failed to open codebase with schema version: " ++ show schemaVersion ++ ", which is unexpected because I just created this codebase.")
|
||||
@ -197,20 +201,16 @@ sqliteCodebase debugName root localOrRemote action = do
|
||||
typeOfTermCache <- Cache.semispaceCache 8192
|
||||
declCache <- Cache.semispaceCache 1024
|
||||
rootBranchCache <- newTVarIO Nothing
|
||||
getDeclType <- CodebaseOps.mkGetDeclType
|
||||
-- The v1 codebase interface has operations to read and write individual definitions
|
||||
-- whereas the v2 codebase writes them as complete components. These two fields buffer
|
||||
-- the individual definitions until a complete component has been written.
|
||||
termBuffer :: TVar (Map Hash CodebaseOps.TermBufferEntry) <- newTVarIO Map.empty
|
||||
declBuffer :: TVar (Map Hash CodebaseOps.DeclBufferEntry) <- newTVarIO Map.empty
|
||||
declTypeCache <- Cache.semispaceCache 2048
|
||||
let getTerm :: Reference.Id -> m (Maybe (Term Symbol Ann))
|
||||
getTerm id =
|
||||
Sqlite.runTransaction conn (CodebaseOps.getTerm getDeclType id)
|
||||
|
||||
getDeclType :: C.Reference.Reference -> Sqlite.Transaction CT.ConstructorType
|
||||
getDeclType =
|
||||
Sqlite.unsafeIO . Cache.apply declTypeCache (\ref -> Sqlite.unsafeUnTransaction (CodebaseOps.getDeclType ref) conn)
|
||||
|
||||
getTypeOfTermImpl :: Reference.Id -> m (Maybe (Type Symbol Ann))
|
||||
getTypeOfTermImpl id | debug && trace ("getTypeOfTermImpl " ++ show id) False = undefined
|
||||
getTypeOfTermImpl id =
|
||||
@ -470,7 +470,7 @@ sqliteCodebase debugName root localOrRemote action = do
|
||||
syncFromDirectory = syncFromDirectory,
|
||||
syncToDirectory = syncToDirectory,
|
||||
viewRemoteBranch' = viewRemoteBranch',
|
||||
pushGitBranch = (\r opts action -> pushGitBranch conn r opts action),
|
||||
pushGitBranch = pushGitBranch conn,
|
||||
watches = watches,
|
||||
getWatch = getWatch,
|
||||
putWatch = putWatch,
|
||||
@ -489,9 +489,7 @@ sqliteCodebase debugName root localOrRemote action = do
|
||||
beforeImpl = (Just \l r -> Sqlite.runTransaction conn $ fromJust <$> CodebaseOps.before l r),
|
||||
namesAtPath = \path -> Sqlite.runReadOnlyTransaction conn \runTx ->
|
||||
runTx (CodebaseOps.namesAtPath path),
|
||||
updateNameLookup = Sqlite.runTransaction conn $ do
|
||||
root <- (CodebaseOps.getRootBranch getDeclType rootBranchCache)
|
||||
CodebaseOps.saveRootNamesIndex (Branch.toNames . Branch.head $ root),
|
||||
updateNameLookup = Sqlite.runTransaction conn (CodebaseOps.updateNameLookupIndexFromV2Root getDeclType),
|
||||
connection = conn
|
||||
}
|
||||
let finalizer :: MonadIO m => m ()
|
||||
@ -529,7 +527,7 @@ syncInternal progress runSrc runDest b = time "syncInternal" do
|
||||
-- or if it exists in the source codebase, then we can sync22 it
|
||||
-- if it doesn't exist in the dest or source branch,
|
||||
-- then just use putBranch to the dest
|
||||
sync <- liftIO (Sync22.sync22 (Sync22.hoistEnv lift syncEnv))
|
||||
sync <- liftIO (Sync22.sync22 v2HashHandle (Sync22.hoistEnv lift syncEnv))
|
||||
let doSync :: [Sync22.Entity] -> m ()
|
||||
doSync =
|
||||
throwExceptT
|
||||
@ -675,14 +673,15 @@ syncProgress progressStateRef = Sync.Progress (liftIO . need) (liftIO . done) (l
|
||||
where
|
||||
v = const ()
|
||||
|
||||
-- FIXME(mitchell) seems like this should have "git" in its name
|
||||
viewRemoteBranch' ::
|
||||
forall m r.
|
||||
(MonadUnliftIO m) =>
|
||||
ReadRemoteNamespace ->
|
||||
ReadGitRemoteNamespace ->
|
||||
Git.GitBranchBehavior ->
|
||||
((Branch m, CodebasePath) -> m r) ->
|
||||
m (Either C.GitError r)
|
||||
viewRemoteBranch' (repo, sbh, path) gitBranchBehavior action = UnliftIO.try $ do
|
||||
viewRemoteBranch' ReadGitRemoteNamespace {repo, sbh, path} gitBranchBehavior action = UnliftIO.try $ do
|
||||
-- set up the cache dir
|
||||
time "Git fetch" $
|
||||
throwEitherMWith C.GitProtocolError . withRepo repo gitBranchBehavior $ \remoteRepo -> do
|
||||
@ -731,7 +730,7 @@ pushGitBranch ::
|
||||
forall m e.
|
||||
(MonadUnliftIO m) =>
|
||||
Sqlite.Connection ->
|
||||
WriteRepo ->
|
||||
WriteGitRepo ->
|
||||
PushGitBranchOpts ->
|
||||
-- An action which accepts the current root branch on the remote and computes a new branch.
|
||||
(Branch m -> m (Either e (Branch m))) ->
|
||||
@ -767,8 +766,8 @@ pushGitBranch srcConn repo (PushGitBranchOpts setRoot _syncMode) action = Unlift
|
||||
for newBranchOrErr $ push pushStaging repo
|
||||
pure newBranchOrErr
|
||||
where
|
||||
readRepo :: ReadRepo
|
||||
readRepo = writeToRead repo
|
||||
readRepo :: ReadGitRepo
|
||||
readRepo = writeToReadGit repo
|
||||
doSync :: CodebaseStatus -> FilePath -> Sqlite.Connection -> Branch m -> m ()
|
||||
doSync codebaseStatus remotePath destConn newBranch = do
|
||||
progressStateRef <- liftIO (newIORef emptySyncProgressState)
|
||||
@ -805,7 +804,7 @@ pushGitBranch srcConn repo (PushGitBranchOpts setRoot _syncMode) action = Unlift
|
||||
Just True -> pure ()
|
||||
CreatedCodebase -> pure ()
|
||||
run (setRepoRoot newBranchHash)
|
||||
repoString = Text.unpack $ printWriteRepo repo
|
||||
repoString = Text.unpack $ printWriteGitRepo repo
|
||||
setRepoRoot :: Branch.CausalHash -> Sqlite.Transaction ()
|
||||
setRepoRoot h = do
|
||||
let h2 = Cv.causalHash1to2 h
|
||||
@ -857,8 +856,8 @@ pushGitBranch srcConn repo (PushGitBranchOpts setRoot _syncMode) action = Unlift
|
||||
hasDeleteShm = any isShmDelete statusLines
|
||||
|
||||
-- Commit our changes
|
||||
push :: forall n. MonadIO n => Git.GitRepo -> WriteRepo -> Branch m -> n Bool -- withIOError needs IO
|
||||
push remotePath repo@(WriteGitRepo {url' = url, branch = mayGitBranch}) newRootBranch = time "SqliteCodebase.pushGitRootBranch.push" $ do
|
||||
push :: forall n. MonadIO n => Git.GitRepo -> WriteGitRepo -> Branch m -> n Bool -- withIOError needs IO
|
||||
push remotePath repo@(WriteGitRepo {url, branch = mayGitBranch}) newRootBranch = time "SqliteCodebase.pushGitRootBranch.push" $ do
|
||||
-- has anything changed?
|
||||
-- note: -uall recursively shows status for all files in untracked directories
|
||||
-- we want this so that we see
|
||||
@ -890,6 +889,6 @@ pushGitBranch srcConn repo (PushGitBranchOpts setRoot _syncMode) action = Unlift
|
||||
-- Push our changes to the repo, silencing all output.
|
||||
-- Even with quiet, the remote (Github) can still send output through,
|
||||
-- so we capture stdout and stderr.
|
||||
(successful, _stdout, stderr) <- gitInCaptured remotePath $ ["push", "--quiet", url] ++ maybe [] (pure @[]) mayGitBranch
|
||||
(successful, _stdout, stderr) <- gitInCaptured remotePath $ ["push", url] ++ Git.gitVerbosity ++ maybe [] (pure @[]) mayGitBranch
|
||||
when (not successful) . throwIO $ GitError.PushException repo (Text.unpack stderr)
|
||||
pure True
|
||||
|
@ -1,11 +1,11 @@
|
||||
module Unison.Codebase.SqliteCodebase.GitError where
|
||||
|
||||
import U.Codebase.Sqlite.DbId (SchemaVersion)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRepo)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadGitRepo)
|
||||
import Unison.CodebasePath (CodebasePath)
|
||||
|
||||
data GitSqliteCodebaseError
|
||||
= GitCouldntParseRootBranchHash ReadRepo String
|
||||
| NoDatabaseFile ReadRepo CodebasePath
|
||||
| UnrecognizedSchemaVersion ReadRepo CodebasePath SchemaVersion
|
||||
= GitCouldntParseRootBranchHash ReadGitRepo String
|
||||
| NoDatabaseFile ReadGitRepo CodebasePath
|
||||
| UnrecognizedSchemaVersion ReadGitRepo CodebasePath SchemaVersion
|
||||
deriving (Show)
|
||||
|
@ -17,6 +17,7 @@ import Unison.Codebase.SqliteCodebase.Migrations.Helpers (abortMigration)
|
||||
import Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema1To2 (migrateSchema1To2)
|
||||
import Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema2To3 (migrateSchema2To3)
|
||||
import Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema3To4 (migrateSchema3To4)
|
||||
import Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema4To5 (migrateSchema4To5)
|
||||
import qualified Unison.Codebase.SqliteCodebase.Operations as Ops2
|
||||
import Unison.Codebase.SqliteCodebase.Paths
|
||||
import Unison.Codebase.Type (LocalOrRemote (..))
|
||||
@ -41,7 +42,8 @@ migrations getDeclType termBuffer declBuffer =
|
||||
Map.fromList
|
||||
[ (2, migrateSchema1To2 getDeclType termBuffer declBuffer),
|
||||
(3, migrateSchema2To3),
|
||||
(4, migrateSchema3To4)
|
||||
(4, migrateSchema3To4),
|
||||
(5, migrateSchema4To5)
|
||||
]
|
||||
|
||||
-- | Migrates a codebase up to the most recent version known to ucm.
|
||||
|
@ -33,7 +33,7 @@ import qualified U.Codebase.Referent as UReferent
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S.Branch.Full
|
||||
import U.Codebase.Sqlite.Causal (GDbCausal (..))
|
||||
import qualified U.Codebase.Sqlite.Causal as SC
|
||||
import qualified U.Codebase.Sqlite.Causal as SC.DbCausal (GDbCausal (..))
|
||||
import U.Codebase.Sqlite.DbId
|
||||
( BranchHashId (..),
|
||||
BranchObjectId (..),
|
||||
@ -50,6 +50,7 @@ import qualified U.Codebase.Sqlite.Patch.Full as S
|
||||
import qualified U.Codebase.Sqlite.Patch.TermEdit as TermEdit
|
||||
import qualified U.Codebase.Sqlite.Patch.TypeEdit as TypeEdit
|
||||
import qualified U.Codebase.Sqlite.Queries as Q
|
||||
import U.Codebase.Sqlite.V2.HashHandle (v2HashHandle)
|
||||
import U.Codebase.Sync (Sync (Sync))
|
||||
import qualified U.Codebase.Sync as Sync
|
||||
import U.Codebase.WatchKind (WatchKind)
|
||||
@ -171,7 +172,7 @@ data MigrationState = MigrationState
|
||||
-- Remember the hashes of term/decls that we have already migrated to avoid migrating them twice.
|
||||
migratedDefnHashes :: Set (Old Hash),
|
||||
numMigrated :: Int,
|
||||
v2EmptyBranchHashInfo :: (BranchHashId, Hash)
|
||||
v2EmptyBranchHashInfo :: (BranchHashId, BranchHash)
|
||||
}
|
||||
deriving (Generic)
|
||||
|
||||
@ -230,7 +231,7 @@ migrateCausal oldCausalHashId = fmap (either id id) . runExceptT $ do
|
||||
Nothing -> use (field @"v2EmptyBranchHashInfo")
|
||||
Just branchObjId -> do
|
||||
let (_, newBranchHashId, newBranchHash, _) = migratedObjIds ^?! ix branchObjId
|
||||
pure (BranchHashId newBranchHashId, newBranchHash)
|
||||
pure (BranchHashId newBranchHashId, BranchHash newBranchHash)
|
||||
|
||||
let (newParentHashes, newParentHashIds) =
|
||||
oldCausalParentHashIds
|
||||
@ -244,7 +245,7 @@ migrateCausal oldCausalHashId = fmap (either id id) . runExceptT $ do
|
||||
CausalHash . Cv.hash1to2 $
|
||||
Hashing.hashCausal
|
||||
( Hashing.Causal
|
||||
{ branchHash = newBranchHash,
|
||||
{ branchHash = unBranchHash newBranchHash,
|
||||
parents = Set.mapMonotonic Cv.hash2to1 newParentHashes
|
||||
}
|
||||
)
|
||||
@ -256,8 +257,11 @@ migrateCausal oldCausalHashId = fmap (either id id) . runExceptT $ do
|
||||
parents = newParentHashIds
|
||||
}
|
||||
(lift . lift) do
|
||||
Q.saveCausal (SC.selfHash newCausal) (SC.valueHash newCausal)
|
||||
Q.saveCausalParents (SC.selfHash newCausal) (Set.toList $ SC.parents newCausal)
|
||||
Q.saveCausal
|
||||
v2HashHandle
|
||||
(SC.DbCausal.selfHash newCausal)
|
||||
(SC.DbCausal.valueHash newCausal)
|
||||
(Set.toList $ SC.DbCausal.parents newCausal)
|
||||
|
||||
field @"causalMapping" %= Map.insert oldCausalHashId (newCausalHash, newCausalHashId)
|
||||
|
||||
@ -331,11 +335,17 @@ migrateBranch oldObjectId = fmap (either id id) . runExceptT $ do
|
||||
& S.patches_ %~ remapPatchObjectId
|
||||
& S.childrenHashes_ %~ (remapBranchObjectId *** remapCausalHashId)
|
||||
|
||||
let (localBranchIds, localBranch) = S.LocalizeObject.localizeBranch newBranch
|
||||
newHash <- lift . lift $ Hashing.dbBranchHash newBranch
|
||||
newHashId <- lift . lift $ Q.saveBranchHash (BranchHash (Cv.hash1to2 newHash))
|
||||
newObjectId <- lift . lift $ Ops.saveBranchObject newHashId localBranchIds localBranch
|
||||
field @"objLookup" %= Map.insert oldObjectId (unBranchObjectId newObjectId, unBranchHashId newHashId, newHash, oldHash)
|
||||
newHashId <- lift . lift $ Q.saveBranchHash (coerce Cv.hash1to2 newHash)
|
||||
newObjectId <- lift . lift $ Ops.saveDbBranchUnderHashId v2HashHandle newHashId newBranch
|
||||
field @"objLookup"
|
||||
%= Map.insert
|
||||
oldObjectId
|
||||
( unBranchObjectId newObjectId,
|
||||
unBranchHashId newHashId,
|
||||
unBranchHash newHash,
|
||||
oldHash
|
||||
)
|
||||
pure Sync.Done
|
||||
|
||||
migratePatch :: Old PatchObjectId -> StateT MigrationState Sqlite.Transaction (Sync.TrySyncResult Entity)
|
||||
@ -387,9 +397,21 @@ migratePatch oldObjectId = fmap (either id id) . runExceptT $ do
|
||||
|
||||
let (localPatchIds, localPatch) = S.LocalizeObject.localizePatch newPatchWithIds
|
||||
newHash <- lift . lift $ Hashing.dbPatchHash newPatchWithIds
|
||||
newObjectId <- lift . lift $ Ops.saveDbPatch (PatchHash (Cv.hash1to2 newHash)) (S.Patch.Format.Full localPatchIds localPatch)
|
||||
newHashId <- lift . lift $ Q.expectHashIdByHash (Cv.hash1to2 newHash)
|
||||
field @"objLookup" %= Map.insert (unPatchObjectId oldObjectId) (unPatchObjectId newObjectId, newHashId, newHash, oldHash)
|
||||
newObjectId <-
|
||||
lift . lift $
|
||||
Ops.saveDbPatch
|
||||
v2HashHandle
|
||||
(coerce Cv.hash1to2 newHash)
|
||||
(S.Patch.Format.Full localPatchIds localPatch)
|
||||
newHashId <- lift . lift $ Q.expectHashIdByHash (coerce Cv.hash1to2 newHash)
|
||||
field @"objLookup"
|
||||
%= Map.insert
|
||||
(unPatchObjectId oldObjectId)
|
||||
( unPatchObjectId newObjectId,
|
||||
newHashId,
|
||||
unPatchHash newHash,
|
||||
oldHash
|
||||
)
|
||||
pure Sync.Done
|
||||
|
||||
-- | PLAN
|
||||
@ -844,11 +866,10 @@ foldSetter t s = execWriter (s & t %%~ \a -> tell [a] *> pure a)
|
||||
|
||||
-- | Save an empty branch and get its new hash to use when replacing
|
||||
-- branches which are missing due to database corruption.
|
||||
saveV2EmptyBranch :: Sqlite.Transaction (BranchHashId, Hash)
|
||||
saveV2EmptyBranch :: Sqlite.Transaction (BranchHashId, BranchHash)
|
||||
saveV2EmptyBranch = do
|
||||
let branch = S.emptyBranch
|
||||
let (localBranchIds, localBranch) = S.LocalizeObject.localizeBranch branch
|
||||
newHash <- Hashing.dbBranchHash branch
|
||||
newHashId <- Q.saveBranchHash (BranchHash (Cv.hash1to2 newHash))
|
||||
_ <- Ops.saveBranchObject newHashId localBranchIds localBranch
|
||||
newHashId <- Q.saveBranchHash (coerce Cv.hash1to2 newHash)
|
||||
_ <- Ops.saveDbBranchUnderHashId v2HashHandle newHashId branch
|
||||
pure (newHashId, newHash)
|
||||
|
@ -1,9 +1,13 @@
|
||||
module Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema1To2.DbHelpers
|
||||
( dbBranchHash,
|
||||
dbPatchHash,
|
||||
syncCausalHash,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Vector as Vector
|
||||
import U.Codebase.HashTags (BranchHash (..), CausalHash (..), PatchHash (..))
|
||||
import qualified U.Codebase.Reference as S hiding (Reference)
|
||||
import qualified U.Codebase.Reference as S.Reference
|
||||
import qualified U.Codebase.Referent as S.Referent
|
||||
@ -11,6 +15,7 @@ import U.Codebase.Sqlite.Branch.Full (DbMetadataSet)
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S.Branch.Full
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as S.MetadataSet
|
||||
import qualified U.Codebase.Sqlite.Causal as S
|
||||
import qualified U.Codebase.Sqlite.DbId as Db
|
||||
import qualified U.Codebase.Sqlite.Patch.Full as S
|
||||
import qualified U.Codebase.Sqlite.Patch.TermEdit as S (TermEdit)
|
||||
@ -24,6 +29,7 @@ import qualified Unison.Codebase.SqliteCodebase.Conversions as Cv
|
||||
import Unison.Hash (Hash)
|
||||
import Unison.Hashing.V2.Branch (NameSegment (..))
|
||||
import qualified Unison.Hashing.V2.Branch as Hashing.Branch
|
||||
import qualified Unison.Hashing.V2.Causal as Hashing.Causal
|
||||
import qualified Unison.Hashing.V2.Patch as Hashing (Patch (..))
|
||||
import qualified Unison.Hashing.V2.Patch as Hashing.Patch
|
||||
import qualified Unison.Hashing.V2.Reference as Hashing (Reference)
|
||||
@ -39,9 +45,16 @@ import Unison.Sqlite (Transaction)
|
||||
import qualified Unison.Util.Map as Map
|
||||
import qualified Unison.Util.Set as Set
|
||||
|
||||
dbBranchHash :: S.DbBranch -> Transaction Hash
|
||||
syncCausalHash :: S.SyncCausalFormat -> Transaction CausalHash
|
||||
syncCausalHash S.SyncCausalFormat {valueHash = valueHashId, parents = parentChIds} = do
|
||||
fmap (CausalHash . Hashing.Causal.hashCausal) $
|
||||
Hashing.Causal.Causal
|
||||
<$> coerce @(Transaction BranchHash) @(Transaction Hash) (Q.expectBranchHash valueHashId)
|
||||
<*> fmap (Set.fromList . coerce @[CausalHash] @[Hash] . Vector.toList) (traverse Q.expectCausalHash parentChIds)
|
||||
|
||||
dbBranchHash :: S.DbBranch -> Transaction BranchHash
|
||||
dbBranchHash (S.Branch.Full.Branch tms tps patches children) =
|
||||
fmap Hashing.Branch.hashBranch $
|
||||
fmap (BranchHash . Hashing.Branch.hashBranch) $
|
||||
Hashing.Branch.Raw
|
||||
<$> doTerms tms
|
||||
<*> doTypes tps
|
||||
@ -72,9 +85,9 @@ dbBranchHash (S.Branch.Full.Branch tms tps patches children) =
|
||||
doChildren =
|
||||
Map.bitraverse s2hNameSegment \(_boId, chId) -> causalHashIdToHash chId
|
||||
|
||||
dbPatchHash :: S.Patch -> Transaction Hash
|
||||
dbPatchHash :: S.Patch -> Transaction PatchHash
|
||||
dbPatchHash S.Patch {S.termEdits, S.typeEdits} =
|
||||
fmap Hashing.Patch.hashPatch $
|
||||
fmap (PatchHash . Hashing.Patch.hashPatch) $
|
||||
Hashing.Patch
|
||||
<$> doTermEdits termEdits
|
||||
<*> doTypeEdits typeEdits
|
||||
|
@ -12,7 +12,6 @@ import Data.Semigroup
|
||||
import qualified Data.Set as Set
|
||||
import Data.Set.Lens (setOf)
|
||||
import Data.String.Here.Uninterpolated (here)
|
||||
import qualified U.Codebase.HashTags as H
|
||||
import qualified U.Codebase.Sqlite.Branch.Format as S.BranchFormat
|
||||
import qualified U.Codebase.Sqlite.Branch.Full as DBBranch
|
||||
import qualified U.Codebase.Sqlite.DbId as DB
|
||||
@ -230,7 +229,7 @@ rehashAndCanonicalizeNamespace causalHashId possiblyIncorrectNamespaceHashId obj
|
||||
liftT $ replaceBranch objId remappedBranch
|
||||
correctNamespaceHash <- liftT $ Helpers.dbBranchHash remappedBranch
|
||||
liftT . debugLog $ "Correct namespace hash: " <> show correctNamespaceHash
|
||||
correctNamespaceHashId <- liftT $ Q.saveBranchHash (H.BranchHash correctNamespaceHash)
|
||||
correctNamespaceHashId <- liftT $ Q.saveBranchHash correctNamespaceHash
|
||||
|
||||
when (correctNamespaceHashId == possiblyIncorrectNamespaceHashId) $ do
|
||||
-- If the existing hash for this namespace was already correct, we don't need to
|
||||
@ -251,17 +250,17 @@ rehashAndCanonicalizeNamespace causalHashId possiblyIncorrectNamespaceHashId obj
|
||||
-- that one.
|
||||
Just canonicalObjectId
|
||||
| canonicalObjectId /= objId -> do
|
||||
-- Found an existing but different object with this hash, so the current object is a duplicate and
|
||||
-- needs to be deleted.
|
||||
liftT . debugLog $ "Mapping objID: " <> show objId <> " to canonical: " <> show canonicalObjectId
|
||||
liftT . debugLog $ "Unilaterally deleting: " <> show objId
|
||||
-- Remove possible foreign-key references before deleting the objects themselves
|
||||
liftT $ Sqlite.execute deleteHashObjectsByObjectId (Sqlite.Only objId)
|
||||
liftT $ Sqlite.execute deleteObjectById (Sqlite.Only objId)
|
||||
pure canonicalObjectId
|
||||
-- Found an existing but different object with this hash, so the current object is a duplicate and
|
||||
-- needs to be deleted.
|
||||
liftT . debugLog $ "Mapping objID: " <> show objId <> " to canonical: " <> show canonicalObjectId
|
||||
liftT . debugLog $ "Unilaterally deleting: " <> show objId
|
||||
-- Remove possible foreign-key references before deleting the objects themselves
|
||||
liftT $ Sqlite.execute deleteHashObjectsByObjectId (Sqlite.Only objId)
|
||||
liftT $ Sqlite.execute deleteObjectById (Sqlite.Only objId)
|
||||
pure canonicalObjectId
|
||||
| otherwise -> do
|
||||
-- This should be impossible.
|
||||
error $ "We proved that the new hash is different from the existing one, but somehow found the same object for each hash. Please report this as a bug." <> show (objId, canonicalObjectId)
|
||||
-- This should be impossible.
|
||||
error $ "We proved that the new hash is different from the existing one, but somehow found the same object for each hash. Please report this as a bug." <> show (objId, canonicalObjectId)
|
||||
Nothing -> do
|
||||
-- There's no existing canonical object, this object BECOMES the canonical one by
|
||||
-- reassigning its primary hash.
|
||||
|
@ -0,0 +1,11 @@
|
||||
module Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema4To5 (migrateSchema4To5) where
|
||||
|
||||
import qualified U.Codebase.Sqlite.Queries as Q
|
||||
import qualified Unison.Sqlite as Sqlite
|
||||
|
||||
-- | The 4 to 5 migration adds initial support for out-of-order sync i.e. Unison Share
|
||||
migrateSchema4To5 :: Sqlite.Transaction ()
|
||||
migrateSchema4To5 = do
|
||||
Q.expectSchemaVersion 4
|
||||
Q.addTempEntityTables
|
||||
Q.setSchemaVersion 5
|
@ -7,14 +7,18 @@
|
||||
-- are unified with non-sqlite operations in the Codebase interface, like 'appendReflog'.
|
||||
module Unison.Codebase.SqliteCodebase.Operations where
|
||||
|
||||
import Control.Lens (ifor)
|
||||
import Data.Bifunctor (Bifunctor (bimap), second)
|
||||
import Data.Bitraversable (bitraverse)
|
||||
import Data.Either.Extra ()
|
||||
import qualified Data.List as List
|
||||
import qualified Data.List.NonEmpty as NEList
|
||||
import Data.List.NonEmpty.Extra (NonEmpty ((:|)), maximum1)
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Set as Set
|
||||
import qualified Data.Text as Text
|
||||
import qualified U.Codebase.Branch as V2Branch
|
||||
import qualified U.Codebase.Causal as V2Causal
|
||||
import U.Codebase.HashTags (CausalHash (unCausalHash))
|
||||
import qualified U.Codebase.Reference as C.Reference
|
||||
import qualified U.Codebase.Referent as C.Referent
|
||||
@ -23,10 +27,15 @@ import qualified U.Codebase.Sqlite.NamedRef as S
|
||||
import qualified U.Codebase.Sqlite.ObjectType as OT
|
||||
import qualified U.Codebase.Sqlite.Operations as Ops
|
||||
import qualified U.Codebase.Sqlite.Queries as Q
|
||||
import U.Codebase.Sqlite.V2.Decl (saveDeclComponent)
|
||||
import U.Codebase.Sqlite.V2.HashHandle (v2HashHandle)
|
||||
import U.Codebase.Sqlite.V2.Term (saveTermComponent)
|
||||
import qualified U.Util.Cache as Cache
|
||||
import qualified U.Util.Hash as H2
|
||||
import qualified Unison.Builtin as Builtins
|
||||
import Unison.Codebase.Branch (Branch (..))
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import qualified Unison.Codebase.Branch.Names as V1Branch
|
||||
import qualified Unison.Codebase.Causal.Type as Causal
|
||||
import Unison.Codebase.Patch (Patch)
|
||||
import Unison.Codebase.Path (Path)
|
||||
@ -55,6 +64,7 @@ import qualified Unison.ShortHash as SH
|
||||
import qualified Unison.ShortHash as ShortHash
|
||||
import Unison.Sqlite (Transaction)
|
||||
import qualified Unison.Sqlite as Sqlite
|
||||
import qualified Unison.Sqlite.Transaction as Sqlite
|
||||
import Unison.Symbol (Symbol)
|
||||
import Unison.Term (Term)
|
||||
import qualified Unison.Term as Term
|
||||
@ -264,24 +274,17 @@ tryFlushTermBuffer termBuffer =
|
||||
let loop h =
|
||||
tryFlushBuffer
|
||||
termBuffer
|
||||
( \h2 component -> do
|
||||
oId <-
|
||||
Ops.saveTermComponent h2 $
|
||||
fmap (bimap (Cv.term1to2 h) Cv.ttype1to2) component
|
||||
addTermComponentTypeIndex oId (fmap snd component)
|
||||
( \h2 component ->
|
||||
void $
|
||||
saveTermComponent
|
||||
Nothing
|
||||
h2
|
||||
(fmap (bimap (Cv.term1to2 h) Cv.ttype1to2) component)
|
||||
)
|
||||
loop
|
||||
h
|
||||
in loop
|
||||
|
||||
addTermComponentTypeIndex :: ObjectId -> [Type Symbol Ann] -> Transaction ()
|
||||
addTermComponentTypeIndex oId types = for_ (types `zip` [0 ..]) \(tp, i) -> do
|
||||
let self = C.Referent.RefId (C.Reference.Id oId i)
|
||||
typeForIndexing = Hashing.typeToReference tp
|
||||
typeMentionsForIndexing = Hashing.typeToReferenceMentions tp
|
||||
Ops.addTypeToIndexForTerm self (Cv.reference1to2 typeForIndexing)
|
||||
Ops.addTypeMentionsToIndexForTerm self (Set.map Cv.reference1to2 typeMentionsForIndexing)
|
||||
|
||||
addDeclComponentTypeIndex :: ObjectId -> [[Type Symbol Ann]] -> Transaction ()
|
||||
addDeclComponentTypeIndex oId ctorss =
|
||||
for_ (ctorss `zip` [0 ..]) \(ctors, i) ->
|
||||
@ -326,10 +329,12 @@ tryFlushDeclBuffer termBuffer declBuffer =
|
||||
let loop h =
|
||||
tryFlushBuffer
|
||||
declBuffer
|
||||
( \h2 component -> do
|
||||
oId <- Ops.saveDeclComponent h2 $ fmap (Cv.decl1to2 h) component
|
||||
addDeclComponentTypeIndex oId $
|
||||
fmap (map snd . Decl.constructors . Decl.asDataDecl) component
|
||||
( \h2 component ->
|
||||
void $
|
||||
saveDeclComponent
|
||||
Nothing
|
||||
h2
|
||||
(fmap (Cv.decl1to2 h) component)
|
||||
)
|
||||
(\h -> tryFlushTermBuffer termBuffer h >> loop h)
|
||||
h
|
||||
@ -359,12 +364,18 @@ getRootBranch doGetDeclType rootBranchCache =
|
||||
where
|
||||
forceReload :: Transaction (Branch Transaction)
|
||||
forceReload = do
|
||||
causal2 <- Ops.expectRootCausal
|
||||
branch1 <- Cv.causalbranch2to1 doGetDeclType causal2
|
||||
branch1 <- uncachedLoadRootBranch doGetDeclType
|
||||
ver <- Sqlite.getDataVersion
|
||||
Sqlite.unsafeIO (atomically (writeTVar rootBranchCache (Just (ver, branch1))))
|
||||
pure branch1
|
||||
|
||||
uncachedLoadRootBranch ::
|
||||
(C.Reference.Reference -> Sqlite.Transaction CT.ConstructorType) ->
|
||||
Transaction (Branch Transaction)
|
||||
uncachedLoadRootBranch getDeclType = do
|
||||
causal2 <- Ops.expectRootCausal
|
||||
Cv.causalbranch2to1 getDeclType causal2
|
||||
|
||||
getRootBranchExists :: Transaction Bool
|
||||
getRootBranchExists =
|
||||
isJust <$> Ops.loadRootCausalHash
|
||||
@ -373,7 +384,7 @@ putRootBranch :: TVar (Maybe (Sqlite.DataVersion, Branch Transaction)) -> Branch
|
||||
putRootBranch rootBranchCache branch1 = do
|
||||
-- todo: check to see if root namespace hash has been externally modified
|
||||
-- and do something (merge?) it if necessary. But for now, we just overwrite it.
|
||||
void (Ops.saveRootBranch (Cv.causalbranch1to2 branch1))
|
||||
void (Ops.saveRootBranch v2HashHandle (Cv.causalbranch1to2 branch1))
|
||||
Sqlite.unsafeIO (atomically $ modifyTVar' rootBranchCache (fmap . second $ const branch1))
|
||||
|
||||
-- if this blows up on cromulent hashes, then switch from `hashToHashId`
|
||||
@ -392,7 +403,7 @@ getBranchForHash doGetDeclType h = do
|
||||
|
||||
putBranch :: Branch Transaction -> Transaction ()
|
||||
putBranch =
|
||||
void . Ops.saveBranch . Cv.causalbranch1to2
|
||||
void . Ops.saveBranch v2HashHandle . Cv.causalbranch1to2
|
||||
|
||||
isCausalHash :: Branch.CausalHash -> Transaction Bool
|
||||
isCausalHash (Causal.CausalHash h) =
|
||||
@ -409,7 +420,7 @@ getPatch h =
|
||||
|
||||
putPatch :: Branch.EditHash -> Patch -> Transaction ()
|
||||
putPatch h p =
|
||||
void $ Ops.savePatch (Cv.patchHash1to2 h) (Cv.patch1to2 p)
|
||||
void $ Ops.savePatch v2HashHandle (Cv.patchHash1to2 h) (Cv.patch1to2 p)
|
||||
|
||||
patchExists :: Branch.EditHash -> Transaction Bool
|
||||
patchExists h = fmap isJust $ Q.loadPatchObjectIdForPrimaryHash (Cv.patchHash1to2 h)
|
||||
@ -584,21 +595,77 @@ namesAtPath path = do
|
||||
Nothing -> (mempty, [(n, ref)])
|
||||
Just stripped -> ([(Name.makeRelative stripped, ref)], mempty)
|
||||
|
||||
saveRootNamesIndex :: Names -> Transaction ()
|
||||
saveRootNamesIndex Names {Names.terms, Names.types} = do
|
||||
let termNames :: [(S.NamedRef (C.Referent.Referent, Maybe C.Referent.ConstructorType))]
|
||||
termNames = Rel.toList terms <&> \(name, ref) -> S.NamedRef {reversedSegments = nameSegments name, ref = splitReferent ref}
|
||||
let typeNames :: [(S.NamedRef C.Reference.Reference)]
|
||||
typeNames =
|
||||
Rel.toList types
|
||||
<&> ( \(name, ref) ->
|
||||
S.NamedRef {reversedSegments = nameSegments name, ref = Cv.reference1to2 ref}
|
||||
)
|
||||
Ops.rebuildNameIndex termNames typeNames
|
||||
-- | Update the root namespace names index which is used by the share server for serving api
|
||||
-- requests.
|
||||
--
|
||||
-- This version should be used if you've already got the root Branch pre-loaded, otherwise
|
||||
-- it's faster to use 'updateNameLookupIndexFromV2Branch'
|
||||
updateNameLookupIndexFromV1Branch :: Branch Transaction -> Sqlite.Transaction ()
|
||||
updateNameLookupIndexFromV1Branch root = do
|
||||
saveRootNamesIndexV1 (V1Branch.toNames . Branch.head $ root)
|
||||
where
|
||||
nameSegments :: Name -> NonEmpty Text
|
||||
nameSegments = coerce @(NonEmpty NameSegment) @(NonEmpty Text) . Name.reverseSegments
|
||||
splitReferent :: Referent.Referent -> (C.Referent.Referent, Maybe C.Referent.ConstructorType)
|
||||
splitReferent referent = case referent of
|
||||
Referent.Ref {} -> (Cv.referent1to2 referent, Nothing)
|
||||
Referent.Con _ref ct -> (Cv.referent1to2 referent, Just (Cv.constructorType1to2 ct))
|
||||
saveRootNamesIndexV1 :: Names -> Transaction ()
|
||||
saveRootNamesIndexV1 Names {Names.terms, Names.types} = do
|
||||
let termNames :: [(S.NamedRef (C.Referent.Referent, Maybe C.Referent.ConstructorType))]
|
||||
termNames = Rel.toList terms <&> \(name, ref) -> S.NamedRef {reversedSegments = nameSegments name, ref = splitReferent ref}
|
||||
let typeNames :: [(S.NamedRef C.Reference.Reference)]
|
||||
typeNames =
|
||||
Rel.toList types
|
||||
<&> ( \(name, ref) ->
|
||||
S.NamedRef {reversedSegments = nameSegments name, ref = Cv.reference1to2 ref}
|
||||
)
|
||||
Ops.rebuildNameIndex termNames typeNames
|
||||
where
|
||||
nameSegments :: Name -> NonEmpty Text
|
||||
nameSegments = coerce @(NonEmpty NameSegment) @(NonEmpty Text) . Name.reverseSegments
|
||||
splitReferent :: Referent.Referent -> (C.Referent.Referent, Maybe C.Referent.ConstructorType)
|
||||
splitReferent referent = case referent of
|
||||
Referent.Ref {} -> (Cv.referent1to2 referent, Nothing)
|
||||
Referent.Con _ref ct -> (Cv.referent1to2 referent, Just (Cv.constructorType1to2 ct))
|
||||
|
||||
-- | Update the root namespace names index which is used by the share server for serving api
|
||||
-- requests.
|
||||
--
|
||||
-- This version should be used if you don't already have the root Branch pre-loaded,
|
||||
-- If you do, use 'updateNameLookupIndexFromV2Branch' instead.
|
||||
updateNameLookupIndexFromV2Root :: (C.Reference.Reference -> Sqlite.Transaction CT.ConstructorType) -> Sqlite.Transaction ()
|
||||
updateNameLookupIndexFromV2Root getDeclType = do
|
||||
rootHash <- Ops.expectRootCausalHash
|
||||
causalBranch <- Ops.expectCausalBranchByCausalHash rootHash
|
||||
(termNameMap, typeNameMap) <- nameMapsFromV2Branch [] causalBranch
|
||||
let expandedTermNames = Map.toList termNameMap >>= (\(name, refs) -> (name,) <$> Set.toList refs)
|
||||
termNameList <- do
|
||||
for expandedTermNames \(name, ref) -> do
|
||||
refWithCT <- addReferentCT ref
|
||||
pure S.NamedRef {S.reversedSegments = coerce name, S.ref = refWithCT}
|
||||
let typeNameList = do
|
||||
(name, refs) <- Map.toList typeNameMap
|
||||
ref <- Set.toList refs
|
||||
pure $ S.NamedRef {S.reversedSegments = coerce name, S.ref = ref}
|
||||
Ops.rebuildNameIndex termNameList typeNameList
|
||||
where
|
||||
addReferentCT :: C.Referent.Referent -> Transaction (C.Referent.Referent, Maybe C.Referent.ConstructorType)
|
||||
addReferentCT referent = case referent of
|
||||
C.Referent.Ref {} -> pure (referent, Nothing)
|
||||
C.Referent.Con ref _conId -> do
|
||||
ct <- getDeclType ref
|
||||
pure (referent, Just $ Cv.constructorType1to2 ct)
|
||||
|
||||
-- Traverse a v2 branch
|
||||
-- Collects two maps, one with all term names and one with all type names.
|
||||
-- Note that unlike the `Name` type in `unison-core1`, this list of name segments is
|
||||
-- in reverse order, e.g. `["map", "List", "base"]`
|
||||
nameMapsFromV2Branch :: Monad m => [V2Branch.NameSegment] -> V2Branch.CausalBranch m -> m (Map (NonEmpty V2Branch.NameSegment) (Set C.Referent.Referent), Map (NonEmpty V2Branch.NameSegment) (Set C.Reference.Reference))
|
||||
nameMapsFromV2Branch reversedNamePrefix cb = do
|
||||
b <- V2Causal.value cb
|
||||
let (shallowTermNames, shallowTypeNames) = (Map.keysSet <$> V2Branch.terms b, Map.keysSet <$> V2Branch.types b)
|
||||
(prefixedChildTerms, prefixedChildTypes) <-
|
||||
fold <$> (ifor (V2Branch.children b) $ \nameSegment cb -> (nameMapsFromV2Branch (nameSegment : reversedNamePrefix) cb))
|
||||
pure (Map.mapKeys (NEList.:| reversedNamePrefix) shallowTermNames <> prefixedChildTerms, Map.mapKeys (NEList.:| reversedNamePrefix) shallowTypeNames <> prefixedChildTypes)
|
||||
|
||||
mkGetDeclType :: MonadIO m => m (C.Reference.Reference -> Sqlite.Transaction CT.ConstructorType)
|
||||
mkGetDeclType = do
|
||||
declTypeCache <- Cache.semispaceCache 2048
|
||||
pure $ \ref -> do
|
||||
conn <- Sqlite.unsafeGetConnection
|
||||
Sqlite.unsafeIO $ Cache.apply declTypeCache (\ref -> Sqlite.unsafeUnTransaction (getDeclType ref) conn) ref
|
||||
|
@ -17,7 +17,7 @@ import qualified U.Codebase.Reference as V2
|
||||
import Unison.Codebase.Branch (Branch)
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import qualified Unison.Codebase.Editor.Git as Git
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, ReadRepo, WriteRepo)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadGitRemoteNamespace, ReadGitRepo, WriteGitRepo)
|
||||
import Unison.Codebase.GitError (GitCodebaseError, GitProtocolError)
|
||||
import Unison.Codebase.Init.OpenCodebaseError (OpenCodebaseError (..))
|
||||
import Unison.Codebase.Patch (Patch)
|
||||
@ -111,9 +111,9 @@ data Codebase m v a = Codebase
|
||||
syncFromDirectory :: CodebasePath -> SyncMode -> Branch m -> m (),
|
||||
-- | Copy a branch and all of its dependencies from this codebase into the given codebase.
|
||||
syncToDirectory :: CodebasePath -> SyncMode -> Branch m -> m (),
|
||||
viewRemoteBranch' :: forall r. ReadRemoteNamespace -> Git.GitBranchBehavior -> ((Branch m, CodebasePath) -> m r) -> m (Either GitError r),
|
||||
viewRemoteBranch' :: forall r. ReadGitRemoteNamespace -> Git.GitBranchBehavior -> ((Branch m, CodebasePath) -> m r) -> m (Either GitError r),
|
||||
-- | Push the given branch to the given repo, and optionally set it as the root branch.
|
||||
pushGitBranch :: forall e. WriteRepo -> PushGitBranchOpts -> (Branch m -> m (Either e (Branch m))) -> m (Either GitError (Either e (Branch m))),
|
||||
pushGitBranch :: forall e. WriteGitRepo -> PushGitBranchOpts -> (Branch m -> m (Either e (Branch m))) -> m (Either GitError (Either e (Branch m))),
|
||||
-- | @watches k@ returns all of the references @r@ that were previously put by a @putWatch k r t@. @t@ can be
|
||||
-- retrieved by @getWatch k r@.
|
||||
watches :: WK.WatchKind -> m [Reference.Id],
|
||||
@ -204,7 +204,7 @@ data GitError
|
||||
|
||||
instance Exception GitError
|
||||
|
||||
gitErrorFromOpenCodebaseError :: CodebasePath -> ReadRepo -> OpenCodebaseError -> GitSqliteCodebaseError
|
||||
gitErrorFromOpenCodebaseError :: CodebasePath -> ReadGitRepo -> OpenCodebaseError -> GitSqliteCodebaseError
|
||||
gitErrorFromOpenCodebaseError path repo = \case
|
||||
OpenCodebaseDoesntExist -> NoDatabaseFile repo path
|
||||
OpenCodebaseUnknownSchemaVersion v ->
|
||||
|
@ -184,11 +184,6 @@ checkForDuplicateTermsAndConstructors uf = do
|
||||
-- Or it is a binding like:
|
||||
-- foo : Nat -> Nat
|
||||
-- foo x = x + 42
|
||||
-- Or it is a namespace like:
|
||||
-- namespace Woot where
|
||||
-- x = 42
|
||||
-- y = 17
|
||||
-- which parses as [(Woot.x, 42), (Woot.y, 17)]
|
||||
|
||||
data Stanza v term
|
||||
= WatchBinding UF.WatchKind Ann ((Ann, v), term)
|
||||
|
@ -1,6 +1,7 @@
|
||||
{-# LANGUAGE PartialTypeSignatures #-}
|
||||
{-# LANGUAGE ViewPatterns #-}
|
||||
|
||||
-- | Description: Converts V1 types to the V2 hashing types
|
||||
module Unison.Hashing.V2.Convert
|
||||
( ResolutionResult,
|
||||
hashBranch0,
|
||||
@ -130,8 +131,8 @@ m2hTerm = ABT.transformM \case
|
||||
Memory.Term.If c t f -> pure (Hashing.Term.If c t f)
|
||||
Memory.Term.Or p q -> pure (Hashing.Term.Or p q)
|
||||
Memory.Term.Lam a -> pure (Hashing.Term.Lam a)
|
||||
Memory.Term.LetRec isTop bs body -> pure (Hashing.Term.LetRec isTop bs body)
|
||||
Memory.Term.Let isTop b body -> pure (Hashing.Term.Let isTop b body)
|
||||
Memory.Term.LetRec _isTop bs body -> pure (Hashing.Term.LetRec bs body)
|
||||
Memory.Term.Let _isTop b body -> pure (Hashing.Term.Let b body)
|
||||
Memory.Term.Match scr cases -> pure (Hashing.Term.Match scr (fmap m2hMatchCase cases))
|
||||
Memory.Term.TermLink r -> Hashing.Term.TermLink <$> m2hReferent r
|
||||
Memory.Term.TypeLink r -> pure (Hashing.Term.TypeLink (m2hReference r))
|
||||
@ -191,8 +192,8 @@ h2mTerm getCT = ABT.transform \case
|
||||
Hashing.Term.And p q -> Memory.Term.And p q
|
||||
Hashing.Term.Or p q -> Memory.Term.Or p q
|
||||
Hashing.Term.Lam a -> Memory.Term.Lam a
|
||||
Hashing.Term.LetRec isTop bs body -> Memory.Term.LetRec isTop bs body
|
||||
Hashing.Term.Let isTop b body -> Memory.Term.Let isTop b body
|
||||
Hashing.Term.LetRec bs body -> Memory.Term.LetRec False bs body
|
||||
Hashing.Term.Let b body -> Memory.Term.Let False b body
|
||||
Hashing.Term.Match scr cases -> Memory.Term.Match scr (h2mMatchCase <$> cases)
|
||||
Hashing.Term.TermLink r -> Memory.Term.TermLink (h2mReferent getCT r)
|
||||
Hashing.Term.TypeLink r -> Memory.Term.TypeLink (h2mReference r)
|
||||
|
@ -1410,11 +1410,9 @@ prettyParseError s = \case
|
||||
Code
|
||||
" + 1",
|
||||
"\n - An `ability` declaration, like "
|
||||
<> style Code "ability Foo where ...",
|
||||
<> style Code "unique ability Foo where ...",
|
||||
"\n - A `type` declaration, like "
|
||||
<> style Code "structural type Optional a = None | Some a",
|
||||
"\n - A `namespace` declaration, like "
|
||||
<> style Code "namespace Seq where ...",
|
||||
"\n"
|
||||
]
|
||||
where
|
||||
|
@ -2311,6 +2311,7 @@ declareForeigns = do
|
||||
declareHashAlgorithm "Sha3_256" Hash.SHA3_256
|
||||
declareHashAlgorithm "Sha2_512" Hash.SHA512
|
||||
declareHashAlgorithm "Sha2_256" Hash.SHA256
|
||||
declareHashAlgorithm "Sha1" Hash.SHA1
|
||||
declareHashAlgorithm "Blake2b_512" Hash.Blake2b_512
|
||||
declareHashAlgorithm "Blake2b_256" Hash.Blake2b_256
|
||||
declareHashAlgorithm "Blake2s_256" Hash.Blake2s_256
|
||||
|
146
parser-typechecker/src/Unison/Share/Types.hs
Normal file
146
parser-typechecker/src/Unison/Share/Types.hs
Normal file
@ -0,0 +1,146 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
-- | Types related to Share and Codeservers.
|
||||
module Unison.Share.Types
|
||||
( CodeserverURI (..),
|
||||
CodeserverId (..),
|
||||
Scheme (..),
|
||||
codeserverFromURI,
|
||||
codeserverIdFromURI,
|
||||
codeserverToURI,
|
||||
codeserverIdFromCodeserverURI,
|
||||
codeserverBaseURL,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson
|
||||
import qualified Data.List as List
|
||||
import qualified Data.List.Extra as List
|
||||
import Data.Text
|
||||
import qualified Data.Text as Text
|
||||
import Network.URI
|
||||
import qualified Servant.Client as Servant
|
||||
import Unison.Prelude
|
||||
|
||||
data Scheme = Http | Https
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
-- | This type is expanded out into all of its fields because we require certain pieces
|
||||
-- which are optional in a URI, and also to make it more typesafe to eventually convert into a
|
||||
-- BaseURL for servant clients.
|
||||
data CodeserverURI = CodeserverURI
|
||||
{ codeserverScheme :: Scheme,
|
||||
codeserverUserInfo :: String,
|
||||
codeserverRegName :: String,
|
||||
-- A custom port, if one was specified.
|
||||
codeserverPort :: Maybe Int,
|
||||
codeserverPath :: [String]
|
||||
}
|
||||
deriving stock (Eq, Ord)
|
||||
|
||||
instance Show CodeserverURI where
|
||||
show = show . codeserverToURI
|
||||
|
||||
codeserverToURI :: CodeserverURI -> URI
|
||||
codeserverToURI cs@(CodeserverURI {..}) =
|
||||
let scheme = case codeserverScheme of
|
||||
Http -> "http:"
|
||||
Https -> "https:"
|
||||
authority = codeserverAuthority cs
|
||||
in URI
|
||||
{ uriScheme = scheme,
|
||||
uriAuthority = Just authority,
|
||||
uriPath = case codeserverPath of
|
||||
[] -> ""
|
||||
segs -> "/" <> List.intercalate "/" segs,
|
||||
uriQuery = "",
|
||||
uriFragment = ""
|
||||
}
|
||||
|
||||
codeserverAuthority :: CodeserverURI -> URIAuth
|
||||
codeserverAuthority (CodeserverURI {..}) =
|
||||
URIAuth
|
||||
{ uriUserInfo = codeserverUserInfo,
|
||||
uriPort = case codeserverPort of
|
||||
Nothing -> ""
|
||||
Just p -> ":" <> show p,
|
||||
uriRegName = codeserverRegName
|
||||
}
|
||||
|
||||
-- |
|
||||
-- >>> import Data.Maybe (fromJust)
|
||||
-- >>> codeserverFromURI . fromJust $ parseURI "http://localhost:8080"
|
||||
-- Just http://localhost:8080
|
||||
-- >>> codeserverFromURI . fromJust $ parseURI "http://localhost:80"
|
||||
-- Just http://localhost:80
|
||||
-- >>> codeserverFromURI . fromJust $ parseURI "https://share.unison-lang.org/api"
|
||||
-- Just https://share.unison-lang.org/api
|
||||
-- >>> codeserverFromURI . fromJust $ parseURI "http://share.unison-lang.org/api"
|
||||
-- Just http://share.unison-lang.org/api
|
||||
codeserverFromURI :: URI -> Maybe CodeserverURI
|
||||
codeserverFromURI URI {..} = do
|
||||
URIAuth {uriUserInfo, uriRegName, uriPort} <- uriAuthority
|
||||
scheme <- case uriScheme of
|
||||
"http:" -> Just Http
|
||||
"https:" -> Just Https
|
||||
_ -> Nothing
|
||||
let port = case uriPort of
|
||||
(':' : p) -> readMaybe p
|
||||
_ -> Nothing
|
||||
pure $
|
||||
CodeserverURI
|
||||
{ codeserverScheme = scheme,
|
||||
codeserverUserInfo = uriUserInfo,
|
||||
codeserverRegName = uriRegName,
|
||||
codeserverPort = port,
|
||||
codeserverPath =
|
||||
let unprefixed =
|
||||
case uriPath of
|
||||
('/' : path) -> path
|
||||
path -> path
|
||||
in case List.splitOn "/" unprefixed of
|
||||
[""] -> []
|
||||
p -> p
|
||||
}
|
||||
|
||||
-- | This is distinct from the codeserver URI in that we store credentials by a normalized ID, since it's
|
||||
-- much easier to look up that way than from an arbitrary path.
|
||||
-- We may wish to use explicitly named configurations in the future.
|
||||
-- This currently uses a stringified uriAuthority.
|
||||
newtype CodeserverId = CodeserverId {codeserverId :: Text}
|
||||
deriving newtype (Show, Eq, Ord, ToJSON, FromJSON, ToJSONKey, FromJSONKey)
|
||||
|
||||
-- | Gets the part of the CodeserverURI that we use for identifying that codeserver in
|
||||
-- credentials files.
|
||||
--
|
||||
-- >>> import Data.Maybe (fromJust)
|
||||
-- >>> import Network.URI (parseURI)
|
||||
-- >>> codeserverIdFromURI (CodeserverURI . fromJust $ parseURI "http://localhost:5424/api")
|
||||
-- >>> codeserverIdFromURI (CodeserverURI . fromJust $ parseURI "https://share.unison-lang.org/api")
|
||||
-- Right "localhost"
|
||||
-- Right "share.unison-lang.org"
|
||||
codeserverIdFromURI :: URI -> Either Text CodeserverId
|
||||
codeserverIdFromURI uri =
|
||||
case uriAuthority uri of
|
||||
Nothing -> Left $ "No URI Authority for URI " <> tShow uri
|
||||
Just ua -> pure $ codeserverIdFromURIAuth ua
|
||||
|
||||
-- | Builds a CodeserverId from a URIAuth
|
||||
codeserverIdFromURIAuth :: URIAuth -> CodeserverId
|
||||
codeserverIdFromURIAuth ua =
|
||||
(CodeserverId (Text.pack $ uriUserInfo ua <> uriRegName ua <> uriPort ua))
|
||||
|
||||
-- | Gets the CodeserverId for a given CodeserverURI
|
||||
codeserverIdFromCodeserverURI :: CodeserverURI -> CodeserverId
|
||||
codeserverIdFromCodeserverURI =
|
||||
codeserverIdFromURIAuth . codeserverAuthority
|
||||
|
||||
-- | Builds a servant-compatible BaseUrl for a given CodeserverURI.
|
||||
codeserverBaseURL :: CodeserverURI -> Servant.BaseUrl
|
||||
codeserverBaseURL (CodeserverURI {..}) =
|
||||
let (scheme, defaultPort) = case codeserverScheme of
|
||||
Https -> (Servant.Https, 443)
|
||||
Http -> (Servant.Http, 80)
|
||||
host = codeserverUserInfo <> codeserverRegName
|
||||
in Servant.BaseUrl scheme host (fromMaybe defaultPort codeserverPort) (List.intercalate "/" codeserverPath)
|
@ -1,6 +1,6 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.7.
|
||||
-- This file has been generated from package.yaml by hpack version 0.34.4.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
@ -72,6 +72,7 @@ library
|
||||
Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema1To2.DbHelpers
|
||||
Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema2To3
|
||||
Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema3To4
|
||||
Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema4To5
|
||||
Unison.Codebase.SqliteCodebase.Operations
|
||||
Unison.Codebase.SqliteCodebase.Paths
|
||||
Unison.Codebase.SqliteCodebase.SyncEphemeral
|
||||
@ -86,23 +87,7 @@ library
|
||||
Unison.DeclPrinter
|
||||
Unison.FileParser
|
||||
Unison.FileParsers
|
||||
Unison.Hashing.V2.ABT
|
||||
Unison.Hashing.V2.Branch
|
||||
Unison.Hashing.V2.Causal
|
||||
Unison.Hashing.V2.Convert
|
||||
Unison.Hashing.V2.DataDeclaration
|
||||
Unison.Hashing.V2.Hashable
|
||||
Unison.Hashing.V2.Kind
|
||||
Unison.Hashing.V2.Patch
|
||||
Unison.Hashing.V2.Pattern
|
||||
Unison.Hashing.V2.Reference
|
||||
Unison.Hashing.V2.Reference.Util
|
||||
Unison.Hashing.V2.Referent
|
||||
Unison.Hashing.V2.Term
|
||||
Unison.Hashing.V2.TermEdit
|
||||
Unison.Hashing.V2.Tokenizable
|
||||
Unison.Hashing.V2.Type
|
||||
Unison.Hashing.V2.TypeEdit
|
||||
Unison.Lexer
|
||||
Unison.NamePrinter
|
||||
Unison.Parser
|
||||
@ -134,6 +119,7 @@ library
|
||||
Unison.Runtime.SparseVector
|
||||
Unison.Runtime.Stack
|
||||
Unison.Runtime.Vector
|
||||
Unison.Share.Types
|
||||
Unison.TermParser
|
||||
Unison.TermPrinter
|
||||
Unison.Typechecker
|
||||
@ -178,6 +164,7 @@ library
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
DuplicateRecordFields
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
@ -244,6 +231,7 @@ library
|
||||
, natural-transformation
|
||||
, network
|
||||
, network-simple
|
||||
, network-uri
|
||||
, nonempty-containers
|
||||
, open-browser
|
||||
, openapi3
|
||||
@ -261,6 +249,7 @@ library
|
||||
, safe-exceptions
|
||||
, semialign
|
||||
, servant
|
||||
, servant-client
|
||||
, servant-docs
|
||||
, servant-openapi3
|
||||
, servant-server
|
||||
@ -279,13 +268,16 @@ library
|
||||
, unicode-show
|
||||
, unison-codebase
|
||||
, unison-codebase-sqlite
|
||||
, unison-codebase-sqlite-hashing-v2
|
||||
, unison-codebase-sync
|
||||
, unison-core
|
||||
, unison-core1
|
||||
, unison-hashing-v2
|
||||
, unison-prelude
|
||||
, unison-pretty-printer
|
||||
, unison-sqlite
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
, unison-util-relation
|
||||
, unison-util-serialization
|
||||
, unliftio
|
||||
@ -350,6 +342,7 @@ test-suite parser-typechecker-tests
|
||||
DerivingStrategies
|
||||
DerivingVia
|
||||
DoAndIfThenElse
|
||||
DuplicateRecordFields
|
||||
FlexibleContexts
|
||||
FlexibleInstances
|
||||
GeneralizedNewtypeDeriving
|
||||
@ -365,37 +358,122 @@ test-suite parser-typechecker-tests
|
||||
ViewPatterns
|
||||
ghc-options: -Wall -O0 -fno-warn-name-shadowing -fno-warn-missing-pattern-synonym-signatures -W -threaded -rtsopts "-with-rtsopts=-N -T" -v0
|
||||
build-depends:
|
||||
async
|
||||
ListLike
|
||||
, NanoID
|
||||
, aeson
|
||||
, ansi-terminal
|
||||
, async
|
||||
, base
|
||||
, base16 >=0.2.1.0
|
||||
, base64-bytestring
|
||||
, basement
|
||||
, binary
|
||||
, bytes
|
||||
, bytestring
|
||||
, bytestring-to-vector
|
||||
, cereal
|
||||
, clock
|
||||
, code-page
|
||||
, containers
|
||||
, configurator
|
||||
, containers >=0.6.3
|
||||
, cryptonite
|
||||
, data-default
|
||||
, data-memocombinators
|
||||
, deepseq
|
||||
, directory
|
||||
, easytest
|
||||
, either
|
||||
, errors
|
||||
, exceptions
|
||||
, extra
|
||||
, filemanip
|
||||
, filepath
|
||||
, fingertree
|
||||
, fsnotify
|
||||
, fuzzyfind
|
||||
, generic-lens
|
||||
, generic-monoid
|
||||
, hashable
|
||||
, hashtables
|
||||
, haskeline
|
||||
, here
|
||||
, http-client
|
||||
, http-media
|
||||
, http-types
|
||||
, lens
|
||||
, lucid
|
||||
, megaparsec
|
||||
, memory
|
||||
, mmorph
|
||||
, monad-validate
|
||||
, mtl
|
||||
, mutable-containers
|
||||
, mwc-random
|
||||
, natural-transformation
|
||||
, network
|
||||
, network-simple
|
||||
, network-uri
|
||||
, nonempty-containers
|
||||
, open-browser
|
||||
, openapi3
|
||||
, optparse-applicative >=0.16.1.0
|
||||
, pem
|
||||
, prelude-extras
|
||||
, pretty-simple
|
||||
, primitive
|
||||
, process
|
||||
, random >=1.2.0
|
||||
, raw-strings-qq
|
||||
, regex-base
|
||||
, regex-tdfa
|
||||
, safe
|
||||
, safe-exceptions
|
||||
, semialign
|
||||
, servant
|
||||
, servant-client
|
||||
, servant-docs
|
||||
, servant-openapi3
|
||||
, servant-server
|
||||
, shellmet
|
||||
, split
|
||||
, stm
|
||||
, strings
|
||||
, tagged
|
||||
, temporary
|
||||
, terminal-size
|
||||
, text
|
||||
, text-short
|
||||
, these
|
||||
, time
|
||||
, tls
|
||||
, transformers
|
||||
, unicode-show
|
||||
, unison-codebase
|
||||
, unison-codebase-sqlite
|
||||
, unison-codebase-sqlite-hashing-v2
|
||||
, unison-codebase-sync
|
||||
, unison-core
|
||||
, unison-core1
|
||||
, unison-hashing-v2
|
||||
, unison-parser-typechecker
|
||||
, unison-prelude
|
||||
, unison-pretty-printer
|
||||
, unison-sqlite
|
||||
, unison-util
|
||||
, unison-util-base32hex
|
||||
, unison-util-relation
|
||||
, unison-util-serialization
|
||||
, unliftio
|
||||
, uri-encode
|
||||
, utf8-string
|
||||
, vector
|
||||
, wai
|
||||
, warp
|
||||
, x509
|
||||
, x509-store
|
||||
, x509-system
|
||||
, yaml
|
||||
, zlib
|
||||
if flag(optimized)
|
||||
ghc-options: -funbox-strict-fields -O2
|
||||
default-language: Haskell2010
|
||||
|
@ -14,9 +14,11 @@ packages:
|
||||
- parser-typechecker
|
||||
- unison-core
|
||||
- unison-cli
|
||||
- unison-hashing-v2
|
||||
- unison-share-api
|
||||
- codebase2/codebase
|
||||
- codebase2/codebase-sqlite
|
||||
- codebase2/codebase-sqlite-hashing-v2
|
||||
- codebase2/codebase-sync
|
||||
- codebase2/core
|
||||
- codebase2/util
|
||||
@ -24,6 +26,9 @@ packages:
|
||||
- codebase2/util-term
|
||||
- lib/unison-prelude
|
||||
- lib/unison-sqlite
|
||||
- lib/unison-util-base32hex
|
||||
- lib/unison-util-base32hex-orphans-aeson
|
||||
- lib/unison-util-base32hex-orphans-sqlite
|
||||
- lib/unison-util-relation
|
||||
- lib/unison-pretty-printer
|
||||
|
||||
@ -33,8 +38,6 @@ resolver: lts-18.28
|
||||
extra-deps:
|
||||
- github: unisonweb/configurator
|
||||
commit: e47e9e9fe1f576f8c835183b9def52d73c01327a
|
||||
- github: unisonweb/haskeline
|
||||
commit: 2944b11d19ee034c48276edc991736105c9d6143
|
||||
- github: unisonweb/shellmet
|
||||
commit: 2fd348592c8f51bb4c0ca6ba4bc8e38668913746
|
||||
- guid-0.1.0@sha256:a7c975be473f6f142d5cc1b39bc807a99043d20b1bb0873fdfe7a3ce84d2faf1,1078
|
||||
@ -44,6 +47,8 @@ extra-deps:
|
||||
- fuzzyfind-3.0.0@sha256:d79a5d3ed194dd436c6b839bf187211d880cf773b2febaca456e5ccf93f5ac65,1814
|
||||
- monad-validate-1.2.0.0@sha256:9850f408431098b28806dd464b6825a88a0b56c84f380d7fe0454c1df9d6f881,3505
|
||||
- NanoID-3.1.0@sha256:9118ab00e8650b5a56a10c90295d357eb77a8057a598b7e56dfedc9c6d53c77d,1524
|
||||
# 2.3.27 bundles sqlite >=3.35.0, needed for 'delete returning'
|
||||
- direct-sqlite-2.3.27
|
||||
# not in lts-18.13
|
||||
- recover-rtti-0.4.0.0@sha256:2ce1e031ec0e34d736fa45f0149bbd55026f614939dc90ffd14a9c5d24093ff4,4423
|
||||
- lock-file-0.7.0.0@sha256:3ad84b5e454145e1d928063b56abb96db24a99a21b493989520e58fa0ab37b00,4484
|
||||
|
@ -15,17 +15,6 @@ packages:
|
||||
sha256: 90547cd983fd15ebdc803e057d3ef8735fe93a75e29a00f8a74eadc13ee0f6e9
|
||||
original:
|
||||
url: https://github.com/unisonweb/configurator/archive/e47e9e9fe1f576f8c835183b9def52d73c01327a.tar.gz
|
||||
- completed:
|
||||
size: 75098
|
||||
url: https://github.com/unisonweb/haskeline/archive/2944b11d19ee034c48276edc991736105c9d6143.tar.gz
|
||||
name: haskeline
|
||||
version: 0.7.5.0
|
||||
sha256: 6d44207a4e94a16bc99d13dccbbb79bf676190c0476436b03235eeeaf6c72f9d
|
||||
pantry-tree:
|
||||
size: 3717
|
||||
sha256: 878c7fb5801ec7418761a49b529ca3fdab274f22707c7d2759f2b3a2df06c3ea
|
||||
original:
|
||||
url: https://github.com/unisonweb/haskeline/archive/2944b11d19ee034c48276edc991736105c9d6143.tar.gz
|
||||
- completed:
|
||||
size: 10460
|
||||
url: https://github.com/unisonweb/shellmet/archive/2fd348592c8f51bb4c0ca6ba4bc8e38668913746.tar.gz
|
||||
@ -86,6 +75,13 @@ packages:
|
||||
sha256: d33d603a2f0d1a220ff0d5e7edb6273def89120e6bb958c2d836cae89e788334
|
||||
original:
|
||||
hackage: NanoID-3.1.0@sha256:9118ab00e8650b5a56a10c90295d357eb77a8057a598b7e56dfedc9c6d53c77d,1524
|
||||
- completed:
|
||||
hackage: direct-sqlite-2.3.27@sha256:94207d3018da3bda84bc6ce00d2c0236ced7edb37afbd726ed2a0bfa236e149b,3771
|
||||
pantry-tree:
|
||||
size: 770
|
||||
sha256: c7f5afe70db567e2cf9f3119b49f4b402705e6bd08ed8ba98747a64a8a0bef41
|
||||
original:
|
||||
hackage: direct-sqlite-2.3.27
|
||||
- completed:
|
||||
hackage: recover-rtti-0.4.0.0@sha256:2ce1e031ec0e34d736fa45f0149bbd55026f614939dc90ffd14a9c5d24093ff4,4423
|
||||
pantry-tree:
|
||||
|
@ -10,56 +10,65 @@ flags:
|
||||
ghc-options: -Wall
|
||||
|
||||
dependencies:
|
||||
- semialign
|
||||
- these
|
||||
- ListLike
|
||||
- aeson
|
||||
- aeson-pretty
|
||||
- async
|
||||
- base
|
||||
- bytes
|
||||
- bytestring
|
||||
- concurrent-output
|
||||
- configurator
|
||||
- containers >= 0.6.3
|
||||
- nonempty-containers
|
||||
- cryptonite
|
||||
- directory
|
||||
- either
|
||||
- errors
|
||||
- exceptions
|
||||
- extra
|
||||
- filepath
|
||||
- generic-lens
|
||||
- haskeline
|
||||
- http-client >= 0.7.6
|
||||
- http-client-tls
|
||||
- http-types
|
||||
- jwt
|
||||
- lens
|
||||
- megaparsec >= 5.0.0 && < 7.0.0
|
||||
- lock-file
|
||||
- megaparsec
|
||||
- memory
|
||||
- mtl
|
||||
- transformers
|
||||
- network-uri
|
||||
- nonempty-containers
|
||||
- open-browser
|
||||
- pretty-simple
|
||||
- random >= 1.2.0
|
||||
- regex-tdfa
|
||||
- semialign
|
||||
- servant
|
||||
- servant-client
|
||||
- stm
|
||||
- text
|
||||
- these
|
||||
- time
|
||||
- transformers
|
||||
- unison-codebase
|
||||
- unison-codebase-sqlite
|
||||
- unison-codebase-sqlite-hashing-v2
|
||||
- unison-sqlite
|
||||
- unison-core1
|
||||
- unison-parser-typechecker
|
||||
- unison-prelude
|
||||
- unison-util
|
||||
- unison-util-relation
|
||||
- unison-pretty-printer
|
||||
- unliftio
|
||||
- network-uri
|
||||
- aeson
|
||||
- aeson-pretty
|
||||
- http-client >= 0.7.6
|
||||
- http-client-tls
|
||||
- http-types
|
||||
- warp
|
||||
- wai
|
||||
- memory
|
||||
- time
|
||||
- lock-file
|
||||
- jwt
|
||||
- either
|
||||
- unison-share-api
|
||||
- servant-client
|
||||
- servant
|
||||
- unison-sqlite
|
||||
- unison-util
|
||||
- unison-util-base32hex
|
||||
- unison-util-relation
|
||||
- unliftio
|
||||
- vector
|
||||
- wai
|
||||
- warp
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
@ -144,12 +153,14 @@ default-extensions:
|
||||
- DeriveGeneric
|
||||
- DerivingStrategies
|
||||
- DoAndIfThenElse
|
||||
- DuplicateRecordFields
|
||||
- FlexibleContexts
|
||||
- FlexibleInstances
|
||||
- GeneralizedNewtypeDeriving
|
||||
- LambdaCase
|
||||
- MultiParamTypeClasses
|
||||
- NamedFieldPuns
|
||||
- OverloadedLabels
|
||||
- OverloadedStrings
|
||||
- PatternSynonyms
|
||||
- RankNTypes
|
||||
|
@ -1,10 +1,10 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
|
||||
module Unison.Auth.CredentialManager
|
||||
( saveTokens,
|
||||
( saveCredentials,
|
||||
CredentialManager,
|
||||
newCredentialManager,
|
||||
getTokens,
|
||||
getCredentials,
|
||||
)
|
||||
where
|
||||
|
||||
@ -22,9 +22,9 @@ import qualified UnliftIO
|
||||
newtype CredentialManager = CredentialManager (UnliftIO.MVar Credentials)
|
||||
|
||||
-- | Saves credentials to the active profile.
|
||||
saveTokens :: UnliftIO.MonadUnliftIO m => CredentialManager -> CodeserverId -> Tokens -> m ()
|
||||
saveTokens credManager aud tokens = do
|
||||
void . modifyCredentials credManager $ setActiveTokens aud tokens
|
||||
saveCredentials :: UnliftIO.MonadUnliftIO m => CredentialManager -> CodeserverId -> CodeserverCredentials -> m ()
|
||||
saveCredentials credManager aud creds = do
|
||||
void . modifyCredentials credManager $ setCodeserverCredentials aud creds
|
||||
|
||||
-- | Atomically update the credential storage file, and update the in-memory cache.
|
||||
modifyCredentials :: UnliftIO.MonadUnliftIO m => CredentialManager -> (Credentials -> Credentials) -> m Credentials
|
||||
@ -33,10 +33,10 @@ modifyCredentials (CredentialManager credsVar) f = do
|
||||
newCreds <- atomicallyModifyCredentialsFile f
|
||||
pure (newCreds, newCreds)
|
||||
|
||||
getTokens :: MonadIO m => CredentialManager -> CodeserverId -> m (Either CredentialFailure Tokens)
|
||||
getTokens (CredentialManager credsVar) aud = do
|
||||
getCredentials :: MonadIO m => CredentialManager -> CodeserverId -> m (Either CredentialFailure CodeserverCredentials)
|
||||
getCredentials (CredentialManager credsVar) aud = do
|
||||
creds <- UnliftIO.readMVar credsVar
|
||||
pure $ getActiveTokens aud creds
|
||||
pure $ getCodeserverCredentials aud creds
|
||||
|
||||
newCredentialManager :: MonadIO m => m CredentialManager
|
||||
newCredentialManager = do
|
||||
|
@ -3,22 +3,23 @@ module Unison.Auth.Discovery where
|
||||
import qualified Data.Aeson as Aeson
|
||||
import qualified Data.Text as Text
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import qualified Network.HTTP.Client.TLS as HTTP
|
||||
import Network.URI
|
||||
import Unison.Auth.Types
|
||||
import Unison.Prelude
|
||||
import Unison.Share.Types (CodeserverURI (..), codeserverToURI)
|
||||
import qualified UnliftIO
|
||||
|
||||
discoveryURI :: CodeserverURI -> URI
|
||||
discoveryURI cs =
|
||||
discoveryURIForCodeserver :: CodeserverURI -> URI
|
||||
discoveryURIForCodeserver cs =
|
||||
let uri = codeserverToURI cs
|
||||
in uri {uriPath = uriPath uri <> "/.well-known/openid-configuration"}
|
||||
|
||||
discoveryForCodeserver :: MonadIO m => HTTP.Manager -> CodeserverURI -> m (Either CredentialFailure DiscoveryDoc)
|
||||
discoveryForCodeserver httpClient host = liftIO . UnliftIO.try @_ @CredentialFailure $ do
|
||||
let uri = discoveryURI host
|
||||
req <- HTTP.requestFromURI uri
|
||||
resp <- HTTP.httpLbs req httpClient
|
||||
fetchDiscoveryDoc :: MonadIO m => URI -> m (Either CredentialFailure DiscoveryDoc)
|
||||
fetchDiscoveryDoc discoveryURI = liftIO . UnliftIO.try @_ @CredentialFailure $ do
|
||||
unauthenticatedHttpClient <- HTTP.getGlobalManager
|
||||
req <- HTTP.requestFromURI discoveryURI
|
||||
resp <- HTTP.httpLbs req unauthenticatedHttpClient
|
||||
case Aeson.eitherDecode (HTTP.responseBody $ resp) of
|
||||
Left err -> UnliftIO.throwIO $ InvalidDiscoveryDocument uri (Text.pack err)
|
||||
Left err -> UnliftIO.throwIO $ InvalidDiscoveryDocument discoveryURI (Text.pack err)
|
||||
Right doc -> pure doc
|
||||
|
@ -1,41 +1,52 @@
|
||||
module Unison.Auth.HTTPClient (newAuthorizedHTTPClient, AuthorizedHttpClient (..)) where
|
||||
module Unison.Auth.HTTPClient (newAuthenticatedHTTPClient, AuthenticatedHttpClient (..)) where
|
||||
|
||||
import qualified Data.Text.Encoding as Text
|
||||
import Network.HTTP.Client (Request)
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import qualified Network.HTTP.Client.TLS as HTTP
|
||||
import Unison.Auth.CredentialManager (CredentialManager)
|
||||
import Unison.Auth.Tokens (TokenProvider, newTokenProvider)
|
||||
import Unison.Codebase.Editor.Command (UCMVersion)
|
||||
import Unison.Auth.Tokens (TokenProvider)
|
||||
import Unison.Codebase.Editor.Output (Output)
|
||||
import qualified Unison.Codebase.Editor.Output as Output
|
||||
import Unison.Codebase.Editor.UCMVersion (UCMVersion)
|
||||
import Unison.Prelude
|
||||
import Unison.Share.Types (codeserverIdFromURI)
|
||||
import qualified Unison.Util.HTTP as HTTP
|
||||
|
||||
-- | Newtype to delineate HTTP Managers with access-token logic.
|
||||
newtype AuthorizedHttpClient = AuthorizedHttpClient HTTP.Manager
|
||||
newtype AuthenticatedHttpClient = AuthenticatedHttpClient HTTP.Manager
|
||||
|
||||
-- | Returns a new http manager which applies the appropriate Authorization header to
|
||||
-- any hosts our UCM is authenticated with.
|
||||
newAuthorizedHTTPClient :: MonadIO m => CredentialManager -> UCMVersion -> m HTTP.Manager
|
||||
newAuthorizedHTTPClient credsMan ucmVersion = liftIO $ do
|
||||
let tokenProvider = newTokenProvider credsMan
|
||||
newAuthenticatedHTTPClient :: MonadIO m => (Output v -> IO ()) -> TokenProvider -> UCMVersion -> m AuthenticatedHttpClient
|
||||
newAuthenticatedHTTPClient responder tokenProvider ucmVersion = liftIO $ do
|
||||
let managerSettings =
|
||||
HTTP.tlsManagerSettings
|
||||
& HTTP.addRequestMiddleware (authMiddleware tokenProvider)
|
||||
& HTTP.addRequestMiddleware (authMiddleware responder tokenProvider)
|
||||
& HTTP.setUserAgent (HTTP.ucmUserAgent ucmVersion)
|
||||
HTTP.newTlsManagerWith managerSettings
|
||||
AuthenticatedHttpClient <$> HTTP.newTlsManagerWith managerSettings
|
||||
|
||||
-- | Adds Bearer tokens to requests according to their host.
|
||||
-- If a CredentialFailure occurs (failure to refresh a token), auth is simply omitted,
|
||||
-- and the request is likely to trigger a 401 response which the caller can detect and initiate a re-auth.
|
||||
--
|
||||
-- If a host isn't associated with any credentials auth is omitted.
|
||||
authMiddleware :: TokenProvider -> (Request -> IO Request)
|
||||
authMiddleware tokenProvider req = do
|
||||
case (codeserverIdFromURI $ (HTTP.getUri req)) of
|
||||
Left _ -> pure req
|
||||
Right codeserverHost -> do
|
||||
result <- tokenProvider codeserverHost
|
||||
case result of
|
||||
Right token -> pure $ HTTP.applyBearerAuth (Text.encodeUtf8 token) req
|
||||
authMiddleware :: (Output v -> IO ()) -> TokenProvider -> (Request -> IO Request)
|
||||
authMiddleware responder tokenProvider req = do
|
||||
-- The http manager "may run this function multiple times" when preparing a request.
|
||||
-- We may wish to look into a better way to attach auth to our requests in middleware, but
|
||||
-- this is a simple fix that works for now.
|
||||
-- https://github.com/snoyberg/http-client/issues/350
|
||||
case Prelude.lookup ("Authorization") (HTTP.requestHeaders req) of
|
||||
Just _ -> pure req
|
||||
Nothing -> do
|
||||
case codeserverIdFromURI $ (HTTP.getUri req) of
|
||||
-- If we can't identify an appropriate codeserver we pass it through without any auth.
|
||||
Left _ -> pure req
|
||||
Right codeserverHost -> do
|
||||
tokenProvider codeserverHost >>= \case
|
||||
Right token -> do
|
||||
let newReq = HTTP.applyBearerAuth (Text.encodeUtf8 token) req
|
||||
pure newReq
|
||||
Left err -> do
|
||||
responder (Output.CredentialFailureMsg err)
|
||||
pure req
|
||||
|
@ -1,6 +1,9 @@
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
module Unison.Auth.OAuth (authenticateCodeserver) where
|
||||
module Unison.Auth.OAuth
|
||||
( authenticateCodeserver,
|
||||
)
|
||||
where
|
||||
|
||||
import qualified Crypto.Hash as Crypto
|
||||
import Crypto.Random (getRandomBytes)
|
||||
@ -17,8 +20,8 @@ import Network.URI
|
||||
import Network.Wai
|
||||
import qualified Network.Wai as Wai
|
||||
import qualified Network.Wai.Handler.Warp as Warp
|
||||
import Unison.Auth.CredentialManager (CredentialManager, saveTokens)
|
||||
import Unison.Auth.Discovery (discoveryForCodeserver)
|
||||
import Unison.Auth.CredentialManager (CredentialManager, saveCredentials)
|
||||
import Unison.Auth.Discovery (discoveryURIForCodeserver, fetchDiscoveryDoc)
|
||||
import Unison.Auth.Types
|
||||
import Unison.Codebase.Editor.HandleInput.LoopState (MonadCommand, respond)
|
||||
import qualified Unison.Codebase.Editor.Output as Output
|
||||
@ -34,23 +37,27 @@ ucmOAuthClientID = "ucm"
|
||||
-- | A server in the format expected for a Wai Application
|
||||
-- This is a temporary server which is spun up only until we get a code back from the
|
||||
-- auth server.
|
||||
authTransferServer :: (Code -> IO Response) -> Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
|
||||
authTransferServer :: (Code -> Maybe URI -> IO Response) -> Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
|
||||
authTransferServer callback req respond =
|
||||
case (requestMethod req, pathInfo req, getCodeQuery req) of
|
||||
("GET", ["redirect"], Just code) -> do
|
||||
callback code >>= respond
|
||||
case (requestMethod req, pathInfo req, getQueryParams req) of
|
||||
("GET", ["redirect"], (Just code, maybeNextURI)) -> do
|
||||
callback code maybeNextURI >>= respond
|
||||
_ -> respond (responseLBS status404 [] "Not Found")
|
||||
where
|
||||
getCodeQuery req = do
|
||||
code <- join $ Prelude.lookup "code" (queryString req)
|
||||
pure $ Text.decodeUtf8 code
|
||||
getQueryParams req = do
|
||||
let code = join $ Prelude.lookup "code" (queryString req)
|
||||
nextURI = do
|
||||
nextBS <- join $ Prelude.lookup "next" (queryString req)
|
||||
parseURI (BSC.unpack nextBS)
|
||||
in (Text.decodeUtf8 <$> code, nextURI)
|
||||
|
||||
-- | Direct the user through an authentication flow with the given server and store the
|
||||
-- credentials in the provided credential manager.
|
||||
authenticateCodeserver :: forall m n i v. (UnliftIO.MonadUnliftIO m, MonadCommand m n i v) => CredentialManager -> CodeserverURI -> m (Either CredentialFailure ())
|
||||
authenticateCodeserver credsManager codeserverURI = UnliftIO.try @_ @CredentialFailure $ do
|
||||
httpClient <- liftIO HTTP.getGlobalManager
|
||||
doc@(DiscoveryDoc {authorizationEndpoint, tokenEndpoint}) <- throwCredFailure $ discoveryForCodeserver httpClient codeserverURI
|
||||
let discoveryURI = discoveryURIForCodeserver codeserverURI
|
||||
doc@(DiscoveryDoc {authorizationEndpoint, tokenEndpoint}) <- throwCredFailure $ fetchDiscoveryDoc discoveryURI
|
||||
debugM Auth "Discovery Doc" doc
|
||||
authResultVar <- UnliftIO.newEmptyMVar @_ @(Either CredentialFailure Tokens)
|
||||
-- The redirect_uri depends on the port, so we need to spin up the server first, but
|
||||
@ -59,7 +66,7 @@ authenticateCodeserver credsManager codeserverURI = UnliftIO.try @_ @CredentialF
|
||||
-- and it all works out fine.
|
||||
redirectURIVar <- UnliftIO.newEmptyMVar
|
||||
(verifier, challenge, state) <- generateParams
|
||||
let codeHandler code = do
|
||||
let codeHandler code mayNextURI = do
|
||||
redirectURI <- UnliftIO.readMVar redirectURIVar
|
||||
result <- exchangeCode httpClient tokenEndpoint code verifier redirectURI
|
||||
UnliftIO.putMVar authResultVar result
|
||||
@ -68,7 +75,10 @@ authenticateCodeserver credsManager codeserverURI = UnliftIO.try @_ @CredentialF
|
||||
debugM Auth "Auth Error" err
|
||||
pure $ Wai.responseLBS internalServerError500 [] "Something went wrong, please try again."
|
||||
Right _ ->
|
||||
pure $ Wai.responseLBS ok200 [] "Authorization successful. You may close this page and return to UCM."
|
||||
case mayNextURI of
|
||||
Nothing -> pure $ Wai.responseLBS found302 [] "Authorization successful. You may close this page and return to UCM."
|
||||
Just nextURI ->
|
||||
pure $ Wai.responseLBS found302 [("LOCATION", BSC.pack $ show @URI nextURI)] "Authorization successful. You may close this page and return to UCM."
|
||||
toIO <- UnliftIO.askRunInIO
|
||||
liftIO . Warp.withApplication (pure $ authTransferServer codeHandler) $ \port -> toIO $ do
|
||||
let redirectURI = "http://localhost:" <> show port <> "/redirect"
|
||||
@ -78,7 +88,8 @@ authenticateCodeserver credsManager codeserverURI = UnliftIO.try @_ @CredentialF
|
||||
respond . Output.InitiateAuthFlow $ authorizationKickoff
|
||||
tokens <- throwCredFailure $ UnliftIO.readMVar authResultVar
|
||||
let codeserverId = codeserverIdFromCodeserverURI codeserverURI
|
||||
saveTokens credsManager codeserverId tokens
|
||||
let creds = codeserverCredentials discoveryURI tokens
|
||||
saveCredentials credsManager codeserverId creds
|
||||
where
|
||||
throwCredFailure :: m (Either CredentialFailure a) -> m a
|
||||
throwCredFailure = throwEitherM
|
||||
|
@ -1,8 +1,16 @@
|
||||
module Unison.Auth.Tokens where
|
||||
|
||||
import Control.Monad.Except
|
||||
import qualified Data.Aeson as Aeson
|
||||
import qualified Data.ByteString.Char8 as BSC
|
||||
import qualified Data.Text as Text
|
||||
import Data.Time.Clock.POSIX (getPOSIXTime)
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import qualified Network.HTTP.Client.TLS as HTTP
|
||||
import qualified Network.HTTP.Types as Network
|
||||
import Network.URI (URI)
|
||||
import Unison.Auth.CredentialManager
|
||||
import Unison.Auth.Discovery (fetchDiscoveryDoc)
|
||||
import Unison.Auth.Types
|
||||
import Unison.CommandLine.InputPattern (patternName)
|
||||
import qualified Unison.CommandLine.InputPatterns as IP
|
||||
@ -29,17 +37,43 @@ type TokenProvider = CodeserverId -> IO (Either CredentialFailure AccessToken)
|
||||
-- | Creates a 'TokenProvider' using the given 'CredentialManager'
|
||||
newTokenProvider :: CredentialManager -> TokenProvider
|
||||
newTokenProvider manager host = UnliftIO.try @_ @CredentialFailure $ do
|
||||
tokens@(Tokens {accessToken}) <- throwEitherM $ getTokens manager host
|
||||
expired <- isExpired accessToken
|
||||
CodeserverCredentials {tokens, discoveryURI} <- throwEitherM $ getCredentials manager host
|
||||
let Tokens {accessToken = currentAccessToken} = tokens
|
||||
expired <- isExpired currentAccessToken
|
||||
if expired
|
||||
then do
|
||||
newTokens@(Tokens {accessToken = newAccessToken}) <- throwEitherM $ refreshTokens manager host tokens
|
||||
saveTokens manager host newTokens
|
||||
newTokens@(Tokens {accessToken = newAccessToken}) <- throwEitherM $ performTokenRefresh discoveryURI tokens
|
||||
saveCredentials manager host (codeserverCredentials discoveryURI newTokens)
|
||||
pure $ newAccessToken
|
||||
else pure accessToken
|
||||
else pure currentAccessToken
|
||||
|
||||
-- | Don't yet support automatically refreshing tokens.
|
||||
refreshTokens :: MonadIO m => CredentialManager -> CodeserverId -> Tokens -> m (Either CredentialFailure Tokens)
|
||||
refreshTokens _manager _host _tokens =
|
||||
-- Refreshing tokens is currently unsupported.
|
||||
pure (Left (RefreshFailure . Text.pack $ "Unable to refresh authentication, please run " <> patternName IP.authLogin <> " and try again."))
|
||||
--
|
||||
-- Specification: https://datatracker.ietf.org/doc/html/rfc6749#section-6
|
||||
performTokenRefresh :: MonadIO m => URI -> Tokens -> m (Either CredentialFailure Tokens)
|
||||
performTokenRefresh discoveryURI (Tokens {refreshToken = currentRefreshToken}) = runExceptT $
|
||||
case currentRefreshToken of
|
||||
Nothing ->
|
||||
throwError $ (RefreshFailure . Text.pack $ "Unable to refresh authentication, please run " <> patternName IP.authLogin <> " and try again.")
|
||||
Just rt -> do
|
||||
DiscoveryDoc {tokenEndpoint} <- ExceptT $ fetchDiscoveryDoc discoveryURI
|
||||
req <- liftIO $ HTTP.requestFromURI tokenEndpoint
|
||||
let addFormData =
|
||||
HTTP.urlEncodedBody
|
||||
[ ("grant_type", "refresh_token"),
|
||||
("refresh_token", BSC.pack . Text.unpack $ rt)
|
||||
]
|
||||
let fullReq = addFormData $ req {HTTP.method = "POST", HTTP.requestHeaders = [("Accept", "application/json")]}
|
||||
unauthenticatedHttpClient <- liftIO $ HTTP.getGlobalManager
|
||||
resp <- liftIO $ HTTP.httpLbs fullReq unauthenticatedHttpClient
|
||||
newTokens <- case HTTP.responseStatus resp of
|
||||
status
|
||||
| status < Network.status300 -> do
|
||||
let respBytes = HTTP.responseBody resp
|
||||
case Aeson.eitherDecode @Tokens respBytes of
|
||||
Left err -> throwError (InvalidTokenResponse tokenEndpoint (Text.pack err))
|
||||
Right a -> pure a
|
||||
| otherwise -> throwError $ (InvalidTokenResponse tokenEndpoint $ "Received " <> tShow status <> " response from token endpoint")
|
||||
-- According to the spec, servers may or may not update the refresh token itself.
|
||||
-- If updated we need to replace it, if not updated we keep the existing one.
|
||||
pure $ newTokens {refreshToken = refreshToken newTokens <|> currentRefreshToken}
|
||||
|
@ -14,8 +14,10 @@ module Unison.Auth.Types
|
||||
PKCEChallenge,
|
||||
ProfileName,
|
||||
CredentialFailure (..),
|
||||
getActiveTokens,
|
||||
setActiveTokens,
|
||||
CodeserverCredentials (..),
|
||||
getCodeserverCredentials,
|
||||
setCodeserverCredentials,
|
||||
codeserverCredentials,
|
||||
emptyCredentials,
|
||||
)
|
||||
where
|
||||
@ -29,7 +31,7 @@ import Data.Time (NominalDiffTime)
|
||||
import Network.URI
|
||||
import qualified Network.URI as URI
|
||||
import Unison.Prelude
|
||||
import Unison.Share.Types (CodeserverId, CodeserverURI)
|
||||
import Unison.Share.Types
|
||||
|
||||
defaultProfileName :: ProfileName
|
||||
defaultProfileName = "default"
|
||||
@ -128,26 +130,11 @@ instance Aeson.FromJSON DiscoveryDoc where
|
||||
type ProfileName = Text
|
||||
|
||||
data Credentials = Credentials
|
||||
{ credentials :: Map ProfileName (Map CodeserverId Tokens),
|
||||
{ credentials :: Map ProfileName (Map CodeserverId CodeserverCredentials),
|
||||
activeProfile :: ProfileName
|
||||
}
|
||||
deriving (Eq)
|
||||
|
||||
emptyCredentials :: Credentials
|
||||
emptyCredentials = Credentials mempty defaultProfileName
|
||||
|
||||
getActiveTokens :: CodeserverId -> Credentials -> Either CredentialFailure Tokens
|
||||
getActiveTokens host (Credentials {credentials, activeProfile}) =
|
||||
maybeToEither (ReauthRequired host) $
|
||||
credentials ^? ix activeProfile . ix host
|
||||
|
||||
setActiveTokens :: CodeserverId -> Tokens -> Credentials -> Credentials
|
||||
setActiveTokens host tokens creds@(Credentials {credentials, activeProfile}) =
|
||||
let newCredMap =
|
||||
credentials
|
||||
& at activeProfile . non Map.empty . at host .~ Just tokens
|
||||
in creds {credentials = newCredMap}
|
||||
|
||||
instance Aeson.ToJSON Credentials where
|
||||
toJSON (Credentials credMap activeProfile) =
|
||||
Aeson.object
|
||||
@ -160,3 +147,45 @@ instance Aeson.FromJSON Credentials where
|
||||
credentials <- obj .: "credentials"
|
||||
activeProfile <- obj .: "active_profile"
|
||||
pure Credentials {..}
|
||||
|
||||
-- | Credentials for a specific codeserver
|
||||
data CodeserverCredentials = CodeserverCredentials
|
||||
{ -- The most recent set of authentication tokens
|
||||
tokens :: Tokens,
|
||||
-- URI where the discovery document for this codeserver can be fetched.
|
||||
discoveryURI :: URI
|
||||
}
|
||||
deriving (Eq)
|
||||
|
||||
instance ToJSON CodeserverCredentials where
|
||||
toJSON (CodeserverCredentials tokens discoveryURI) =
|
||||
Aeson.object ["tokens" .= tokens, "discovery_uri" .= show discoveryURI]
|
||||
|
||||
instance FromJSON CodeserverCredentials where
|
||||
parseJSON =
|
||||
Aeson.withObject "CodeserverCredentials" $ \v ->
|
||||
do
|
||||
tokens <- v .: "tokens"
|
||||
discoveryURIString <- v .: "discovery_uri"
|
||||
discoveryURI <- case parseURI discoveryURIString of
|
||||
Nothing -> fail "discovery_uri is not a valid URI"
|
||||
Just uri -> pure uri
|
||||
pure $ CodeserverCredentials {..}
|
||||
|
||||
emptyCredentials :: Credentials
|
||||
emptyCredentials = Credentials mempty defaultProfileName
|
||||
|
||||
codeserverCredentials :: URI -> Tokens -> CodeserverCredentials
|
||||
codeserverCredentials discoveryURI tokens = CodeserverCredentials {discoveryURI, tokens}
|
||||
|
||||
getCodeserverCredentials :: CodeserverId -> Credentials -> Either CredentialFailure CodeserverCredentials
|
||||
getCodeserverCredentials host (Credentials {credentials, activeProfile}) =
|
||||
maybeToEither (ReauthRequired host) $
|
||||
credentials ^? ix activeProfile . ix host
|
||||
|
||||
setCodeserverCredentials :: CodeserverId -> CodeserverCredentials -> Credentials -> Credentials
|
||||
setCodeserverCredentials host codeserverCreds creds@(Credentials {credentials, activeProfile}) =
|
||||
let newCredMap =
|
||||
credentials
|
||||
& at activeProfile . non Map.empty . at host .~ Just codeserverCreds
|
||||
in creds {credentials = newCredMap}
|
||||
|
@ -13,7 +13,6 @@ module Unison.Codebase.Editor.Command
|
||||
EvalResult,
|
||||
commandName,
|
||||
lookupEvalResult,
|
||||
UCMVersion,
|
||||
)
|
||||
where
|
||||
|
||||
@ -30,6 +29,7 @@ import Unison.Codebase.Editor.AuthorInfo (AuthorInfo)
|
||||
import qualified Unison.Codebase.Editor.Git as Git
|
||||
import Unison.Codebase.Editor.Output
|
||||
import Unison.Codebase.Editor.RemoteRepo
|
||||
import Unison.Codebase.Editor.UCMVersion (UCMVersion)
|
||||
import Unison.Codebase.IntegrityCheck (IntegrityResult)
|
||||
import Unison.Codebase.Path (Path)
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
@ -95,8 +95,6 @@ type TypecheckingResult v =
|
||||
(Seq (Note v Ann))
|
||||
(Either Names (UF.TypecheckedUnisonFile v Ann))
|
||||
|
||||
type UCMVersion = Text
|
||||
|
||||
data
|
||||
Command
|
||||
m -- Command monad
|
||||
@ -198,16 +196,16 @@ data
|
||||
LoadLocalBranch :: Branch.CausalHash -> Command m i v (Branch m)
|
||||
-- Merge two branches, using the codebase for the LCA calculation where possible.
|
||||
Merge :: Branch.MergeMode -> Branch m -> Branch m -> Command m i v (Branch m)
|
||||
ViewRemoteBranch ::
|
||||
ReadRemoteNamespace ->
|
||||
ViewRemoteGitBranch ::
|
||||
ReadGitRemoteNamespace ->
|
||||
Git.GitBranchBehavior ->
|
||||
(Branch m -> (Free (Command m i v) r)) ->
|
||||
Command m i v (Either GitError r)
|
||||
-- we want to import as little as possible, so we pass the SBH/path as part
|
||||
-- of the `RemoteNamespace`. The Branch that's returned should be fully
|
||||
-- imported and not retain any resources from the remote codebase
|
||||
ImportRemoteBranch ::
|
||||
ReadRemoteNamespace ->
|
||||
ImportRemoteGitBranch ::
|
||||
ReadGitRemoteNamespace ->
|
||||
SyncMode ->
|
||||
-- | A preprocessing step to perform on the branch before it's imported.
|
||||
-- This is sometimes useful for minimizing the number of definitions to sync.
|
||||
@ -218,7 +216,7 @@ data
|
||||
-- Any definitions in the head of the supplied branch that aren't in the target
|
||||
-- codebase are copied there.
|
||||
SyncLocalRootBranch :: Branch m -> Command m i v ()
|
||||
SyncRemoteBranch :: WriteRepo -> PushGitBranchOpts -> (Branch m -> m (Either e (Branch m))) -> Command m i v (Either GitError (Either e (Branch m)))
|
||||
SyncRemoteGitBranch :: WriteGitRepo -> PushGitBranchOpts -> (Branch m -> m (Either e (Branch m))) -> Command m i v (Either GitError (Either e (Branch m)))
|
||||
AppendToReflog :: Text -> Branch m -> Branch m -> Command m i v ()
|
||||
-- load the reflog in file (chronological) order
|
||||
LoadReflog :: Command m i v [Reflog.Entry Branch.CausalHash]
|
||||
@ -318,10 +316,10 @@ commandName = \case
|
||||
LoadLocalRootBranch -> "LoadLocalRootBranch"
|
||||
LoadLocalBranch {} -> "LoadLocalBranch"
|
||||
Merge {} -> "Merge"
|
||||
ViewRemoteBranch {} -> "ViewRemoteBranch"
|
||||
ImportRemoteBranch {} -> "ImportRemoteBranch"
|
||||
ViewRemoteGitBranch {} -> "ViewRemoteGitBranch"
|
||||
ImportRemoteGitBranch {} -> "ImportRemoteGitBranch"
|
||||
SyncLocalRootBranch {} -> "SyncLocalRootBranch"
|
||||
SyncRemoteBranch {} -> "SyncRemoteBranch"
|
||||
SyncRemoteGitBranch {} -> "SyncRemoteGitBranch"
|
||||
AppendToReflog {} -> "AppendToReflog"
|
||||
LoadReflog -> "LoadReflog"
|
||||
LoadTerm {} -> "LoadTerm"
|
||||
|
@ -22,8 +22,9 @@ import Unison.Codebase.Branch (Branch)
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import qualified Unison.Codebase.Branch.Merge as Branch
|
||||
import qualified Unison.Codebase.Editor.AuthorInfo as AuthorInfo
|
||||
import Unison.Codebase.Editor.Command (Command (..), LexedSource, LoadSourceResult, SourceName, TypecheckingResult, UCMVersion, UseCache)
|
||||
import Unison.Codebase.Editor.Command (Command (..), LexedSource, LoadSourceResult, SourceName, TypecheckingResult, UseCache)
|
||||
import Unison.Codebase.Editor.Output (NumberedArgs, NumberedOutput, Output (PrintMessage))
|
||||
import Unison.Codebase.Editor.UCMVersion (UCMVersion)
|
||||
import Unison.Codebase.IntegrityCheck (integrityCheckFullCodebase)
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
import Unison.Codebase.Runtime (Runtime)
|
||||
@ -153,14 +154,14 @@ commandLine config awaitInput setBranchRef rt notifyUser notifyNumbered loadSour
|
||||
SyncLocalRootBranch branch -> lift $ do
|
||||
setBranchRef branch
|
||||
Codebase.putRootBranch codebase branch
|
||||
ViewRemoteBranch ns gitBranchBehavior action -> do
|
||||
ViewRemoteGitBranch ns gitBranchBehavior action -> do
|
||||
-- TODO: We probably won'd need to unlift anything once we remove the Command
|
||||
-- abstraction.
|
||||
toIO <- UnliftIO.askRunInIO
|
||||
lift $ Codebase.viewRemoteBranch codebase ns gitBranchBehavior (toIO . Free.fold go . action)
|
||||
ImportRemoteBranch ns syncMode preprocess ->
|
||||
ImportRemoteGitBranch ns syncMode preprocess ->
|
||||
lift $ Codebase.importRemoteBranch codebase ns syncMode preprocess
|
||||
SyncRemoteBranch repo opts action ->
|
||||
SyncRemoteGitBranch repo opts action ->
|
||||
lift $ Codebase.pushGitBranch codebase repo opts action
|
||||
LoadTerm r -> lift $ Codebase.getTerm codebase r
|
||||
LoadTypeOfTerm r -> lift $ Codebase.getTypeOfTerm codebase r
|
||||
|
@ -8,9 +8,11 @@ where
|
||||
|
||||
-- TODO: Don't import backend
|
||||
|
||||
import Control.Concurrent.STM (atomically, newTVarIO, readTVar, readTVarIO, writeTVar)
|
||||
import qualified Control.Error.Util as ErrorUtil
|
||||
import Control.Lens
|
||||
import Control.Monad.Except (ExceptT (..), runExceptT, throwError, withExceptT)
|
||||
import Control.Monad.Reader (ask)
|
||||
import Control.Monad.State (StateT)
|
||||
import qualified Control.Monad.State as State
|
||||
import Data.Bifunctor (first, second)
|
||||
@ -26,13 +28,17 @@ import Data.Set.NonEmpty (NESet)
|
||||
import qualified Data.Set.NonEmpty as NESet
|
||||
import qualified Data.Text as Text
|
||||
import Data.Tuple.Extra (uncurry3)
|
||||
import qualified System.Console.Regions as Console.Regions
|
||||
import qualified Text.Megaparsec as P
|
||||
import U.Util.Timing (unsafeTime)
|
||||
import U.Codebase.HashTags (CausalHash)
|
||||
import qualified U.Codebase.Sqlite.Operations as Ops
|
||||
import U.Util.Timing (time, unsafeTime)
|
||||
import qualified Unison.ABT as ABT
|
||||
import qualified Unison.Builtin as Builtin
|
||||
import qualified Unison.Builtin.Decls as DD
|
||||
import qualified Unison.Builtin.Terms as Builtin
|
||||
import Unison.Codebase (Preprocessing (..), PushGitBranchOpts (..))
|
||||
import qualified Unison.Codebase as Codebase
|
||||
import Unison.Codebase.Branch (Branch (..), Branch0 (..))
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import qualified Unison.Codebase.Branch.Merge as Branch
|
||||
@ -44,7 +50,7 @@ import Unison.Codebase.Editor.AuthorInfo (AuthorInfo (..))
|
||||
import Unison.Codebase.Editor.Command as Command
|
||||
import Unison.Codebase.Editor.DisplayObject
|
||||
import qualified Unison.Codebase.Editor.Git as Git
|
||||
import Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin)
|
||||
import Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin, ensureAuthenticatedWithCodeserver)
|
||||
import Unison.Codebase.Editor.HandleInput.LoopState (Action, Action', MonadCommand (..), eval, liftF, respond, respondNumbered)
|
||||
import qualified Unison.Codebase.Editor.HandleInput.LoopState as LoopState
|
||||
import qualified Unison.Codebase.Editor.HandleInput.NamespaceDependencies as NamespaceDependencies
|
||||
@ -55,7 +61,19 @@ import qualified Unison.Codebase.Editor.Output as Output
|
||||
import qualified Unison.Codebase.Editor.Output.BranchDiff as OBranchDiff
|
||||
import qualified Unison.Codebase.Editor.Output.DumpNamespace as Output.DN
|
||||
import qualified Unison.Codebase.Editor.Propagate as Propagate
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, WriteRemotePath, WriteRepo, printNamespace, writePathToRead)
|
||||
import Unison.Codebase.Editor.RemoteRepo
|
||||
( ReadGitRemoteNamespace (..),
|
||||
ReadRemoteNamespace (..),
|
||||
ReadShareRemoteNamespace (..),
|
||||
WriteGitRemotePath (..),
|
||||
WriteGitRepo,
|
||||
WriteRemotePath (..),
|
||||
WriteShareRemotePath (..),
|
||||
printNamespace,
|
||||
writePathToRead,
|
||||
writeToReadGit,
|
||||
)
|
||||
import qualified Unison.Codebase.Editor.RemoteRepo as RemoteRepo
|
||||
import qualified Unison.Codebase.Editor.Slurp as Slurp
|
||||
import Unison.Codebase.Editor.SlurpComponent (SlurpComponent (..))
|
||||
import qualified Unison.Codebase.Editor.SlurpComponent as SC
|
||||
@ -76,11 +94,12 @@ import qualified Unison.Codebase.PushBehavior as PushBehavior
|
||||
import qualified Unison.Codebase.Reflog as Reflog
|
||||
import Unison.Codebase.ShortBranchHash (ShortBranchHash)
|
||||
import qualified Unison.Codebase.ShortBranchHash as SBH
|
||||
import qualified Unison.Codebase.SqliteCodebase.Conversions as Cv
|
||||
import qualified Unison.Codebase.SyncMode as SyncMode
|
||||
import Unison.Codebase.TermEdit (TermEdit (..))
|
||||
import qualified Unison.Codebase.TermEdit as TermEdit
|
||||
import qualified Unison.Codebase.TermEdit.Typing as TermEdit
|
||||
import Unison.Codebase.Type (GitError)
|
||||
import Unison.Codebase.Type (Codebase (..), GitError)
|
||||
import qualified Unison.Codebase.TypeEdit as TypeEdit
|
||||
import qualified Unison.Codebase.Verbosity as Verbosity
|
||||
import qualified Unison.CommandLine.DisplayValues as DisplayValues
|
||||
@ -125,8 +144,13 @@ import Unison.Server.QueryResult
|
||||
import Unison.Server.SearchResult (SearchResult)
|
||||
import qualified Unison.Server.SearchResult as SR
|
||||
import qualified Unison.Server.SearchResult' as SR'
|
||||
import qualified Unison.Share.Codeserver as Codeserver
|
||||
import qualified Unison.Share.Sync as Share
|
||||
import Unison.Share.Types (codeserverBaseURL)
|
||||
import qualified Unison.ShortHash as SH
|
||||
import qualified Unison.Sqlite as Sqlite
|
||||
import Unison.Symbol (Symbol)
|
||||
import qualified Unison.Sync.Types as Share (Path (..))
|
||||
import Unison.Term (Term)
|
||||
import qualified Unison.Term as Term
|
||||
import Unison.Type (Type)
|
||||
@ -150,6 +174,7 @@ import Unison.Util.TransitiveClosure (transitiveClosure)
|
||||
import Unison.Var (Var)
|
||||
import qualified Unison.Var as Var
|
||||
import qualified Unison.WatchKind as WK
|
||||
import qualified Unison.Share.Sync.Types as Sync
|
||||
|
||||
defaultPatchNameSegment :: NameSegment
|
||||
defaultPatchNameSegment = "patch"
|
||||
@ -409,7 +434,7 @@ loop = do
|
||||
-- todo: show the actual config-loaded namespace
|
||||
<> maybe
|
||||
"(remote namespace from .unisonConfig)"
|
||||
(uncurry3 printNamespace)
|
||||
printNamespace
|
||||
orepo
|
||||
<> " "
|
||||
<> p' dest
|
||||
@ -421,9 +446,9 @@ loop = do
|
||||
CreatePullRequestI {} -> wat
|
||||
LoadPullRequestI base head dest ->
|
||||
"pr.load "
|
||||
<> uncurry3 printNamespace base
|
||||
<> printNamespace base
|
||||
<> " "
|
||||
<> uncurry3 printNamespace head
|
||||
<> printNamespace head
|
||||
<> " "
|
||||
<> p' dest
|
||||
PushRemoteBranchI {} -> wat
|
||||
@ -486,9 +511,8 @@ loop = do
|
||||
(Branch m -> Action m i v1 (Branch m)) ->
|
||||
Action m i v1 Bool
|
||||
updateAtM = Unison.Codebase.Editor.HandleInput.updateAtM inputDescription
|
||||
unlessGitError = unlessError' Output.GitError
|
||||
importRemoteBranch ns mode preprocess =
|
||||
ExceptT . eval $ ImportRemoteBranch ns mode preprocess
|
||||
importRemoteGitBranch ns mode preprocess =
|
||||
ExceptT . eval $ ImportRemoteGitBranch ns mode preprocess
|
||||
loadSearchResults = eval . LoadSearchResults
|
||||
saveAndApplyPatch patchPath'' patchName patch' = do
|
||||
stepAtM
|
||||
@ -644,24 +668,20 @@ loop = do
|
||||
(resolveToAbsolute <$> after)
|
||||
ppe
|
||||
outputDiff
|
||||
CreatePullRequestI baseRepo headRepo -> do
|
||||
result <-
|
||||
join @(Either GitError) <$> viewRemoteBranch baseRepo Git.RequireExistingBranch \baseBranch -> do
|
||||
viewRemoteBranch headRepo Git.RequireExistingBranch \headBranch -> do
|
||||
merged <- eval $ Merge Branch.RegularMerge baseBranch headBranch
|
||||
(ppe, diff) <- diffHelperCmd root' currentPath' (Branch.head baseBranch) (Branch.head merged)
|
||||
pure $ ShowDiffAfterCreatePR baseRepo headRepo ppe diff
|
||||
case result of
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right diff -> respondNumbered diff
|
||||
CreatePullRequestI baseRepo headRepo -> handleCreatePullRequest baseRepo headRepo
|
||||
LoadPullRequestI baseRepo headRepo dest0 -> do
|
||||
let desta = resolveToAbsolute dest0
|
||||
let dest = Path.unabsolute desta
|
||||
destb <- getAt desta
|
||||
let tryImportBranch = \case
|
||||
ReadRemoteNamespaceGit repo ->
|
||||
withExceptT Output.GitError (importRemoteGitBranch repo SyncMode.ShortCircuit Unmodified)
|
||||
ReadRemoteNamespaceShare repo ->
|
||||
ExceptT (importRemoteShareBranch repo)
|
||||
if Branch.isEmpty0 (Branch.head destb)
|
||||
then unlessGitError do
|
||||
baseb <- importRemoteBranch baseRepo SyncMode.ShortCircuit Unmodified
|
||||
headb <- importRemoteBranch headRepo SyncMode.ShortCircuit Unmodified
|
||||
then unlessError do
|
||||
baseb <- tryImportBranch baseRepo
|
||||
headb <- tryImportBranch headRepo
|
||||
lift $ do
|
||||
mergedb <- eval $ Merge Branch.RegularMerge baseb headb
|
||||
squashedb <- eval $ Merge Branch.SquashMerge headb baseb
|
||||
@ -701,8 +721,8 @@ loop = do
|
||||
case getAtSplit' dest of
|
||||
Just existingDest
|
||||
| not (Branch.isEmpty0 (Branch.head existingDest)) -> do
|
||||
-- Branch exists and isn't empty, print an error
|
||||
throwError (BranchAlreadyExists (Path.unsplit' dest))
|
||||
-- Branch exists and isn't empty, print an error
|
||||
throwError (BranchAlreadyExists (Path.unsplit' dest))
|
||||
_ -> pure ()
|
||||
-- allow rewriting history to ensure we move the branch's history too.
|
||||
lift $
|
||||
@ -773,7 +793,7 @@ loop = do
|
||||
doDelete = do
|
||||
stepAt Branch.CompressHistory $ BranchUtil.makeDeleteBranch (resolveSplit' p)
|
||||
respond Success
|
||||
-- Looks similar to the 'toDelete' above... investigate me! ;)
|
||||
-- Looks similar to the 'toDelete' above... investigate me! ;)
|
||||
computeEndangerments :: Branch0 m1 -> Action' m v (Map LabeledDependency (NESet LabeledDependency))
|
||||
computeEndangerments b0 = do
|
||||
let rootNames = Branch.toNames root0
|
||||
@ -1390,11 +1410,11 @@ loop = do
|
||||
case filtered of
|
||||
[(Referent.Ref ref, ty)]
|
||||
| Typechecker.isSubtype ty mainType ->
|
||||
eval (MakeStandalone ppe ref output) >>= \case
|
||||
Just err -> respond $ EvaluationFailure err
|
||||
Nothing -> pure ()
|
||||
eval (MakeStandalone ppe ref output) >>= \case
|
||||
Just err -> respond $ EvaluationFailure err
|
||||
Nothing -> pure ()
|
||||
| otherwise ->
|
||||
respond $ BadMainFunction smain ty ppe [mainType]
|
||||
respond $ BadMainFunction smain ty ppe [mainType]
|
||||
_ -> respond $ NoMainFunction smain ppe [mainType]
|
||||
IOTestI main -> do
|
||||
-- todo - allow this to run tests from scratch file, using addRunMain
|
||||
@ -1490,9 +1510,11 @@ loop = do
|
||||
let preprocess = case pullMode of
|
||||
Input.PullWithHistory -> Unmodified
|
||||
Input.PullWithoutHistory -> Preprocessed $ pure . Branch.discardHistory
|
||||
ns <- maybe (writePathToRead <$> resolveConfiguredGitUrl Pull path) pure mayRepo
|
||||
lift $ unlessGitError do
|
||||
remoteBranch <- importRemoteBranch ns syncMode preprocess
|
||||
ns <- maybe (writePathToRead <$> resolveConfiguredUrl Pull path) pure mayRepo
|
||||
lift $ unlessError do
|
||||
remoteBranch <- case ns of
|
||||
ReadRemoteNamespaceGit repo -> withExceptT Output.GitError (importRemoteGitBranch repo syncMode preprocess)
|
||||
ReadRemoteNamespaceShare repo -> ExceptT (importRemoteShareBranch repo)
|
||||
let unchangedMsg = PullAlreadyUpToDate ns path
|
||||
let destAbs = resolveToAbsolute path
|
||||
let printDiffPath = if Verbosity.isSilent verbosity then Nothing else Just path
|
||||
@ -1632,7 +1654,7 @@ loop = do
|
||||
UpdateBuiltinsI -> notImplemented
|
||||
QuitI -> empty
|
||||
GistI input -> handleGist input
|
||||
AuthLoginI -> authLogin
|
||||
AuthLoginI -> authLogin (Codeserver.resolveCodeserver RemoteRepo.DefaultCodeserver)
|
||||
VersionI -> do
|
||||
ucmVersion <- eval UCMVersion
|
||||
respond $ PrintVersion ucmVersion
|
||||
@ -1644,6 +1666,65 @@ loop = do
|
||||
Right input -> LoopState.lastInput .= Just input
|
||||
_ -> pure ()
|
||||
|
||||
handleCreatePullRequest :: forall m v. MonadUnliftIO m => ReadRemoteNamespace -> ReadRemoteNamespace -> Action' m v ()
|
||||
handleCreatePullRequest baseRepo0 headRepo0 = do
|
||||
root' <- use LoopState.root
|
||||
currentPath' <- use LoopState.currentPath
|
||||
|
||||
-- One of these needs a callback and the other doesn't. you might think you can get around that problem with
|
||||
-- a helper function to unify the two cases, but we tried that and they were in such different monads that it
|
||||
-- was hard to do.
|
||||
-- viewRemoteBranch' :: forall r. ReadGitRemoteNamespace -> Git.GitBranchBehavior -> ((Branch m, CodebasePath) -> m r) -> m (Either GitError r),
|
||||
-- because there's no MonadUnliftIO instance on Action.
|
||||
-- We need `Command` to go away (the FreeT layer goes away),
|
||||
-- We have the StateT layer goes away (can put it into an IORef in the environment),
|
||||
-- We have the MaybeT layer that signals end of input (can just been an IORef bool that we check before looping),
|
||||
-- and once all those things become IO, we can add a MonadUnliftIO instance on Action, and unify these cases.
|
||||
let mergeAndDiff :: MonadCommand n m i v => Branch m -> Branch m -> n (NumberedOutput v)
|
||||
mergeAndDiff baseBranch headBranch = do
|
||||
merged <- eval $ Merge Branch.RegularMerge baseBranch headBranch
|
||||
(ppe, diff) <- diffHelperCmd root' currentPath' (Branch.head baseBranch) (Branch.head merged)
|
||||
pure $ ShowDiffAfterCreatePR baseRepo0 headRepo0 ppe diff
|
||||
|
||||
case (baseRepo0, headRepo0) of
|
||||
(ReadRemoteNamespaceGit baseRepo, ReadRemoteNamespaceGit headRepo) -> do
|
||||
result <-
|
||||
viewRemoteGitBranch baseRepo Git.RequireExistingBranch \baseBranch ->
|
||||
viewRemoteGitBranch headRepo Git.RequireExistingBranch \headBranch ->
|
||||
mergeAndDiff baseBranch headBranch
|
||||
case join result of
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right diff -> respondNumbered diff
|
||||
(ReadRemoteNamespaceGit baseRepo, ReadRemoteNamespaceShare headRepo) ->
|
||||
importRemoteShareBranch headRepo >>= \case
|
||||
Left err -> respond err
|
||||
Right headBranch -> do
|
||||
result <-
|
||||
viewRemoteGitBranch baseRepo Git.RequireExistingBranch \baseBranch ->
|
||||
mergeAndDiff baseBranch headBranch
|
||||
case result of
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right diff -> respondNumbered diff
|
||||
(ReadRemoteNamespaceShare baseRepo, ReadRemoteNamespaceGit headRepo) ->
|
||||
importRemoteShareBranch baseRepo >>= \case
|
||||
Left err -> respond err
|
||||
Right baseBranch -> do
|
||||
result <-
|
||||
viewRemoteGitBranch headRepo Git.RequireExistingBranch \headBranch ->
|
||||
mergeAndDiff baseBranch headBranch
|
||||
case result of
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right diff -> respondNumbered diff
|
||||
(ReadRemoteNamespaceShare baseRepo, ReadRemoteNamespaceShare headRepo) ->
|
||||
importRemoteShareBranch headRepo >>= \case
|
||||
Left err -> respond err
|
||||
Right headBranch ->
|
||||
importRemoteShareBranch baseRepo >>= \case
|
||||
Left err -> respond err
|
||||
Right baseBranch -> do
|
||||
diff <- mergeAndDiff baseBranch headBranch
|
||||
respondNumbered diff
|
||||
|
||||
handleDependents :: Monad m => HQ.HashQualified Name -> Action' m v ()
|
||||
handleDependents hq = do
|
||||
hqLength <- eval CodebaseHashLength
|
||||
@ -1683,7 +1764,7 @@ handleDependents hq = do
|
||||
-- | Handle a @gist@ command.
|
||||
handleGist :: MonadUnliftIO m => GistInput -> Action' m v ()
|
||||
handleGist (GistInput repo) =
|
||||
doPushRemoteBranch repo Path.relativeEmpty' SyncMode.ShortCircuit Nothing
|
||||
doPushRemoteBranch (GistyPush repo) Path.relativeEmpty' SyncMode.ShortCircuit
|
||||
|
||||
-- | Handle a @push@ command.
|
||||
handlePushRemoteBranch ::
|
||||
@ -1697,51 +1778,75 @@ handlePushRemoteBranch ::
|
||||
PushBehavior ->
|
||||
SyncMode.SyncMode ->
|
||||
Action' m v ()
|
||||
handlePushRemoteBranch mayRepo path pushBehavior syncMode = do
|
||||
unlessError do
|
||||
(repo, remotePath) <- maybe (resolveConfiguredGitUrl Push path) pure mayRepo
|
||||
lift (doPushRemoteBranch repo path syncMode (Just (remotePath, pushBehavior)))
|
||||
handlePushRemoteBranch mayRepo path pushBehavior syncMode =
|
||||
time
|
||||
"handlePushRemoteBranch"
|
||||
case mayRepo of
|
||||
Nothing ->
|
||||
runExceptT (resolveConfiguredUrl Push path) >>= \case
|
||||
Left output -> respond output
|
||||
Right repo -> push repo
|
||||
Just repo -> push repo
|
||||
where
|
||||
push repo =
|
||||
doPushRemoteBranch (NormalPush repo pushBehavior) path syncMode
|
||||
|
||||
-- | Either perform a "normal" push (updating a remote path), which takes a 'PushBehavior' (to control whether creating
|
||||
-- a new namespace is allowed), or perform a "gisty" push, which doesn't update any paths (and also is currently only
|
||||
-- uploaded for remote git repos, not remote Share repos).
|
||||
data PushFlavor
|
||||
= NormalPush WriteRemotePath PushBehavior
|
||||
| GistyPush WriteGitRepo
|
||||
|
||||
-- Internal helper that implements pushing to a remote repo, which generalizes @gist@ and @push@.
|
||||
doPushRemoteBranch ::
|
||||
forall m v.
|
||||
MonadUnliftIO m =>
|
||||
-- | The repo to push to.
|
||||
WriteRepo ->
|
||||
PushFlavor ->
|
||||
-- | The local path to push. If relative, it's resolved relative to the current path (`cd`).
|
||||
Path' ->
|
||||
SyncMode.SyncMode ->
|
||||
-- | The remote target. If missing, the given branch contents should be pushed to the remote repo without updating the
|
||||
-- root namespace.
|
||||
Maybe (Path, PushBehavior) ->
|
||||
Action' m v ()
|
||||
doPushRemoteBranch repo localPath syncMode remoteTarget = do
|
||||
sourceBranch <- do
|
||||
currentPath' <- use LoopState.currentPath
|
||||
getAt (Path.resolve currentPath' localPath)
|
||||
doPushRemoteBranch pushFlavor localPath0 syncMode = do
|
||||
currentPath' <- use LoopState.currentPath
|
||||
let localPath = Path.resolve currentPath' localPath0
|
||||
|
||||
unlessError do
|
||||
withExceptT Output.GitError $ do
|
||||
case remoteTarget of
|
||||
Nothing -> do
|
||||
let opts = PushGitBranchOpts {setRoot = False, syncMode}
|
||||
syncRemoteBranch repo opts (\_remoteRoot -> pure (Right sourceBranch))
|
||||
sbhLength <- (eval BranchHashLength)
|
||||
respond (GistCreated sbhLength repo (Branch.headHash sourceBranch))
|
||||
Just (remotePath, pushBehavior) -> do
|
||||
let withRemoteRoot :: Branch m -> m (Either (Output v) (Branch m))
|
||||
withRemoteRoot remoteRoot = do
|
||||
let -- We don't merge `sourceBranch` with `remoteBranch`, we just replace it. This push will be rejected if this
|
||||
-- rewinds time or misses any new updates in the remote branch that aren't in `sourceBranch` already.
|
||||
f remoteBranch = if shouldPushTo pushBehavior remoteBranch then Just sourceBranch else Nothing
|
||||
Branch.modifyAtM remotePath f remoteRoot & \case
|
||||
Nothing -> pure (Left $ RefusedToPush pushBehavior)
|
||||
Just newRemoteRoot -> pure (Right newRemoteRoot)
|
||||
let opts = PushGitBranchOpts {setRoot = True, syncMode}
|
||||
runExceptT (syncRemoteBranch repo opts withRemoteRoot) >>= \case
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right (Left output) -> respond output
|
||||
Right (Right _branch) -> respond Success
|
||||
case pushFlavor of
|
||||
NormalPush (writeRemotePath@(WriteRemotePathGit WriteGitRemotePath {repo, path = remotePath})) pushBehavior -> do
|
||||
sourceBranch <- getAt localPath
|
||||
let withRemoteRoot :: Branch m -> m (Either (Output v) (Branch m))
|
||||
withRemoteRoot remoteRoot = do
|
||||
let -- We don't merge `sourceBranch` with `remoteBranch`, we just replace it. This push will be rejected if this
|
||||
-- rewinds time or misses any new updates in the remote branch that aren't in `sourceBranch` already.
|
||||
f remoteBranch = if shouldPushTo pushBehavior remoteBranch then Just sourceBranch else Nothing
|
||||
Branch.modifyAtM remotePath f remoteRoot & \case
|
||||
Nothing -> pure (Left $ RefusedToPush pushBehavior writeRemotePath)
|
||||
Just newRemoteRoot -> pure (Right newRemoteRoot)
|
||||
let opts = PushGitBranchOpts {setRoot = True, syncMode}
|
||||
runExceptT (syncGitRemoteBranch repo opts withRemoteRoot) >>= \case
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right (Left errOutput) -> respond errOutput
|
||||
Right (Right _branch) -> respond Success
|
||||
NormalPush (WriteRemotePathShare sharePath) pushBehavior ->
|
||||
handlePushToUnisonShare sharePath localPath pushBehavior
|
||||
GistyPush repo -> do
|
||||
sourceBranch <- getAt localPath
|
||||
let opts = PushGitBranchOpts {setRoot = False, syncMode}
|
||||
runExceptT (syncGitRemoteBranch repo opts (\_remoteRoot -> pure (Right sourceBranch))) >>= \case
|
||||
Left gitErr -> respond (Output.GitError gitErr)
|
||||
Right (Left errOutput) -> respond errOutput
|
||||
Right _result -> do
|
||||
sbhLength <- eval BranchHashLength
|
||||
respond $
|
||||
GistCreated
|
||||
( ReadRemoteNamespaceGit
|
||||
ReadGitRemoteNamespace
|
||||
{ repo = writeToReadGit repo,
|
||||
sbh = Just (SBH.fromHash sbhLength (Branch.headHash sourceBranch)),
|
||||
path = Path.empty
|
||||
}
|
||||
)
|
||||
where
|
||||
-- Per `pushBehavior`, we are either:
|
||||
--
|
||||
@ -1753,6 +1858,84 @@ doPushRemoteBranch repo localPath syncMode remoteTarget = do
|
||||
PushBehavior.RequireEmpty -> Branch.isEmpty0 (Branch.head remoteBranch)
|
||||
PushBehavior.RequireNonEmpty -> not (Branch.isEmpty0 (Branch.head remoteBranch))
|
||||
|
||||
handlePushToUnisonShare :: (MonadUnliftIO m) => WriteShareRemotePath -> Path.Absolute -> PushBehavior -> Action' m v ()
|
||||
handlePushToUnisonShare WriteShareRemotePath {server, repo, path = remotePath} localPath behavior = do
|
||||
let codeserver = Codeserver.resolveCodeserver server
|
||||
let baseURL = codeserverBaseURL codeserver
|
||||
let sharePath = Share.Path (repo Nel.:| pathToSegments remotePath)
|
||||
ensureAuthenticatedWithCodeserver codeserver
|
||||
|
||||
LoopState.Env {authHTTPClient, codebase = Codebase {connection}} <- ask
|
||||
|
||||
-- doesn't handle the case where a non-existent path is supplied
|
||||
Sqlite.runTransaction connection (Ops.loadCausalHashAtPath (pathToSegments (Path.unabsolute localPath)))
|
||||
>>= \case
|
||||
Nothing -> respond (BranchNotFound . Path.absoluteToPath' $ localPath)
|
||||
Just localCausalHash ->
|
||||
case behavior of
|
||||
PushBehavior.RequireEmpty -> do
|
||||
let push :: IO (Either (Sync.SyncError Share.CheckAndSetPushError) ())
|
||||
push =
|
||||
withEntitiesUploadedProgressCallback \entitiesUploadedProgressCallback ->
|
||||
Share.checkAndSetPush
|
||||
authHTTPClient
|
||||
baseURL
|
||||
connection
|
||||
sharePath
|
||||
Nothing
|
||||
localCausalHash
|
||||
entitiesUploadedProgressCallback
|
||||
liftIO push >>= \case
|
||||
Left (Sync.SyncError err) -> respond (Output.ShareError (ShareErrorCheckAndSetPush err))
|
||||
Left (Sync.TransportError err) -> respond (Output.ShareError (ShareErrorTransport err))
|
||||
Right () -> pure ()
|
||||
PushBehavior.RequireNonEmpty -> do
|
||||
let push :: IO (Either (Sync.SyncError Share.FastForwardPushError) ())
|
||||
push = do
|
||||
withEntitiesUploadedProgressCallback \entitiesUploadedProgressCallback ->
|
||||
Share.fastForwardPush
|
||||
authHTTPClient
|
||||
baseURL
|
||||
connection
|
||||
sharePath
|
||||
localCausalHash
|
||||
entitiesUploadedProgressCallback
|
||||
liftIO push >>= \case
|
||||
Left (Sync.SyncError err) -> respond (Output.ShareError (ShareErrorFastForwardPush err))
|
||||
Left (Sync.TransportError err) -> respond (Output.ShareError (ShareErrorTransport err))
|
||||
Right () -> pure ()
|
||||
where
|
||||
pathToSegments :: Path -> [Text]
|
||||
pathToSegments =
|
||||
coerce Path.toList
|
||||
|
||||
-- Provide the given action a callback that prints out the number of entities uploaded, and the number of entities
|
||||
-- enqueued to be uploaded.
|
||||
withEntitiesUploadedProgressCallback :: ((Int -> Int -> IO ()) -> IO a) -> IO a
|
||||
withEntitiesUploadedProgressCallback action = do
|
||||
entitiesUploadedVar <- newTVarIO 0
|
||||
entitiesToUploadVar <- newTVarIO 0
|
||||
Console.Regions.displayConsoleRegions do
|
||||
Console.Regions.withConsoleRegion Console.Regions.Linear \region -> do
|
||||
Console.Regions.setConsoleRegion region do
|
||||
entitiesUploaded <- readTVar entitiesUploadedVar
|
||||
entitiesToUpload <- readTVar entitiesToUploadVar
|
||||
pure $
|
||||
"\n Uploaded "
|
||||
<> tShow entitiesUploaded
|
||||
<> "/"
|
||||
<> tShow (entitiesUploaded + entitiesToUpload)
|
||||
<> " entities...\n\n"
|
||||
result <-
|
||||
action \entitiesUploaded entitiesToUpload ->
|
||||
atomically do
|
||||
writeTVar entitiesUploadedVar entitiesUploaded
|
||||
writeTVar entitiesToUploadVar entitiesToUpload
|
||||
entitiesUploaded <- readTVarIO entitiesUploadedVar
|
||||
Console.Regions.finishConsoleRegion region $
|
||||
"\n Uploaded " <> tShow entitiesUploaded <> " entities.\n"
|
||||
pure result
|
||||
|
||||
-- | Handle a @ShowDefinitionI@ input command, i.e. `view` or `edit`.
|
||||
handleShowDefinition ::
|
||||
forall m v.
|
||||
@ -2079,27 +2262,40 @@ manageLinks silent srcs mdValues op = do
|
||||
-- Takes a maybe (namespace address triple); returns it as-is if `Just`;
|
||||
-- otherwise, tries to load a value from .unisonConfig, and complains
|
||||
-- if needed.
|
||||
resolveConfiguredGitUrl ::
|
||||
resolveConfiguredUrl ::
|
||||
PushPull ->
|
||||
Path' ->
|
||||
ExceptT (Output v) (Action m i v) WriteRemotePath
|
||||
resolveConfiguredGitUrl pushPull destPath' = ExceptT do
|
||||
resolveConfiguredUrl pushPull destPath' = ExceptT do
|
||||
currentPath' <- use LoopState.currentPath
|
||||
let destPath = Path.resolve currentPath' destPath'
|
||||
let configKey = gitUrlKey destPath
|
||||
(eval . ConfigLookup) configKey >>= \case
|
||||
Just url ->
|
||||
case P.parse UriParser.writeRepoPath (Text.unpack configKey) url of
|
||||
let remoteMappingConfigKey = remoteMappingKey destPath
|
||||
(eval . ConfigLookup) remoteMappingConfigKey >>= \case
|
||||
Nothing -> do
|
||||
let gitUrlConfigKey = gitUrlKey destPath
|
||||
-- Fall back to deprecated GitUrl key
|
||||
(eval . ConfigLookup) gitUrlConfigKey >>= \case
|
||||
Just url ->
|
||||
case WriteRemotePathGit <$> P.parse UriParser.deprecatedWriteGitRemotePath (Text.unpack gitUrlConfigKey) url of
|
||||
Left e ->
|
||||
pure . Left $
|
||||
ConfiguredRemoteMappingParseError pushPull destPath url (show e)
|
||||
Right ns ->
|
||||
pure . Right $ ns
|
||||
Nothing ->
|
||||
pure . Left $ NoConfiguredRemoteMapping pushPull destPath
|
||||
Just url -> do
|
||||
case P.parse UriParser.writeRemotePath (Text.unpack remoteMappingConfigKey) url of
|
||||
Left e ->
|
||||
pure . Left $
|
||||
ConfiguredGitUrlParseError pushPull destPath' url (show e)
|
||||
ConfiguredRemoteMappingParseError pushPull destPath url (show e)
|
||||
Right ns ->
|
||||
pure . Right $ ns
|
||||
Nothing ->
|
||||
pure . Left $ NoConfiguredGitUrl pushPull destPath'
|
||||
|
||||
gitUrlKey :: Path.Absolute -> Text
|
||||
gitUrlKey = configKey "GitUrl"
|
||||
where
|
||||
gitUrlKey :: Path.Absolute -> Text
|
||||
gitUrlKey = configKey "GitUrl"
|
||||
remoteMappingKey :: Path.Absolute -> Text
|
||||
remoteMappingKey = configKey "RemoteMapping"
|
||||
|
||||
configKey :: Text -> Path.Absolute -> Text
|
||||
configKey k p =
|
||||
@ -2109,26 +2305,66 @@ configKey k p =
|
||||
NameSegment.toText
|
||||
(Path.toSeq $ Path.unabsolute p)
|
||||
|
||||
viewRemoteBranch ::
|
||||
viewRemoteGitBranch ::
|
||||
(MonadCommand n m i v, MonadUnliftIO m) =>
|
||||
ReadRemoteNamespace ->
|
||||
ReadGitRemoteNamespace ->
|
||||
Git.GitBranchBehavior ->
|
||||
(Branch m -> Free (Command m i v) r) ->
|
||||
n (Either GitError r)
|
||||
viewRemoteBranch ns gitBranchBehavior action = do
|
||||
eval $ ViewRemoteBranch ns gitBranchBehavior action
|
||||
viewRemoteGitBranch ns gitBranchBehavior action = do
|
||||
eval $ ViewRemoteGitBranch ns gitBranchBehavior action
|
||||
|
||||
importRemoteShareBranch :: MonadUnliftIO m => ReadShareRemoteNamespace -> Action' m v (Either (Output v) (Branch m))
|
||||
importRemoteShareBranch ReadShareRemoteNamespace {server, repo, path} = do
|
||||
let codeserver = Codeserver.resolveCodeserver server
|
||||
let baseURL = codeserverBaseURL codeserver
|
||||
ensureAuthenticatedWithCodeserver codeserver
|
||||
mapLeft Output.ShareError <$> do
|
||||
let shareFlavoredPath = Share.Path (repo Nel.:| coerce @[NameSegment] @[Text] (Path.toList path))
|
||||
LoopState.Env {authHTTPClient, codebase = codebase@Codebase {connection}} <- ask
|
||||
let pull :: IO (Either (Sync.SyncError Share.PullError) CausalHash)
|
||||
pull =
|
||||
withEntitiesDownloadedProgressCallback \entitiesDownloadedProgressCallback ->
|
||||
Share.pull
|
||||
authHTTPClient
|
||||
baseURL
|
||||
connection
|
||||
shareFlavoredPath
|
||||
entitiesDownloadedProgressCallback
|
||||
liftIO pull >>= \case
|
||||
Left (Sync.SyncError err) -> pure (Left (Output.ShareErrorPull err))
|
||||
Left (Sync.TransportError err) -> pure (Left (Output.ShareErrorTransport err))
|
||||
Right causalHash -> do
|
||||
(eval . Eval) (Codebase.getBranchForHash codebase (Cv.causalHash2to1 causalHash)) >>= \case
|
||||
Nothing -> error $ reportBug "E412939" "`pull` \"succeeded\", but I can't find the result in the codebase. (This is a bug.)"
|
||||
Just branch -> pure (Right branch)
|
||||
where
|
||||
-- Provide the given action a callback that prints out the number of entities downloaded.
|
||||
withEntitiesDownloadedProgressCallback :: ((Int -> IO ()) -> IO a) -> IO a
|
||||
withEntitiesDownloadedProgressCallback action = do
|
||||
entitiesDownloadedVar <- newTVarIO 0
|
||||
Console.Regions.displayConsoleRegions do
|
||||
Console.Regions.withConsoleRegion Console.Regions.Linear \region -> do
|
||||
Console.Regions.setConsoleRegion region do
|
||||
entitiesDownloaded <- readTVar entitiesDownloadedVar
|
||||
pure ("\n Downloaded " <> tShow entitiesDownloaded <> " entities...\n\n")
|
||||
result <- action \entitiesDownloaded -> atomically (writeTVar entitiesDownloadedVar entitiesDownloaded)
|
||||
entitiesDownloaded <- readTVarIO entitiesDownloadedVar
|
||||
Console.Regions.finishConsoleRegion region $
|
||||
"\n Downloaded " <> tShow entitiesDownloaded <> " entities.\n"
|
||||
pure result
|
||||
|
||||
-- | Given the current root branch of a remote
|
||||
-- (or an empty branch if no root branch exists)
|
||||
-- compute a new branch, which will then be synced and pushed.
|
||||
syncRemoteBranch ::
|
||||
syncGitRemoteBranch ::
|
||||
MonadCommand n m i v =>
|
||||
WriteRepo ->
|
||||
WriteGitRepo ->
|
||||
PushGitBranchOpts ->
|
||||
(Branch m -> m (Either e (Branch m))) ->
|
||||
ExceptT GitError n (Either e (Branch m))
|
||||
syncRemoteBranch repo opts action =
|
||||
ExceptT . eval $ SyncRemoteBranch repo opts action
|
||||
syncGitRemoteBranch repo opts action =
|
||||
ExceptT . eval $ SyncRemoteGitBranch repo opts action
|
||||
|
||||
-- todo: compare to `getHQTerms` / `getHQTypes`. Is one universally better?
|
||||
resolveHQToLabeledDependencies :: Functor m => HQ.HashQualified Name -> Action' m v (Set LabeledDependency)
|
||||
@ -2407,10 +2643,10 @@ searchBranchScored names0 score queries =
|
||||
pair qn
|
||||
HQ.HashQualified qn h
|
||||
| h `SH.isPrefixOf` Referent.toShortHash ref ->
|
||||
pair qn
|
||||
pair qn
|
||||
HQ.HashOnly h
|
||||
| h `SH.isPrefixOf` Referent.toShortHash ref ->
|
||||
Set.singleton (Nothing, result)
|
||||
Set.singleton (Nothing, result)
|
||||
_ -> mempty
|
||||
where
|
||||
result = SR.termSearchResult names0 name ref
|
||||
@ -2427,10 +2663,10 @@ searchBranchScored names0 score queries =
|
||||
pair qn
|
||||
HQ.HashQualified qn h
|
||||
| h `SH.isPrefixOf` Reference.toShortHash ref ->
|
||||
pair qn
|
||||
pair qn
|
||||
HQ.HashOnly h
|
||||
| h `SH.isPrefixOf` Reference.toShortHash ref ->
|
||||
Set.singleton (Nothing, result)
|
||||
Set.singleton (Nothing, result)
|
||||
_ -> mempty
|
||||
where
|
||||
result = SR.typeSearchResult names0 name ref
|
||||
@ -2823,7 +3059,7 @@ docsI srcLoc prettyPrintNames src = do
|
||||
| Set.size s == 1 -> displayI prettyPrintNames ConsoleLocation dotDoc
|
||||
| Set.size s == 0 -> respond $ ListOfLinks mempty []
|
||||
| otherwise -> -- todo: return a list of links here too
|
||||
respond $ ListOfLinks mempty []
|
||||
respond $ ListOfLinks mempty []
|
||||
|
||||
filterBySlurpResult ::
|
||||
Ord v =>
|
||||
|
@ -1,36 +1,24 @@
|
||||
module Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin) where
|
||||
module Unison.Codebase.Editor.HandleInput.AuthLogin (authLogin, ensureAuthenticatedWithCodeserver) where
|
||||
|
||||
import Control.Monad.Reader
|
||||
import Network.URI (URIAuth (..), parseURI)
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import Unison.Auth.CredentialManager (getCredentials)
|
||||
import Unison.Auth.OAuth (authenticateCodeserver)
|
||||
import Unison.Codebase.Editor.HandleInput.LoopState
|
||||
import Unison.Codebase.Editor.Output (Output (CredentialFailureMsg, Success))
|
||||
import Unison.Prelude
|
||||
import Unison.Share.Types
|
||||
import qualified UnliftIO
|
||||
import UnliftIO.Environment (lookupEnv)
|
||||
|
||||
-- | This is the URI where the share API is based.
|
||||
defaultShareURI :: CodeserverURI
|
||||
defaultShareURI = unsafePerformIO $ do
|
||||
lookupEnv "UNISON_SHARE_HOST" <&> \case
|
||||
-- TODO: swap to production share before release.
|
||||
Nothing ->
|
||||
CodeserverURI
|
||||
{ codeserverScheme = "https:",
|
||||
codeserverAuthority = URIAuth {uriUserInfo = "", uriRegName = "share-next.us-west-2.unison-lang.org", uriPort = ""},
|
||||
codeserverPath = "/api"
|
||||
}
|
||||
Just shareHost ->
|
||||
fromMaybe (error $ "Share Host is not a valid URI: " <> shareHost) $ do
|
||||
uri <- parseURI shareHost
|
||||
codeserverFromURI uri
|
||||
{-# NOINLINE defaultShareURI #-}
|
||||
-- | Checks if the user has valid auth for the given codeserver,
|
||||
-- and runs through an authentication flow if not.
|
||||
ensureAuthenticatedWithCodeserver :: UnliftIO.MonadUnliftIO m => CodeserverURI -> Action m i v ()
|
||||
ensureAuthenticatedWithCodeserver codeserverURI = do
|
||||
credsMan <- asks credentialManager
|
||||
getCredentials credsMan (codeserverIdFromCodeserverURI codeserverURI) >>= \case
|
||||
Right _ -> pure ()
|
||||
Left _ -> authLogin codeserverURI
|
||||
|
||||
authLogin :: UnliftIO.MonadUnliftIO m => Action m i v ()
|
||||
authLogin = do
|
||||
let host = defaultShareURI
|
||||
authLogin :: UnliftIO.MonadUnliftIO m => CodeserverURI -> Action m i v ()
|
||||
authLogin host = do
|
||||
credsMan <- asks credentialManager
|
||||
(Action . lift . lift . lift $ authenticateCodeserver credsMan host) >>= \case
|
||||
Left err -> respond (CredentialFailureMsg err)
|
||||
|
@ -11,8 +11,9 @@ import Control.Monad.State
|
||||
import Data.Configurator ()
|
||||
import Data.List.NonEmpty (NonEmpty)
|
||||
import qualified Data.List.NonEmpty as Nel
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import Unison.Auth.CredentialManager (CredentialManager)
|
||||
import Unison.Auth.HTTPClient (AuthenticatedHttpClient)
|
||||
import Unison.Codebase (Codebase)
|
||||
import Unison.Codebase.Branch
|
||||
( Branch (..),
|
||||
)
|
||||
@ -28,18 +29,19 @@ import qualified Unison.Util.Free as Free
|
||||
|
||||
type F m i v = Free (Command m i v)
|
||||
|
||||
data Env = Env
|
||||
{ authHTTPClient :: HTTP.Manager,
|
||||
data Env m v = Env
|
||||
{ authHTTPClient :: AuthenticatedHttpClient,
|
||||
codebase :: Codebase m v Ann,
|
||||
credentialManager :: CredentialManager
|
||||
}
|
||||
|
||||
newtype Action m i v a = Action {unAction :: MaybeT (ReaderT Env (StateT (LoopState m v) (F m i v))) a}
|
||||
deriving newtype (Functor, Applicative, Alternative, Monad, MonadIO, MonadState (LoopState m v), MonadReader Env)
|
||||
newtype Action m i v a = Action {unAction :: MaybeT (ReaderT (Env m v) (StateT (LoopState m v) (F m i v))) a}
|
||||
deriving newtype (Functor, Applicative, Alternative, Monad, MonadIO, MonadState (LoopState m v), MonadReader (Env m v))
|
||||
-- We should likely remove this MonadFail instance since it's really hard to debug,
|
||||
-- but it's currently in use.
|
||||
deriving newtype (MonadFail)
|
||||
|
||||
runAction :: Env -> LoopState m v -> Action m i v a -> (F m i v (Maybe a, LoopState m v))
|
||||
runAction :: Env m v -> LoopState m v -> Action m i v a -> (F m i v (Maybe a, LoopState m v))
|
||||
runAction env state (Action m) =
|
||||
m
|
||||
& runMaybeT
|
||||
@ -125,3 +127,8 @@ respondNumbered output = do
|
||||
args <- eval $ NotifyNumbered output
|
||||
unless (null args) $
|
||||
numberedArgs .= toList args
|
||||
|
||||
-- | Get the codebase out of the environment.
|
||||
askCodebase :: Action m i v (Codebase m v Ann)
|
||||
askCodebase =
|
||||
asks codebase
|
||||
|
@ -188,9 +188,9 @@ data Input
|
||||
| VersionI
|
||||
deriving (Eq, Show)
|
||||
|
||||
-- | @"gist repo"@ pushes the contents of the current namespace to @repo@.
|
||||
-- | @"push.gist repo"@ pushes the contents of the current namespace to @repo@.
|
||||
data GistInput = GistInput
|
||||
{ repo :: WriteRepo
|
||||
{ repo :: WriteGitRepo
|
||||
}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
|
@ -10,6 +10,7 @@ module Unison.Codebase.Editor.Output
|
||||
UndoFailureReason (..),
|
||||
PushPull (..),
|
||||
ReflogEntry (..),
|
||||
ShareError (..),
|
||||
pushPull,
|
||||
isFailure,
|
||||
isNumberedFailure,
|
||||
@ -57,6 +58,7 @@ import qualified Unison.Reference as Reference
|
||||
import Unison.Referent (Referent)
|
||||
import Unison.Server.Backend (ShallowListEntry (..))
|
||||
import Unison.Server.SearchResult' (SearchResult')
|
||||
import qualified Unison.Share.Sync.Types as Sync
|
||||
import Unison.ShortHash (ShortHash)
|
||||
import Unison.Term (Term)
|
||||
import Unison.Type (Type)
|
||||
@ -207,9 +209,10 @@ data Output v
|
||||
-- and a nicer render.
|
||||
BustedBuiltins (Set Reference) (Set Reference)
|
||||
| GitError GitError
|
||||
| ShareError ShareError
|
||||
| ConfiguredMetadataParseError Path' String (P.Pretty P.ColorText)
|
||||
| NoConfiguredGitUrl PushPull Path'
|
||||
| ConfiguredGitUrlParseError PushPull Path' Text String
|
||||
| NoConfiguredRemoteMapping PushPull Path.Absolute
|
||||
| ConfiguredRemoteMappingParseError PushPull Path.Absolute Text String
|
||||
| MetadataMissingType PPE.PrettyPrintEnv Referent
|
||||
| TermMissingType Reference
|
||||
| MetadataAmbiguous (HQ.HashQualified Name) PPE.PrettyPrintEnv [Referent]
|
||||
@ -248,9 +251,9 @@ data Output v
|
||||
| NamespaceEmpty (NonEmpty AbsBranchId)
|
||||
| NoOp
|
||||
| -- Refused to push, either because a `push` targeted an empty namespace, or a `push.create` targeted a non-empty namespace.
|
||||
RefusedToPush PushBehavior
|
||||
| -- | @GistCreated repo hash@ means causal @hash@ was just published to @repo@.
|
||||
GistCreated Int WriteRepo Branch.CausalHash
|
||||
RefusedToPush PushBehavior WriteRemotePath
|
||||
| -- | @GistCreated repo@ means a causal was just published to @repo@.
|
||||
GistCreated ReadRemoteNamespace
|
||||
| -- | Directs the user to URI to begin an authorization flow.
|
||||
InitiateAuthFlow URI
|
||||
| UnknownCodeServer Text
|
||||
@ -258,6 +261,13 @@ data Output v
|
||||
| PrintVersion Text
|
||||
| IntegrityCheck IntegrityResult
|
||||
|
||||
data ShareError
|
||||
= ShareErrorCheckAndSetPush Sync.CheckAndSetPushError
|
||||
| ShareErrorFastForwardPush Sync.FastForwardPushError
|
||||
| ShareErrorPull Sync.PullError
|
||||
| ShareErrorGetCausalHashByPath Sync.GetCausalHashByPathError
|
||||
| ShareErrorTransport Sync.CodeserverTransportError
|
||||
|
||||
data ReflogEntry = ReflogEntry {hash :: ShortBranchHash, reason :: Text}
|
||||
deriving (Show)
|
||||
|
||||
@ -345,8 +355,8 @@ isFailure o = case o of
|
||||
GitError {} -> True
|
||||
BustedBuiltins {} -> True
|
||||
ConfiguredMetadataParseError {} -> True
|
||||
NoConfiguredGitUrl {} -> True
|
||||
ConfiguredGitUrlParseError {} -> True
|
||||
NoConfiguredRemoteMapping {} -> True
|
||||
ConfiguredRemoteMappingParseError {} -> True
|
||||
MetadataMissingType {} -> True
|
||||
MetadataAmbiguous {} -> True
|
||||
PatchNeedsToBeConflictFree {} -> True
|
||||
@ -387,6 +397,7 @@ isFailure o = case o of
|
||||
case r of
|
||||
NoIntegrityErrors -> False
|
||||
IntegrityErrorDetected {} -> True
|
||||
ShareError {} -> True
|
||||
|
||||
isNumberedFailure :: NumberedOutput v -> Bool
|
||||
isNumberedFailure = \case
|
||||
|
5
unison-cli/src/Unison/Codebase/Editor/UCMVersion.hs
Normal file
5
unison-cli/src/Unison/Codebase/Editor/UCMVersion.hs
Normal file
@ -0,0 +1,5 @@
|
||||
module Unison.Codebase.Editor.UCMVersion where
|
||||
|
||||
import Data.Text (Text)
|
||||
|
||||
type UCMVersion = Text
|
@ -1,6 +1,12 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Unison.Codebase.Editor.UriParser (repoPath, writeRepo, writeRepoPath) where
|
||||
module Unison.Codebase.Editor.UriParser
|
||||
( repoPath,
|
||||
writeGitRepo,
|
||||
deprecatedWriteGitRemotePath,
|
||||
writeRemotePath,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Char (isAlphaNum, isDigit, isSpace)
|
||||
import Data.Sequence as Seq
|
||||
@ -8,14 +14,24 @@ import Data.Text as Text
|
||||
import Data.Void
|
||||
import qualified Text.Megaparsec as P
|
||||
import qualified Text.Megaparsec.Char as C
|
||||
import qualified Text.Megaparsec.Char.Lexer as L
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, ReadRepo (..), WriteRemotePath, WriteRepo (..))
|
||||
import Unison.Codebase.Editor.RemoteRepo
|
||||
( ReadGitRemoteNamespace (..),
|
||||
ReadGitRepo (..),
|
||||
ReadRemoteNamespace (..),
|
||||
ReadShareRemoteNamespace (..),
|
||||
ShareCodeserver (DefaultCodeserver),
|
||||
WriteGitRemotePath (..),
|
||||
WriteGitRepo (..),
|
||||
WriteRemotePath (..),
|
||||
WriteShareRemotePath (..),
|
||||
)
|
||||
import Unison.Codebase.Path (Path (..))
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
import Unison.Codebase.ShortBranchHash (ShortBranchHash (..))
|
||||
import qualified Unison.Hash as Hash
|
||||
import qualified Unison.Lexer
|
||||
import Unison.NameSegment (NameSegment (..))
|
||||
import qualified Unison.NameSegment as NameSegment
|
||||
import Unison.Prelude
|
||||
|
||||
type P = P.Parsec Void Text.Text
|
||||
@ -39,30 +55,157 @@ type P = P.Parsec Void Text.Text
|
||||
-- $ git clone [user@]server:project.git[:treeish][:[#hash][.path]]
|
||||
|
||||
repoPath :: P ReadRemoteNamespace
|
||||
repoPath = P.label "generic git repo" $ do
|
||||
protocol <- parseProtocol
|
||||
treeish <- P.optional treeishSuffix
|
||||
repoPath =
|
||||
P.label "generic repo" $
|
||||
fmap ReadRemoteNamespaceGit readGitRemoteNamespace
|
||||
<|> fmap ReadRemoteNamespaceShare readShareRemoteNamespace
|
||||
|
||||
-- >>> P.parseMaybe writeRemotePath "unisonweb.base._releases.M4"
|
||||
-- >>> P.parseMaybe writeRemotePath "git(git@github.com:unisonweb/base:v3)._releases.M3"
|
||||
-- Just (WriteRemotePathShare (WriteShareRemotePath {server = ShareRepo, repo = "unisonweb", path = base._releases.M4}))
|
||||
-- Just (WriteRemotePathGit (WriteGitRemotePath {repo = WriteGitRepo {url = "git@github.com:unisonweb/base", branch = Just "v3"}, path = _releases.M3}))
|
||||
writeRemotePath :: P WriteRemotePath
|
||||
writeRemotePath =
|
||||
(fmap WriteRemotePathGit writeGitRemotePath)
|
||||
<|> fmap WriteRemotePathShare writeShareRemotePath
|
||||
|
||||
-- >>> P.parseMaybe writeShareRemotePath "unisonweb.base._releases.M4"
|
||||
-- Just (WriteShareRemotePath {server = ShareRepo, repo = "unisonweb", path = base._releases.M4})
|
||||
writeShareRemotePath :: P WriteShareRemotePath
|
||||
writeShareRemotePath =
|
||||
P.label "write share remote path" $
|
||||
WriteShareRemotePath
|
||||
<$> pure DefaultCodeserver
|
||||
<*> (NameSegment.toText <$> nameSegment)
|
||||
<*> (Path.fromList <$> P.many (C.char '.' *> nameSegment))
|
||||
|
||||
-- >>> P.parseMaybe readShareRemoteNamespace ".unisonweb.base._releases.M4"
|
||||
-- >>> P.parseMaybe readShareRemoteNamespace "unisonweb.base._releases.M4"
|
||||
-- Nothing
|
||||
-- Just (ReadShareRemoteNamespace {server = ShareRepo, repo = "unisonweb", path = base._releases.M4})
|
||||
readShareRemoteNamespace :: P ReadShareRemoteNamespace
|
||||
readShareRemoteNamespace = do
|
||||
P.label "read share remote namespace" $
|
||||
ReadShareRemoteNamespace
|
||||
<$> pure DefaultCodeserver
|
||||
-- <*> sbh <- P.optional shortBranchHash
|
||||
<*> (NameSegment.toText <$> nameSegment)
|
||||
<*> (Path.fromList <$> P.many (C.char '.' *> nameSegment))
|
||||
|
||||
-- >>> P.parseMaybe readGitRemoteNamespace "git(user@server:project.git:branch)#asdf"
|
||||
-- >>> P.parseMaybe readGitRemoteNamespace "git(user@server:project.git:branch)#asdf."
|
||||
-- >>> P.parseMaybe readGitRemoteNamespace "git(user@server:project.git:branch)"
|
||||
-- >>> P.parseMaybe readGitRemoteNamespace "git(git@github.com:unisonweb/base:v3)._releases.M3"
|
||||
-- >>> P.parseMaybe readGitRemoteNamespace "git( user@server:project.git:branch )#asdf.foo.bar"
|
||||
-- Just (ReadGitRemoteNamespace {repo = ReadGitRepo {url = "user@server:project.git", ref = Just "branch"}, sbh = Just #asdf, path = })
|
||||
-- Just (ReadGitRemoteNamespace {repo = ReadGitRepo {url = "user@server:project.git", ref = Just "branch"}, sbh = Just #asdf, path = })
|
||||
-- Just (ReadGitRemoteNamespace {repo = ReadGitRepo {url = "user@server:project.git", ref = Just "branch"}, sbh = Nothing, path = })
|
||||
-- Just (ReadGitRemoteNamespace {repo = ReadGitRepo {url = "git@github.com:unisonweb/base", ref = Just "v3"}, sbh = Nothing, path = _releases.M3})
|
||||
-- Just (ReadGitRemoteNamespace {repo = ReadGitRepo {url = "user@server:project.git", ref = Just "branch"}, sbh = Just #asdf, path = foo.bar})
|
||||
readGitRemoteNamespace :: P ReadGitRemoteNamespace
|
||||
readGitRemoteNamespace = P.label "generic git repo" $ do
|
||||
C.string "git("
|
||||
protocol <- parseGitProtocol
|
||||
treeish <- P.optional gitTreeishSuffix
|
||||
let repo = ReadGitRepo {url = printProtocol protocol, ref = treeish}
|
||||
nshashPath <- P.optional (C.char ':' *> namespaceHashPath)
|
||||
case nshashPath of
|
||||
Nothing -> pure (repo, Nothing, Path.empty)
|
||||
Just (sbh, p) -> pure (repo, sbh, p)
|
||||
C.string ")"
|
||||
nshashPath <- P.optional namespaceHashPath
|
||||
pure case nshashPath of
|
||||
Nothing -> ReadGitRemoteNamespace {repo, sbh = Nothing, path = Path.empty}
|
||||
Just (sbh, path) -> ReadGitRemoteNamespace {repo, sbh, path}
|
||||
|
||||
writeRepo :: P WriteRepo
|
||||
writeRepo = P.label "repo root for writing" $ do
|
||||
uri <- parseProtocol
|
||||
treeish <- P.optional treeishSuffix
|
||||
pure WriteGitRepo {url' = printProtocol uri, branch = treeish}
|
||||
-- >>> P.parseMaybe writeGitRepo "git(/srv/git/project.git)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(/srv/git/project.git:branch)"
|
||||
-- Just (WriteGitRepo {url = "/srv/git/project.git", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "/srv/git/project.git", branch = Just "branch"})
|
||||
--
|
||||
-- >>> P.parseMaybe writeGitRepo "git(file:///srv/git/project.git)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(file:///srv/git/project.git:branch)"
|
||||
-- Just (WriteGitRepo {url = "file:///srv/git/project.git", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "file:///srv/git/project.git", branch = Just "branch"})
|
||||
--
|
||||
-- >>> P.parseMaybe writeGitRepo "git(https://example.com/gitproject.git)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(https://example.com/gitproject.git:base)"
|
||||
-- Just (WriteGitRepo {url = "https://example.com/gitproject.git", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "https://example.com/gitproject.git", branch = Just "base"})
|
||||
--
|
||||
-- >>> P.parseMaybe writeGitRepo "git(ssh://user@server/project.git)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(ssh://user@server/project.git:branch)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(ssh://server/project.git)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(ssh://server/project.git:branch)"
|
||||
-- Just (WriteGitRepo {url = "ssh://user@server/project.git", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "ssh://user@server/project.git", branch = Just "branch"})
|
||||
-- Just (WriteGitRepo {url = "ssh://server/project.git", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "ssh://server/project.git", branch = Just "branch"})
|
||||
--
|
||||
-- >>> P.parseMaybe writeGitRepo "git(server:project)"
|
||||
-- >>> P.parseMaybe writeGitRepo "git(user@server:project.git:branch)"
|
||||
-- Just (WriteGitRepo {url = "server:project", branch = Nothing})
|
||||
-- Just (WriteGitRepo {url = "user@server:project.git", branch = Just "branch"})
|
||||
writeGitRepo :: P WriteGitRepo
|
||||
writeGitRepo = P.label "repo root for writing" $ do
|
||||
C.string "git("
|
||||
uri <- parseGitProtocol
|
||||
treeish <- P.optional gitTreeishSuffix
|
||||
C.string ")"
|
||||
pure WriteGitRepo {url = printProtocol uri, branch = treeish}
|
||||
|
||||
writeRepoPath :: P WriteRemotePath
|
||||
writeRepoPath = P.label "generic git repo" $ do
|
||||
repo <- writeRepo
|
||||
-- | A parser for the deprecated format of git URLs, which may still exist in old GitURL
|
||||
-- unisonConfigs.
|
||||
--
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "/srv/git/project.git:.namespace"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "/srv/git/project.git:branch:.namespace"
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "/srv/git/project.git", branch = Nothing}, path = namespace})
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "/srv/git/project.git", branch = Just "branch"}, path = namespace})
|
||||
--
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "file:///srv/git/project.git"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "file:///srv/git/project.git:branch"
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "file:///srv/git/project.git", branch = Nothing}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "file:///srv/git/project.git", branch = Just "branch"}, path = })
|
||||
--
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "https://example.com/gitproject.git"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "https://example.com/gitproject.git:base"
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "https://example.com/gitproject.git", branch = Nothing}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "https://example.com/gitproject.git", branch = Just "base"}, path = })
|
||||
--
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "ssh://user@server/project.git"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "ssh://user@server/project.git:branch"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "ssh://server/project.git"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "ssh://server/project.git:branch"
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "ssh://user@server/project.git", branch = Nothing}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "ssh://user@server/project.git", branch = Just "branch"}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "ssh://server/project.git", branch = Nothing}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "ssh://server/project.git", branch = Just "branch"}, path = })
|
||||
--
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "server:project"
|
||||
-- >>> P.parseMaybe deprecatedWriteGitRemotePath "user@server:project.git:branch"
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "server:project", branch = Nothing}, path = })
|
||||
-- Just (WriteGitRemotePath {repo = WriteGitRepo {url = "user@server:project.git", branch = Just "branch"}, path = })
|
||||
deprecatedWriteGitRemotePath :: P WriteGitRemotePath
|
||||
deprecatedWriteGitRemotePath = P.label "generic write repo" $ do
|
||||
repo <- deprecatedWriteGitRepo
|
||||
path <- P.optional (C.char ':' *> absolutePath)
|
||||
pure (repo, fromMaybe Path.empty path)
|
||||
pure WriteGitRemotePath {repo, path = fromMaybe Path.empty path}
|
||||
where
|
||||
deprecatedWriteGitRepo :: P WriteGitRepo
|
||||
deprecatedWriteGitRepo = do
|
||||
P.label "repo root for writing" $ do
|
||||
uri <- parseGitProtocol
|
||||
treeish <- P.optional deprecatedTreeishSuffix
|
||||
pure WriteGitRepo {url = printProtocol uri, branch = treeish}
|
||||
deprecatedTreeishSuffix :: P Text
|
||||
deprecatedTreeishSuffix = P.label "git treeish" . P.try $ do
|
||||
void $ C.char ':'
|
||||
notdothash <- P.noneOf @[] ".#:"
|
||||
rest <- P.takeWhileP (Just "not colon") (/= ':')
|
||||
pure $ Text.cons notdothash rest
|
||||
|
||||
-- does this not exist somewhere in megaparsec? yes in 7.0
|
||||
symbol :: Text -> P Text
|
||||
symbol = L.symbol (pure ())
|
||||
-- git(myrepo@git.com).foo.bar
|
||||
writeGitRemotePath :: P WriteGitRemotePath
|
||||
writeGitRemotePath = P.label "generic write repo" $ do
|
||||
repo <- writeGitRepo
|
||||
path <- P.optional absolutePath
|
||||
pure WriteGitRemotePath {repo, path = fromMaybe Path.empty path}
|
||||
|
||||
data GitProtocol
|
||||
= HttpsProtocol (Maybe User) HostInfo UrlPath
|
||||
@ -110,29 +253,29 @@ type Host = Text -- no port
|
||||
-- doesn't yet handle basic authentication like https://user:pass@server.com
|
||||
-- (does anyone even want that?)
|
||||
-- or handle ipv6 addresses (https://en.wikipedia.org/wiki/IPv6#Addressing)
|
||||
parseProtocol :: P GitProtocol
|
||||
parseProtocol =
|
||||
P.label "parseProtocol" $
|
||||
parseGitProtocol :: P GitProtocol
|
||||
parseGitProtocol =
|
||||
P.label "parseGitProtocol" $
|
||||
fileRepo <|> httpsRepo <|> sshRepo <|> scpRepo <|> localRepo
|
||||
where
|
||||
localRepo, fileRepo, httpsRepo, sshRepo, scpRepo :: P GitProtocol
|
||||
parsePath =
|
||||
P.takeWhile1P
|
||||
(Just "repo path character")
|
||||
(\c -> not (isSpace c || c == ':'))
|
||||
(\c -> not (isSpace c || c == ':' || c == ')'))
|
||||
localRepo = LocalProtocol <$> parsePath
|
||||
fileRepo = P.label "fileRepo" $ do
|
||||
void $ symbol "file://"
|
||||
void $ C.string "file://"
|
||||
FileProtocol <$> parsePath
|
||||
httpsRepo = P.label "httpsRepo" $ do
|
||||
void $ symbol "https://"
|
||||
void $ C.string "https://"
|
||||
HttpsProtocol <$> P.optional userInfo <*> parseHostInfo <*> parsePath
|
||||
sshRepo = P.label "sshRepo" $ do
|
||||
void $ symbol "ssh://"
|
||||
void $ C.string "ssh://"
|
||||
SshProtocol <$> P.optional userInfo <*> parseHostInfo <*> parsePath
|
||||
scpRepo =
|
||||
P.label "scpRepo" . P.try $
|
||||
ScpProtocol <$> P.optional userInfo <*> parseHost <* symbol ":" <*> parsePath
|
||||
ScpProtocol <$> P.optional userInfo <*> parseHost <* C.string ":" <*> parsePath
|
||||
userInfo :: P User
|
||||
userInfo = P.label "userInfo" . P.try $ do
|
||||
username <- P.takeWhile1P (Just "username character") (/= '@')
|
||||
@ -143,7 +286,7 @@ parseProtocol =
|
||||
P.label "parseHostInfo" $
|
||||
HostInfo <$> parseHost
|
||||
<*> ( P.optional $ do
|
||||
void $ symbol ":"
|
||||
void $ C.char ':'
|
||||
P.takeWhile1P (Just "digits") isDigit
|
||||
)
|
||||
|
||||
@ -164,29 +307,47 @@ parseProtocol =
|
||||
pure $ Text.pack $ o1 <> "." <> o2 <> "." <> o3 <> "." <> o4
|
||||
decOctet = P.count' 1 3 C.digitChar
|
||||
|
||||
-- #nshashabc.path.foo.bar or .path.foo.bar
|
||||
-- >>> P.parseMaybe namespaceHashPath "#nshashabc.path.foo.bar"
|
||||
-- Just (Just #nshashabc,path.foo.bar)
|
||||
--
|
||||
-- >>> P.parseMaybe namespaceHashPath ".path.foo.bar"
|
||||
-- Just (Nothing,path.foo.bar)
|
||||
--
|
||||
-- >>> P.parseMaybe namespaceHashPath "#nshashabc"
|
||||
-- Just (Just #nshashabc,)
|
||||
--
|
||||
-- >>> P.parseMaybe namespaceHashPath "#nshashabc."
|
||||
-- Just (Just #nshashabc,)
|
||||
--
|
||||
-- >>> P.parseMaybe namespaceHashPath "."
|
||||
-- Just (Nothing,)
|
||||
namespaceHashPath :: P (Maybe ShortBranchHash, Path)
|
||||
namespaceHashPath = do
|
||||
sbh <- P.optional shortBranchHash
|
||||
p <- P.optional absolutePath
|
||||
pure (sbh, fromMaybe Path.empty p)
|
||||
|
||||
-- >>> P.parseMaybe absolutePath "."
|
||||
-- Just
|
||||
--
|
||||
-- >>> P.parseMaybe absolutePath ".path.foo.bar"
|
||||
-- Just path.foo.bar
|
||||
absolutePath :: P Path
|
||||
absolutePath = do
|
||||
void $ C.char '.'
|
||||
Path . Seq.fromList . fmap (NameSegment . Text.pack)
|
||||
<$> P.sepBy1
|
||||
( (:) <$> P.satisfy Unison.Lexer.wordyIdStartChar
|
||||
<*> P.many (P.satisfy Unison.Lexer.wordyIdChar)
|
||||
)
|
||||
(C.char '.')
|
||||
Path . Seq.fromList <$> P.sepBy nameSegment (C.char '.')
|
||||
|
||||
treeishSuffix :: P Text
|
||||
treeishSuffix = P.label "git treeish" . P.try $ do
|
||||
nameSegment :: P NameSegment
|
||||
nameSegment =
|
||||
NameSegment . Text.pack
|
||||
<$> ( (:) <$> P.satisfy Unison.Lexer.wordyIdStartChar
|
||||
<*> P.many (P.satisfy Unison.Lexer.wordyIdChar)
|
||||
)
|
||||
|
||||
gitTreeishSuffix :: P Text
|
||||
gitTreeishSuffix = P.label "git treeish" . P.try $ do
|
||||
void $ C.char ':'
|
||||
notdothash <- P.noneOf @[] ".#:"
|
||||
rest <- P.takeWhileP (Just "not colon") (/= ':')
|
||||
pure $ Text.cons notdothash rest
|
||||
P.takeWhile1P (Just "not close paren") (/= ')')
|
||||
|
||||
shortBranchHash :: P ShortBranchHash
|
||||
shortBranchHash = P.label "short branch hash" $ do
|
||||
|
@ -15,7 +15,7 @@ import qualified Unison.Codebase.Path as Path
|
||||
-- "release/M1j" -> "releases._M1j"
|
||||
-- "release/M1j.2" -> "releases._M1j_2"
|
||||
-- "latest-*" -> "trunk"
|
||||
defaultBaseLib :: Parsec Void Text ReadRemoteNamespace
|
||||
defaultBaseLib :: Parsec Void Text ReadGitRemoteNamespace
|
||||
defaultBaseLib = fmap makeNS $ latest <|> release
|
||||
where
|
||||
latest, release, version :: Parsec Void Text Text
|
||||
@ -23,16 +23,18 @@ defaultBaseLib = fmap makeNS $ latest <|> release
|
||||
release = fmap ("releases._" <>) $ "release/" *> version <* eof
|
||||
version = do
|
||||
Text.pack <$> some (alphaNumChar <|> ('_' <$ oneOf ['.', '_', '-']))
|
||||
makeNS :: Text -> ReadRemoteNamespace
|
||||
makeNS :: Text -> ReadGitRemoteNamespace
|
||||
makeNS t =
|
||||
( ReadGitRepo
|
||||
{ url = "https://github.com/unisonweb/base",
|
||||
-- Use the 'v4' branch of base for now.
|
||||
-- We can revert back to the main branch once enough people have upgraded ucm and
|
||||
-- we're okay with pushing the v4 base codebase to main (perhaps by the next ucm
|
||||
-- release).
|
||||
ref = Just "v4"
|
||||
},
|
||||
Nothing,
|
||||
Path.fromText t
|
||||
)
|
||||
ReadGitRemoteNamespace
|
||||
{ repo =
|
||||
ReadGitRepo
|
||||
{ url = "https://github.com/unisonweb/base",
|
||||
-- Use the 'v4' branch of base for now.
|
||||
-- We can revert back to the main branch once enough people have upgraded ucm and
|
||||
-- we're okay with pushing the v3 base codebase to main (perhaps by the next ucm
|
||||
-- release).
|
||||
ref = Just "v4"
|
||||
},
|
||||
sbh = Nothing,
|
||||
path = Path.fromText t
|
||||
}
|
||||
|
@ -33,19 +33,24 @@ import qualified Data.Map as Map
|
||||
import qualified Data.Text as Text
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import System.Directory (doesFileExist)
|
||||
import System.Environment (lookupEnv)
|
||||
import System.Exit (die)
|
||||
import qualified System.IO as IO
|
||||
import System.IO.Error (catchIOError)
|
||||
import qualified Text.Megaparsec as P
|
||||
import qualified Unison.Auth.CredentialManager as AuthN
|
||||
import qualified Unison.Auth.HTTPClient as AuthN
|
||||
import qualified Unison.Auth.Tokens as AuthN
|
||||
import Unison.Codebase (Codebase)
|
||||
import qualified Unison.Codebase as Codebase
|
||||
import qualified Unison.Codebase.Branch as Branch
|
||||
import Unison.Codebase.Editor.Command (LoadSourceResult (..), UCMVersion)
|
||||
import Unison.Codebase.Editor.Command (LoadSourceResult (..))
|
||||
import qualified Unison.Codebase.Editor.HandleCommand as HandleCommand
|
||||
import qualified Unison.Codebase.Editor.HandleInput as HandleInput
|
||||
import qualified Unison.Codebase.Editor.HandleInput.LoopState as LoopState
|
||||
import Unison.Codebase.Editor.Input (Event (UnisonFileChanged), Input (..))
|
||||
import qualified Unison.Codebase.Editor.Output as Output
|
||||
import Unison.Codebase.Editor.UCMVersion (UCMVersion)
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
import qualified Unison.Codebase.Path.Parse as Path
|
||||
import qualified Unison.Codebase.Runtime as Runtime
|
||||
@ -70,6 +75,13 @@ import Prelude hiding (readFile, writeFile)
|
||||
terminalWidth :: Pretty.Width
|
||||
terminalWidth = 65
|
||||
|
||||
-- | If provided, this access token will be used on all
|
||||
-- requests which use the Authenticated HTTP Client; i.e. all codeserver interactions.
|
||||
--
|
||||
-- It's useful in scripted contexts or when running transcripts against a codeserver.
|
||||
accessTokenEnvVarKey :: String
|
||||
accessTokenEnvVarKey = "UNISON_SHARE_ACCESS_TOKEN"
|
||||
|
||||
type ExpectingError = Bool
|
||||
|
||||
type ScratchFileName = Text
|
||||
@ -218,6 +230,15 @@ run dir stanzas codebase runtime config ucmVersion baseURL = UnliftIO.try $ do
|
||||
]
|
||||
root <- Codebase.getRootBranch codebase
|
||||
do
|
||||
mayShareAccessToken <- fmap Text.pack <$> lookupEnv accessTokenEnvVarKey
|
||||
credMan <- AuthN.newCredentialManager
|
||||
let tokenProvider :: AuthN.TokenProvider
|
||||
tokenProvider =
|
||||
case mayShareAccessToken of
|
||||
Nothing -> do
|
||||
AuthN.newTokenProvider credMan
|
||||
Just accessToken ->
|
||||
\_codeserverID -> pure $ Right accessToken
|
||||
pathRef <- newIORef initialPath
|
||||
rootBranchRef <- newIORef root
|
||||
numberedArgsRef <- newIORef []
|
||||
@ -360,6 +381,7 @@ run dir stanzas codebase runtime config ucmVersion baseURL = UnliftIO.try $ do
|
||||
let f = LoadSuccess <$> readUtf8 (Text.unpack name)
|
||||
in f <|> pure InvalidSourceNameError
|
||||
|
||||
print :: Output.Output Symbol -> IO ()
|
||||
print o = do
|
||||
msg <- notifyUser dir o
|
||||
errOk <- readIORef allowErrors
|
||||
@ -370,6 +392,7 @@ run dir stanzas codebase runtime config ucmVersion baseURL = UnliftIO.try $ do
|
||||
then writeIORef hasErrors True
|
||||
else dieWithMsg rendered
|
||||
|
||||
printNumbered :: Output.NumberedOutput Symbol -> IO Output.NumberedArgs
|
||||
printNumbered o = do
|
||||
let (msg, numberedArgs) = notifyNumbered o
|
||||
errOk <- readIORef allowErrors
|
||||
@ -419,12 +442,14 @@ run dir stanzas codebase runtime config ucmVersion baseURL = UnliftIO.try $ do
|
||||
"The transcript was expecting an error in the stanza above, but did not encounter one."
|
||||
]
|
||||
|
||||
loop state = do
|
||||
authenticatedHTTPClient <- AuthN.newAuthenticatedHTTPClient print tokenProvider ucmVersion
|
||||
let loop state = do
|
||||
writeIORef pathRef (view LoopState.currentPath state)
|
||||
let env =
|
||||
LoopState.Env
|
||||
{ LoopState.authHTTPClient = error "Error: No access to authorized requests from transcripts.",
|
||||
LoopState.credentialManager = error "Error: No access to credentials from transcripts."
|
||||
{ LoopState.authHTTPClient = authenticatedHTTPClient,
|
||||
LoopState.codebase = codebase,
|
||||
LoopState.credentialManager = credMan
|
||||
}
|
||||
let free = LoopState.runAction env state $ HandleInput.loop
|
||||
rng i = pure $ Random.drgNewSeed (Random.seedFromInteger (fromIntegral i))
|
||||
|
@ -22,7 +22,7 @@ import qualified Unison.Codebase.Branch.Merge as Branch
|
||||
import qualified Unison.Codebase.Branch.Names as Branch
|
||||
import Unison.Codebase.Editor.Input (Input)
|
||||
import qualified Unison.Codebase.Editor.Input as Input
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, WriteRemotePath, WriteRepo)
|
||||
import Unison.Codebase.Editor.RemoteRepo (ReadRemoteNamespace, WriteGitRepo, WriteRemotePath)
|
||||
import qualified Unison.Codebase.Editor.SlurpResult as SR
|
||||
import qualified Unison.Codebase.Editor.UriParser as UriParser
|
||||
import qualified Unison.Codebase.Path as Path
|
||||
@ -998,7 +998,7 @@ pullImpl name verbosity pullMode addendum = do
|
||||
name
|
||||
[]
|
||||
I.Visible
|
||||
[(Optional, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Optional, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap
|
||||
"The"
|
||||
@ -1017,8 +1017,7 @@ pullImpl name verbosity pullMode addendum = do
|
||||
),
|
||||
( makeExample' self,
|
||||
"merges the remote namespace configured in `.unisonConfig`"
|
||||
<> "with the key `GitUrl.ns` where `ns` is the current namespace,"
|
||||
<> "into the current namespace"
|
||||
<> "at the key `RemoteMappings.<namespace>` where `<namespace>` is the current namespace,"
|
||||
)
|
||||
],
|
||||
"",
|
||||
@ -1029,10 +1028,10 @@ pullImpl name verbosity pullMode addendum = do
|
||||
[] ->
|
||||
Right $ Input.PullRemoteBranchI Nothing Path.relativeEmpty' SyncMode.ShortCircuit pullMode verbosity
|
||||
[url] -> do
|
||||
ns <- parseUri "url" url
|
||||
ns <- parseReadRemoteNamespace "remote-namespace" url
|
||||
Right $ Input.PullRemoteBranchI (Just ns) Path.relativeEmpty' SyncMode.ShortCircuit pullMode verbosity
|
||||
[url, path] -> do
|
||||
ns <- parseUri "url" url
|
||||
ns <- parseReadRemoteNamespace "remote-namespace" url
|
||||
p <- first fromString $ Path.parsePath' path
|
||||
Right $ Input.PullRemoteBranchI (Just ns) p SyncMode.ShortCircuit pullMode verbosity
|
||||
_ -> Left (I.help self)
|
||||
@ -1044,7 +1043,7 @@ pullExhaustive =
|
||||
"debug.pull-exhaustive"
|
||||
[]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap $
|
||||
"The " <> makeExample' pullExhaustive <> "command can be used in place of"
|
||||
@ -1058,10 +1057,10 @@ pullExhaustive =
|
||||
[] ->
|
||||
Right $ Input.PullRemoteBranchI Nothing Path.relativeEmpty' SyncMode.Complete Input.PullWithHistory Verbosity.Default
|
||||
[url] -> do
|
||||
ns <- parseUri "url" url
|
||||
ns <- parseReadRemoteNamespace "remote-namespace" url
|
||||
Right $ Input.PullRemoteBranchI (Just ns) Path.relativeEmpty' SyncMode.Complete Input.PullWithHistory Verbosity.Default
|
||||
[url, path] -> do
|
||||
ns <- parseUri "url" url
|
||||
ns <- parseReadRemoteNamespace "remote-namespace" url
|
||||
p <- first fromString $ Path.parsePath' path
|
||||
Right $ Input.PullRemoteBranchI (Just ns) p SyncMode.Complete Input.PullWithHistory Verbosity.Default
|
||||
_ -> Left (I.help pull)
|
||||
@ -1073,7 +1072,7 @@ push =
|
||||
"push"
|
||||
[]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap
|
||||
"The `push` command merges a local namespace into a remote namespace.",
|
||||
@ -1087,9 +1086,8 @@ push =
|
||||
"publishes the current namespace into the remote namespace `remote`"
|
||||
),
|
||||
( "`push`",
|
||||
"publishes the current namespace"
|
||||
<> "into the remote namespace configured in `.unisonConfig`"
|
||||
<> "with the key `GitUrl.ns` where `ns` is the current namespace"
|
||||
"publishes the current namespace into the remote namespace configured in your `.unisonConfig`"
|
||||
<> "at the key `RemoteMappings.<namespace>` where `<namespace>` is the current namespace."
|
||||
)
|
||||
],
|
||||
"",
|
||||
@ -1100,12 +1098,12 @@ push =
|
||||
[] ->
|
||||
Right $ Input.PushRemoteBranchI Nothing Path.relativeEmpty' PushBehavior.RequireNonEmpty SyncMode.ShortCircuit
|
||||
url : rest -> do
|
||||
(repo, path) <- parsePushPath "url" url
|
||||
pushPath <- parseWriteRemotePath "remote-path" url
|
||||
p <- case rest of
|
||||
[] -> Right Path.relativeEmpty'
|
||||
[path] -> first fromString $ Path.parsePath' path
|
||||
_ -> Left (I.help push)
|
||||
Right $ Input.PushRemoteBranchI (Just (repo, path)) p PushBehavior.RequireNonEmpty SyncMode.ShortCircuit
|
||||
Right $ Input.PushRemoteBranchI (Just pushPath) p PushBehavior.RequireNonEmpty SyncMode.ShortCircuit
|
||||
)
|
||||
|
||||
pushCreate :: InputPattern
|
||||
@ -1114,7 +1112,7 @@ pushCreate =
|
||||
"push.create"
|
||||
[]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap
|
||||
"The `push.create` command pushes a local namespace to an empty remote namespace.",
|
||||
@ -1128,9 +1126,9 @@ pushCreate =
|
||||
"publishes the current namespace into the empty remote namespace `remote`"
|
||||
),
|
||||
( "`push`",
|
||||
"publishes the current namespace"
|
||||
<> "into the empty remote namespace configured in `.unisonConfig`"
|
||||
<> "with the key `GitUrl.ns` where `ns` is the current namespace"
|
||||
"publishes the current namespace into the remote namespace configured in your `.unisonConfig`"
|
||||
<> "at the key `RemoteMappings.<namespace>` where `<namespace>` is the current namespace,"
|
||||
<> "then publishes the current namespace to that location."
|
||||
)
|
||||
],
|
||||
"",
|
||||
@ -1141,12 +1139,12 @@ pushCreate =
|
||||
[] ->
|
||||
Right $ Input.PushRemoteBranchI Nothing Path.relativeEmpty' PushBehavior.RequireEmpty SyncMode.ShortCircuit
|
||||
url : rest -> do
|
||||
(repo, path) <- parsePushPath "url" url
|
||||
pushPath <- parseWriteRemotePath "remote-path" url
|
||||
p <- case rest of
|
||||
[] -> Right Path.relativeEmpty'
|
||||
[path] -> first fromString $ Path.parsePath' path
|
||||
_ -> Left (I.help push)
|
||||
Right $ Input.PushRemoteBranchI (Just (repo, path)) p PushBehavior.RequireEmpty SyncMode.ShortCircuit
|
||||
Right $ Input.PushRemoteBranchI (Just pushPath) p PushBehavior.RequireEmpty SyncMode.ShortCircuit
|
||||
)
|
||||
|
||||
pushExhaustive :: InputPattern
|
||||
@ -1155,7 +1153,7 @@ pushExhaustive =
|
||||
"debug.push-exhaustive"
|
||||
[]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap $
|
||||
"The " <> makeExample' pushExhaustive <> "command can be used in place of"
|
||||
@ -1169,12 +1167,12 @@ pushExhaustive =
|
||||
[] ->
|
||||
Right $ Input.PushRemoteBranchI Nothing Path.relativeEmpty' PushBehavior.RequireNonEmpty SyncMode.Complete
|
||||
url : rest -> do
|
||||
(repo, path) <- parsePushPath "url" url
|
||||
pushPath <- parseWriteRemotePath "remote-path" url
|
||||
p <- case rest of
|
||||
[] -> Right Path.relativeEmpty'
|
||||
[path] -> first fromString $ Path.parsePath' path
|
||||
_ -> Left (I.help push)
|
||||
Right $ Input.PushRemoteBranchI (Just (repo, path)) p PushBehavior.RequireNonEmpty SyncMode.Complete
|
||||
Right $ Input.PushRemoteBranchI (Just pushPath) p PushBehavior.RequireNonEmpty SyncMode.Complete
|
||||
)
|
||||
|
||||
createPullRequest :: InputPattern
|
||||
@ -1183,7 +1181,7 @@ createPullRequest =
|
||||
"pull-request.create"
|
||||
["pr.create"]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.group $
|
||||
P.lines
|
||||
[ P.wrap $
|
||||
@ -1201,8 +1199,8 @@ createPullRequest =
|
||||
)
|
||||
( \case
|
||||
[baseUrl, headUrl] -> do
|
||||
baseRepo <- parseUri "baseRepo" baseUrl
|
||||
headRepo <- parseUri "headRepo" headUrl
|
||||
baseRepo <- parseReadRemoteNamespace "base-remote-namespace" baseUrl
|
||||
headRepo <- parseReadRemoteNamespace "head-remote-namespace" headUrl
|
||||
pure $ Input.CreatePullRequestI baseRepo headRepo
|
||||
_ -> Left (I.help createPullRequest)
|
||||
)
|
||||
@ -1213,7 +1211,7 @@ loadPullRequest =
|
||||
"pull-request.load"
|
||||
["pr.load"]
|
||||
I.Visible
|
||||
[(Required, gitUrlArg), (Required, gitUrlArg), (Optional, namespaceArg)]
|
||||
[(Required, remoteNamespaceArg), (Required, remoteNamespaceArg), (Optional, namespaceArg)]
|
||||
( P.lines
|
||||
[ P.wrap $
|
||||
makeExample loadPullRequest ["base", "head"]
|
||||
@ -1228,19 +1226,19 @@ loadPullRequest =
|
||||
)
|
||||
( \case
|
||||
[baseUrl, headUrl] -> do
|
||||
baseRepo <- parseUri "baseRepo" baseUrl
|
||||
headRepo <- parseUri "topicRepo" headUrl
|
||||
baseRepo <- parseReadRemoteNamespace "base-remote-namespace" baseUrl
|
||||
headRepo <- parseReadRemoteNamespace "head-remote-namespace" headUrl
|
||||
pure $ Input.LoadPullRequestI baseRepo headRepo Path.relativeEmpty'
|
||||
[baseUrl, headUrl, dest] -> do
|
||||
baseRepo <- parseUri "baseRepo" baseUrl
|
||||
headRepo <- parseUri "topicRepo" headUrl
|
||||
baseRepo <- parseReadRemoteNamespace "base-remote-namespace" baseUrl
|
||||
headRepo <- parseReadRemoteNamespace "head-remote-namespace" headUrl
|
||||
destPath <- first fromString $ Path.parsePath' dest
|
||||
pure $ Input.LoadPullRequestI baseRepo headRepo destPath
|
||||
_ -> Left (I.help loadPullRequest)
|
||||
)
|
||||
|
||||
parseUri :: String -> String -> Either (P.Pretty P.ColorText) ReadRemoteNamespace
|
||||
parseUri label input =
|
||||
parseReadRemoteNamespace :: String -> String -> Either (P.Pretty P.ColorText) ReadRemoteNamespace
|
||||
parseReadRemoteNamespace label input =
|
||||
let printError err = P.lines [P.string "I couldn't parse the repository address given above.", prettyPrintParseError input err]
|
||||
in first printError (P.parse UriParser.repoPath label (Text.pack input))
|
||||
|
||||
@ -1279,17 +1277,17 @@ prettyPrintParseError input errBundle =
|
||||
message = [expected] <> catMaybes [found]
|
||||
in P.oxfordCommasWith "." message
|
||||
|
||||
parseWriteRepo :: String -> String -> Either (P.Pretty P.ColorText) WriteRepo
|
||||
parseWriteRepo label input = do
|
||||
parseWriteGitRepo :: String -> String -> Either (P.Pretty P.ColorText) WriteGitRepo
|
||||
parseWriteGitRepo label input = do
|
||||
first
|
||||
(fromString . show) -- turn any parsing errors into a Pretty.
|
||||
(P.parse UriParser.writeRepo label (Text.pack input))
|
||||
(P.parse UriParser.writeGitRepo label (Text.pack input))
|
||||
|
||||
parsePushPath :: String -> String -> Either (P.Pretty P.ColorText) WriteRemotePath
|
||||
parsePushPath label input = do
|
||||
parseWriteRemotePath :: String -> String -> Either (P.Pretty P.ColorText) WriteRemotePath
|
||||
parseWriteRemotePath label input = do
|
||||
first
|
||||
(fromString . show) -- turn any parsing errors into a Pretty.
|
||||
(P.parse UriParser.writeRepoPath label (Text.pack input))
|
||||
(P.parse UriParser.writeRemotePath label (Text.pack input))
|
||||
|
||||
squashMerge :: InputPattern
|
||||
squashMerge =
|
||||
@ -2032,7 +2030,7 @@ gist =
|
||||
)
|
||||
( \case
|
||||
[repoString] -> do
|
||||
repo <- parseWriteRepo "repo" repoString
|
||||
repo <- parseWriteGitRepo "repo" repoString
|
||||
pure (Input.GistI (Input.GistInput repo))
|
||||
_ -> Left (showPatternHelp gist)
|
||||
)
|
||||
@ -2345,12 +2343,30 @@ gitUrlArg =
|
||||
suggestions =
|
||||
let complete s = pure [Completion s s False]
|
||||
in \input _ _ _ -> case input of
|
||||
"gh" -> complete "https://github.com/"
|
||||
"gl" -> complete "https://gitlab.com/"
|
||||
"bb" -> complete "https://bitbucket.com/"
|
||||
"ghs" -> complete "git@github.com:"
|
||||
"gls" -> complete "git@gitlab.com:"
|
||||
"bbs" -> complete "git@bitbucket.com:"
|
||||
"gh" -> complete "git(https://github.com/"
|
||||
"gl" -> complete "git(https://gitlab.com/"
|
||||
"bb" -> complete "git(https://bitbucket.com/"
|
||||
"ghs" -> complete "git(git@github.com:"
|
||||
"gls" -> complete "git(git@gitlab.com:"
|
||||
"bbs" -> complete "git(git@bitbucket.com:"
|
||||
_ -> pure [],
|
||||
globTargets = mempty
|
||||
}
|
||||
|
||||
-- | Refers to a namespace on some remote code host.
|
||||
remoteNamespaceArg :: ArgumentType
|
||||
remoteNamespaceArg =
|
||||
ArgumentType
|
||||
{ typeName = "remote-namespace",
|
||||
suggestions =
|
||||
let complete s = pure [Completion s s False]
|
||||
in \input _ _ _ -> case input of
|
||||
"gh" -> complete "git(https://github.com/"
|
||||
"gl" -> complete "git(https://gitlab.com/"
|
||||
"bb" -> complete "git(https://bitbucket.com/"
|
||||
"ghs" -> complete "git(git@github.com:"
|
||||
"gls" -> complete "git(git@gitlab.com:"
|
||||
"bbs" -> complete "git(git@bitbucket.com:"
|
||||
_ -> pure [],
|
||||
globTargets = mempty
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user