Skip to content

Commit 8524962

Browse files
committed
agent: ConnectTarget type for connection link or SimpleX name
Adds `ConnectTarget = CTLink AConnectionLink | CTName SimplexNameInfo` in Agent/Protocol.hs next to AConnectionLink. The StrEncoding parser gates the CTName branch on a `@`/`#`/`simplex:/name` discriminator so that bare tokens (which SimplexNameInfo accepts for Markdown's sake) cannot ambiguously match at this layer. JSON is a plain string via strToJEncoding, mirroring AConnectionLink. No consumers yet.
1 parent 1950634 commit 8524962

4 files changed

Lines changed: 84 additions & 0 deletions

File tree

simplexmq.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ test-suite simplexmq-test
500500
AgentTests.ShortLinkTests
501501
CLITests
502502
CoreTests.BatchingTests
503+
CoreTests.ConnectTargetTests
503504
CoreTests.CryptoFileTests
504505
CoreTests.CryptoTests
505506
CoreTests.EncodingTests

src/Simplex/Messaging/Agent/Protocol.hs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ module Simplex.Messaging.Agent.Protocol
122122
OwnerId,
123123
ConnectionLink (..),
124124
AConnectionLink (..),
125+
ConnectTarget (..),
125126
SimplexNameInfo (..),
126127
SimplexNameDomain (..),
127128
SimplexTLD (..),
@@ -195,6 +196,7 @@ import qualified Data.Aeson.TH as J
195196
import qualified Data.Aeson.Types as JT
196197
import Data.Attoparsec.ByteString.Char8 (Parser)
197198
import qualified Data.Attoparsec.ByteString.Char8 as A
199+
import Data.Attoparsec.Combinator (lookAhead)
198200
import qualified Data.ByteString.Base64.URL as B64
199201
import Data.ByteString.Char8 (ByteString)
200202
import qualified Data.ByteString.Char8 as B
@@ -1596,6 +1598,26 @@ instance ToJSON AConnectionLink where
15961598
instance FromJSON AConnectionLink where
15971599
parseJSON = strParseJSON "AConnectionLink"
15981600

1601+
data ConnectTarget = CTLink AConnectionLink | CTName SimplexNameInfo
1602+
deriving (Eq, Show)
1603+
1604+
instance StrEncoding ConnectTarget where
1605+
strEncode = \case
1606+
CTLink l -> strEncode l
1607+
CTName n -> strEncode n
1608+
strP = CTName <$> (lookAhead nameStart *> strP) <|> CTLink <$> strP
1609+
where
1610+
nameStart =
1611+
() <$ A.satisfy (\c -> c == '@' || c == '#')
1612+
<|> () <$ "simplex:/name"
1613+
1614+
instance ToJSON ConnectTarget where
1615+
toEncoding = strToJEncoding
1616+
toJSON = strToJSON
1617+
1618+
instance FromJSON ConnectTarget where
1619+
parseJSON = strParseJSON "ConnectTarget"
1620+
15991621
instance ConnectionModeI m => StrEncoding (ConnShortLink m) where
16001622
strEncode = \case
16011623
CSLInvitation sch srv (SMP.EntityId lnkId) (LinkKey k) -> slEncode sch srv 'i' lnkId k
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
{-# LANGUAGE TypeApplications #-}
3+
4+
module CoreTests.ConnectTargetTests where
5+
6+
import AgentTests.ConnectionRequestTests (contactConnRequest, invConnRequest)
7+
import qualified Data.Aeson as J
8+
import Simplex.Messaging.Agent.Protocol (AConnectionLink (..), ConnectTarget (..), ConnectionLink (..), SConnectionMode (..))
9+
import Simplex.Messaging.Encoding.String (strDecode, strEncode)
10+
import Test.Hspec hiding (fit, it)
11+
import Util (it)
12+
13+
connectTargetTests :: Spec
14+
connectTargetTests = describe "ConnectTarget" $ do
15+
describe "CTName (SimpleX name) — canonical wire form prefixes simplex:/name" $ do
16+
it "@alice.simplex encodes as simplex:/name@alice.simplex" $
17+
"@alice.simplex" `encodesAs` "simplex:/name@alice.simplex"
18+
it "#privacy (bare TLD-less channel) encodes as simplex:/name#privacy.simplex" $
19+
"#privacy" `encodesAs` "simplex:/name#privacy.simplex"
20+
it "#privacy.simplex encodes as simplex:/name#privacy.simplex" $
21+
"#privacy.simplex" `encodesAs` "simplex:/name#privacy.simplex"
22+
it "#support.acme.simplex preserves subdomain" $
23+
"#support.acme.simplex" `encodesAs` "simplex:/name#support.acme.simplex"
24+
it "simplex:/name@alice.simplex round-trips" $
25+
"simplex:/name@alice.simplex" `encodesAs` "simplex:/name@alice.simplex"
26+
it "simplex:/name#privacy.simplex round-trips" $
27+
"simplex:/name#privacy.simplex" `encodesAs` "simplex:/name#privacy.simplex"
28+
29+
describe "CTLink (connection link) round-trips" $ do
30+
it "parses simplex:/contact#… as CTLink and round-trips" $ do
31+
let s = strEncode (ACL SCMContact (CLFull contactConnRequest))
32+
s `decodesSuccessfully` ()
33+
s `encodesAs` s
34+
it "parses simplex:/invitation#… as CTLink" $ do
35+
let s = strEncode (ACL SCMInvitation (CLFull invConnRequest))
36+
s `decodesSuccessfully` ()
37+
38+
describe "rejects ambiguous bare input at this layer" $ do
39+
it "rejects bare 'alice' — no @, no #, no simplex:/name prefix" $
40+
strDecode @ConnectTarget "alice" `shouldSatisfy` isLeft
41+
it "rejects empty input" $
42+
strDecode @ConnectTarget "" `shouldSatisfy` isLeft
43+
it "rejects whitespace input" $
44+
strDecode @ConnectTarget " " `shouldSatisfy` isLeft
45+
46+
describe "JSON shape mirrors AConnectionLink (plain string, not tagged sum)" $ do
47+
it "encodes @alice.simplex as a JSON string" $
48+
case strDecode @ConnectTarget "@alice.simplex" of
49+
Right ct -> J.toJSON ct `shouldBe` J.String "simplex:/name@alice.simplex"
50+
Left e -> expectationFailure $ "strDecode failed: " <> e
51+
it "parses JSON string back to ConnectTarget" $
52+
J.eitherDecode @ConnectTarget "\"@alice.simplex\""
53+
`shouldSatisfy` either (const False) (const True)
54+
where
55+
encodesAs input canonical =
56+
(strEncode <$> strDecode @ConnectTarget input) `shouldBe` Right canonical
57+
decodesSuccessfully s () =
58+
strDecode @ConnectTarget s `shouldSatisfy` either (const False) (const True)
59+
isLeft = either (const True) (const False)

tests/Test.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Control.Concurrent (threadDelay)
88
import qualified Control.Exception as E
99
import Control.Logger.Simple
1010
import CoreTests.BatchingTests
11+
import CoreTests.ConnectTargetTests
1112
import CoreTests.CryptoFileTests
1213
import CoreTests.CryptoTests
1314
import CoreTests.EncodingTests
@@ -83,6 +84,7 @@ main = do
8384
$ do
8485
describe "Core tests" $ do
8586
describe "Batching tests" batchingTests
87+
describe "ConnectTarget tests" connectTargetTests
8688
describe "Encoding tests" encodingTests
8789
describe "Version range" versionRangeTests
8890
describe "Encryption tests" cryptoTests

0 commit comments

Comments
 (0)