1- using Google . Protobuf ;
2- using Ipfs . CoreApi ;
3- using Newtonsoft . Json . Linq ;
1+ using Ipfs . CoreApi ;
42using System . Collections . Generic ;
53using System . Linq ;
64using System . Threading ;
75using System . Threading . Tasks ;
6+ using System ;
7+ using System . IO ;
8+ using Newtonsoft . Json ;
89
10+ #nullable enable
911namespace Ipfs . Http
1012{
1113 class PinApi : IPinApi
@@ -17,41 +19,174 @@ internal PinApi(IpfsClient ipfs)
1719 this . ipfs = ipfs ;
1820 }
1921
20- public async Task < IEnumerable < Cid > > AddAsync ( string path , bool recursive = true , CancellationToken cancel = default ( CancellationToken ) )
22+ public async Task < IEnumerable < Cid > > AddAsync ( string path , PinAddOptions options , CancellationToken cancel = default )
2123 {
22- var opts = "recursive=" + recursive . ToString ( ) . ToLowerInvariant ( ) ;
23- var json = await ipfs . DoCommandAsync ( "pin/add" , cancel , path , opts ) ;
24- return ( ( JArray ) JObject . Parse ( json ) [ "Pins" ] )
25- . Select ( p => ( Cid ) ( string ) p ) ;
24+ options ??= new PinAddOptions ( ) ;
25+ var optList = new List < string >
26+ {
27+ "recursive=" + options . Recursive . ToString ( ) . ToLowerInvariant ( )
28+ } ;
29+ if ( ! string . IsNullOrEmpty ( options . Name ) )
30+ {
31+ optList . Add ( "name=" + options . Name ) ;
32+ }
33+ var json = await ipfs . DoCommandAsync ( "pin/add" , cancel , path , optList . ToArray ( ) ) ;
34+ var dto = JsonConvert . DeserializeObject < PinChangeResponseDto > ( json ) ;
35+ var pins = dto ? . Pins ?? new List < string > ( ) ;
36+ return pins . Select ( p => ( Cid ) p ) ;
2637 }
2738
28- public async Task < IEnumerable < Cid > > ListAsync ( CancellationToken cancel = default ( CancellationToken ) )
39+ public async Task < IEnumerable < Cid > > AddAsync ( string path , PinAddOptions options , IProgress < BlocksPinnedProgress > progress , CancellationToken cancel = default )
2940 {
30- var json = await ipfs . DoCommandAsync ( "pin/ls" , cancel ) ;
31- var keys = ( JObject ) ( JObject . Parse ( json ) [ "Keys" ] ) ;
32- return keys
33- . Properties ( )
34- . Select ( p => ( Cid ) p . Name ) ;
41+ options ??= new PinAddOptions ( ) ;
42+ var optList = new List < string >
43+ {
44+ "recursive=" + options . Recursive . ToString ( ) . ToLowerInvariant ( ) ,
45+ "progress=true"
46+ } ;
47+ if ( ! string . IsNullOrEmpty ( options . Name ) )
48+ {
49+ optList . Add ( "name=" + options . Name ) ;
50+ }
51+ var pinned = new List < Cid > ( ) ;
52+ var stream = await ipfs . PostDownloadAsync ( "pin/add" , cancel , path , optList . ToArray ( ) ) ;
53+ using var sr = new StreamReader ( stream ) ;
54+ while ( ! sr . EndOfStream && ! cancel . IsCancellationRequested )
55+ {
56+ var line = await sr . ReadLineAsync ( ) ;
57+ if ( string . IsNullOrWhiteSpace ( line ) )
58+ continue ;
59+ var dto = JsonConvert . DeserializeObject < PinChangeResponseDto > ( line ) ;
60+ if ( dto is null )
61+ continue ;
62+ if ( dto . Progress . HasValue )
63+ {
64+ progress ? . Report ( new BlocksPinnedProgress { BlocksPinned = dto . Progress . Value } ) ;
65+ }
66+ if ( dto . Pins != null )
67+ {
68+ foreach ( var p in dto . Pins )
69+ {
70+ pinned . Add ( ( Cid ) p ) ;
71+ }
72+ }
73+ }
74+ return pinned ;
3575 }
3676
37- public async Task < IEnumerable < Cid > > ListAsync ( PinType type , CancellationToken cancel = default ( CancellationToken ) )
77+ public async IAsyncEnumerable < PinListItem > ListAsync ( [ System . Runtime . CompilerServices . EnumeratorCancellation ] CancellationToken cancel = default )
3878 {
39- var typeOpt = type . ToString ( ) . ToLowerInvariant ( ) ;
40- var json = await ipfs . DoCommandAsync ( "pin/ls" , cancel ,
41- null ,
42- $ "type={ typeOpt } ") ;
43- var keys = ( JObject ) ( JObject . Parse ( json ) [ "Keys" ] ) ;
44- return keys
45- . Properties ( )
46- . Select ( p => ( Cid ) p . Name ) ;
79+ // Default non-streaming, no names
80+ foreach ( var item in await ListItemsOnceAsync ( null , new List < string > ( ) , cancel ) )
81+ {
82+ yield return item ;
83+ }
84+ }
85+
86+ public async IAsyncEnumerable < PinListItem > ListAsync ( PinType type , [ System . Runtime . CompilerServices . EnumeratorCancellation ] CancellationToken cancel = default )
87+ {
88+ var opts = new List < string > { $ "type={ type . ToString ( ) . ToLowerInvariant ( ) } " } ;
89+ foreach ( var item in await ListItemsOnceAsync ( null , opts , cancel ) )
90+ {
91+ yield return item ;
92+ }
93+ }
94+
95+ public async IAsyncEnumerable < PinListItem > ListAsync ( PinListOptions options , [ System . Runtime . CompilerServices . EnumeratorCancellation ] CancellationToken cancel = default )
96+ {
97+ options ??= new PinListOptions ( ) ;
98+ var opts = new List < string > ( ) ;
99+ if ( options . Type != PinType . All )
100+ opts . Add ( $ "type={ options . Type . ToString ( ) . ToLowerInvariant ( ) } ") ;
101+ if ( ! string . IsNullOrEmpty ( options . Name ) )
102+ {
103+ opts . Add ( $ "name={ options . Name } ") ;
104+ opts . Add ( "names=true" ) ;
105+ }
106+ else if ( options . Names )
107+ {
108+ opts . Add ( "names=true" ) ;
109+ }
110+
111+ if ( options . Stream )
112+ {
113+ await foreach ( var item in ListItemsStreamAsync ( null , opts , options . Names , cancel ) )
114+ {
115+ yield return item ;
116+ }
117+ }
118+ else
119+ {
120+ foreach ( var item in await ListItemsOnceAsync ( null , opts , cancel ) )
121+ {
122+ yield return item ;
123+ }
124+ }
47125 }
48126
49127 public async Task < IEnumerable < Cid > > RemoveAsync ( Cid id , bool recursive = true , CancellationToken cancel = default ( CancellationToken ) )
50128 {
51129 var opts = "recursive=" + recursive . ToString ( ) . ToLowerInvariant ( ) ;
52130 var json = await ipfs . DoCommandAsync ( "pin/rm" , cancel , id , opts ) ;
53- return ( ( JArray ) JObject . Parse ( json ) [ "Pins" ] )
54- . Select ( p => ( Cid ) ( string ) p ) ;
131+ var dto = JsonConvert . DeserializeObject < PinChangeResponseDto > ( json ) ;
132+ var pins = dto ? . Pins ?? new List < string > ( ) ;
133+ return pins . Select ( p => ( Cid ) p ) ;
134+ }
135+
136+ // Internal helper used by ListAsync overloads
137+
138+ async IAsyncEnumerable < PinListItem > ListItemsStreamAsync ( string ? path , List < string > opts , bool includeNames , [ System . Runtime . CompilerServices . EnumeratorCancellation ] CancellationToken cancel )
139+ {
140+ opts = new List < string > ( opts ) { "stream=true" } ;
141+ var stream = await ipfs . PostDownloadAsync ( "pin/ls" , cancel , path , opts . ToArray ( ) ) ;
142+ using var sr = new StreamReader ( stream ) ;
143+ while ( ! sr . EndOfStream && ! cancel . IsCancellationRequested )
144+ {
145+ var line = await sr . ReadLineAsync ( ) ;
146+ if ( string . IsNullOrWhiteSpace ( line ) )
147+ continue ;
148+ var dto = JsonConvert . DeserializeObject < PinLsObjectDto > ( line ) ;
149+ if ( dto is null || string . IsNullOrEmpty ( dto . Cid ) )
150+ continue ;
151+ yield return new PinListItem
152+ {
153+ Cid = ( Cid ) dto . Cid ! ,
154+ Type = ParseType ( dto . Type ) ,
155+ Name = dto . Name
156+ } ;
157+ }
158+ }
159+
160+ async Task < IEnumerable < PinListItem > > ListItemsOnceAsync ( string ? path , List < string > opts , CancellationToken cancel )
161+ {
162+ var json = await ipfs . DoCommandAsync ( "pin/ls" , cancel , path , opts . ToArray ( ) ) ;
163+ var root = JsonConvert . DeserializeObject < PinListResponseDto > ( json ) ;
164+ var list = new List < PinListItem > ( ) ;
165+ if ( root ? . Keys != null )
166+ {
167+ foreach ( var kv in root . Keys )
168+ {
169+ list . Add ( new PinListItem
170+ {
171+ Cid = ( Cid ) kv . Key ! ,
172+ Type = ParseType ( kv . Value ? . Type ) ,
173+ Name = string . IsNullOrEmpty ( kv . Value ? . Name ) ? null : kv . Value ! . Name
174+ } ) ;
175+ }
176+ }
177+ return list ;
178+ }
179+
180+ static PinType ParseType ( string ? t )
181+ {
182+ return t ? . ToLowerInvariant ( ) switch
183+ {
184+ "direct" => PinType . Direct ,
185+ "indirect" => PinType . Indirect ,
186+ "recursive" => PinType . Recursive ,
187+ "all" => PinType . All ,
188+ _ => PinType . All
189+ } ;
55190 }
56191
57192 }
0 commit comments